<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
有時播放實時流的時候有截圖的需求,需要將解碼出來的圖片儲存本地或上傳伺服器,這時就需要將avframe中的資料編碼成png、jpg等格式的圖片,我們使用ffmpeg的相關編碼器就可以實現功能。
首先需要查詢圖片編碼器,比如jpg為AV_CODEC_ID_MJPEG,png為AV_CODEC_ID_PNG
範例程式碼:
enum AVCodecID codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
有了編碼器就可以構造編碼器上下文了。
AVCodecContext*ctx = avcodec_alloc_context3(codec); ctx->bit_rate = 3000000; ctx->width = frame->width;//視訊幀的寬 ctx->height = frame->height;//視訊幀的高 ctx->time_base.num = 1; ctx->time_base.den = 25; ctx->gop_size = 10; ctx->max_b_frames = 0; ctx->thread_count = 1; ctx->pix_fmt = *codec->pix_fmts;//使用編碼器適配的畫素格式 //開啟編碼器 avcodec_open2(ctx, codec, NULL);
如果輸入視訊幀的畫素和編碼器的畫素格式不相同則需要轉換畫素格式,我們採用SwsContext 轉換即可
AVFrame*rgbFrame = av_frame_alloc();//轉換後的幀 swsContext = sws_getContext(frame->width, frame->height, (enum AVPixelFormat)frame->format, frame->width, frame->height, ctx->pix_fmt, 1, NULL, NULL, NULL); int bufferSize = av_image_get_buffer_size(ctx->pix_fmt, frame->width, frame->height, 1) * 2; buffer = (unsigned char*)av_malloc(bufferSize); //構造幀的快取 av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, ctx->pix_fmt, frame->width, frame->height, 1); sws_scale(swsContext, frame->data, frame->linesize, 0, frame->height, rgbFrame->data, rgbFrame->linesize); //構造必要的引數 rgbFrame->format = ctx->pix_fmt; rgbFrame->width = ctx->width; rgbFrame->height = ctx->height;
得到轉後的幀就可以編碼
ret = avcodec_send_frame(ctx, rgbFrame);
獲取解碼後的包即可得到圖片資料。
uint8_t* outbuf;//輸出圖片的快取 size_t outbufSize;//快取大小 AVPacket pkt; av_init_packet(&pkt); //獲取解碼的包 avcodec_receive_packet(ctx, &pkt); //將圖片資料拷貝到快取 if (pkt.size > 0 && pkt.size <= outbufSize) memcpy(outbuf, pkt.data, pkt.size);
將上述步驟使用的物件銷燬。
if (swsContext) { sws_freeContext(swsContext); } if (rgbFrame) { av_frame_unref(rgbFrame); av_frame_free(&rgbFrame); } if (buffer) { av_free(buffer); } av_packet_unref(&pkt); if (ctx) { avcodec_close(ctx); avcodec_free_context(&ctx); }
/// <summary> /// 幀轉圖片 /// 如果外部提供的快取長度不足則不會寫入。 /// </summary> /// <param name="frame">[in]視訊幀</param> /// <param name="codecID">[in]圖片編碼器ID,如jpg:AV_CODEC_ID_MJPEG,png:AV_CODEC_ID_PNG</param> /// <param name="outbuf">[out]圖片快取,由外部提供</param> /// <param name="outbufSize">[in]圖片快取長度</param> /// <returns>返回圖片實際長度</returns> static int frameToImage(AVFrame* frame, enum AVCodecID codecID, uint8_t* outbuf, size_t outbufSize) { int ret = 0; AVPacket pkt; AVCodec* codec; AVCodecContext* ctx = NULL; AVFrame* rgbFrame = NULL; uint8_t* buffer = NULL; struct SwsContext* swsContext = NULL; av_init_packet(&pkt); codec = avcodec_find_encoder(codecID); if (!codec) { printf("avcodec_send_frame error %d", codecID); goto end; } if (!codec->pix_fmts) { printf("unsupport pix format with codec %s", codec->name); goto end; } ctx = avcodec_alloc_context3(codec); ctx->bit_rate = 3000000; ctx->width = frame->width; ctx->height = frame->height; ctx->time_base.num = 1; ctx->time_base.den = 25; ctx->gop_size = 10; ctx->max_b_frames = 0; ctx->thread_count = 1; ctx->pix_fmt = *codec->pix_fmts; ret = avcodec_open2(ctx, codec, NULL); if (ret < 0) { printf("avcodec_open2 error %d", ret); goto end; } if (frame->format != ctx->pix_fmt) { rgbFrame = av_frame_alloc(); if (rgbFrame == NULL) { printf("av_frame_alloc fail:%d"); goto end; } swsContext = sws_getContext(frame->width, frame->height, (enum AVPixelFormat)frame->format, frame->width, frame->height, ctx->pix_fmt, 1, NULL, NULL, NULL); if (!swsContext) { printf("sws_getContext fail:%d"); goto end; } int bufferSize = av_image_get_buffer_size(ctx->pix_fmt, frame->width, frame->height, 1) * 2; buffer = (unsigned char*)av_malloc(bufferSize); if (buffer == NULL) { printf("buffer alloc fail:%d", bufferSize); goto end; } av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, ctx->pix_fmt, frame->width, frame->height, 1); if ((ret = sws_scale(swsContext, frame->data, frame->linesize, 0, frame->height, rgbFrame->data, rgbFrame->linesize)) < 0) { printf("sws_scale error %d", ret); } rgbFrame->format = ctx->pix_fmt; rgbFrame->width = ctx->width; rgbFrame->height = ctx->height; ret = avcodec_send_frame(ctx, rgbFrame); } else { ret = avcodec_send_frame(ctx, frame); } if (ret < 0) { printf("avcodec_send_frame error %d", ret); goto end; } ret = avcodec_receive_packet(ctx, &pkt); if (ret < 0) { printf("avcodec_receive_packet error %d", ret); goto end; } if (pkt.size > 0 && pkt.size <= outbufSize) memcpy(outbuf, pkt.data, pkt.size); ret = pkt.size; end: if (swsContext) { sws_freeContext(swsContext); } if (rgbFrame) { av_frame_unref(rgbFrame); av_frame_free(&rgbFrame); } if (buffer) { av_free(buffer); } av_packet_unref(&pkt); if (ctx) { avcodec_close(ctx); avcodec_free_context(&ctx); } return ret; }
void main() { AVFrame* frame;//視訊解碼得到的幀 saveFrameToJpg(frame,"snapshot.jpg"); }
/// <summary> /// 將視訊幀儲存為jpg圖片 /// </summary> /// <param name="frame">視訊幀</param> /// <param name="path">儲存的路徑</param> void saveFrameToJpg(AVFrame*frame,const char*path) { //確保緩衝區長度大於圖片,使用brga畫素格式計算。如果是bmp或tiff依然可能超出長度,需要加一個頭部長度,或直接乘以2。 int bufSize = av_image_get_buffer_size(AV_PIX_FMT_BGRA, frame->width, frame->height, 64); //申請緩衝區 uint8_t* buf = (uint8_t*)av_malloc(bufSize); //將視訊幀轉換成圖片 int picSize = frameToImage(frame, AV_CODEC_ID_MJPEG, buf, bufSize); //寫入檔案 auto f = fopen(path, "wb+"); if (f) { fwrite(buf, sizeof(uint8_t), bufSize, f); fclose(f); } //釋放緩衝區 av_free(buf); }
void main() { uint8_t*frameData;//解碼得到的視訊資料 AVFrame* frame=allocFrame(frameData,640,360,AV_PIX_FMT_YUV420P); saveFrameToJpg(frame,"snapshot.jpg");//此方法定義在範例1中 av_frame_free(&frame); }
/// <summary> /// 通過裸資料生成avframe /// </summary> /// <param name="frameData">幀資料</param> /// <param name="width">幀寬</param> /// <param name="height">幀高</param> /// <param name="format">畫素格式</param> /// <returns>avframe,使用完成後需要呼叫av_frame_free釋放</returns> AVFrame* allocFrame(uint8_t*frameData,int width,int height,AVPixelFormat format) { AVFrame* frame = av_frame_alloc(); frame->width = width; frame->height = height; frame->format = format; av_image_fill_arrays(frame->data, frame->linesize, frameData, format, frame->width, frame->height, 64); return frame; }
以上就是今天要講的內容,總的來說整個流程和一般的視訊編碼是一致的,只是選擇的編碼器不同,拿到的圖片資料在記憶體中,可以直接網路傳輸或儲存到本地。可以很方便的在視訊介面過程中截圖,尤其是解碼使用ffmpeg的情況下。實現也不算難,寫成文章是為了以後能直接複用,畢竟時間久了一些細節還是會遺忘的。
到此這篇關於C++ ffmpeg實現將視訊幀轉換成jpg或png等圖片的文章就介紹到這了,更多相關C++ ffmpeg視訊幀轉圖片內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45