日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[SimplePlayer] 1. 从视频文件中提取图像

發布時間:2023/12/10 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [SimplePlayer] 1. 从视频文件中提取图像 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在開始之前,我們需要了解視頻文件的格式。視頻文件的格式眾多,無法三言兩語就能詳細分析其結構,盡管如此,ffmpeg卻很好地提取了各類視頻文件的共同特性,并對其進行了抽象描述。

視頻文件格式,統稱為container。它包含一個描述視頻信息的頭部,以及內含實際的音視頻編碼數據的packets。當然,這里的頭部以及packet部分只是個抽象描述,實際的視頻格式的描述信息可能不是存放在視頻文件的起始位置,可能是由分散于視頻文件的各個位置的多個部分組成;數據包有可能是由頭部以及尾部進行分割的傳統數據包形式,也有可能是一大塊數據區域,由索引進行各個數據包的分割。

視頻文件中的packets最主要的就是視頻以及音頻packets,demux的過程就是解析container的header來獲取視頻信息,所得到的視頻信息能幫助我們區分packet是音頻或者視頻。同樣屬性的packets會被稱為stream。

packet中存儲的數據就是音視頻編碼后的數據,通過解碼器進行decode后就能得到視頻圖像或者音頻幀。其中需要注意的一點是,一個packet不一定對應一幀,packet的順序也不一定是實際的播放順序,而通過ffmpeg解碼出來的frame的順序就是實際的播放順序。

?

Demux

首先需要一個用于存儲視頻文件信息的結構體。

pFormatCtx = avformat_alloc_context();

?

讀取視頻文件,并對該文件進行demux,所得到的視頻信息存儲于剛剛所構建的結構體當中

if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0){fprintf(stderr, "open input failed\n");return -1;}

如果pFormatCtx=NULL,那么avformat_open_input也能自動為pFormatCtx分配存儲空間。

?

對于有些視頻格式,單單通過demux并不能獲得所有的視頻信息,為了獲得這些信息,還需要讀取并嘗試解碼該視頻幾個最前端packets(通常會解碼每個stream第一個packet)。所讀取的這幾個packets會被緩存以供后續處理。

if(avformat_find_stream_info(pFormatCtx, NULL)<0){fprintf(stderr, "find stream info failed\n");return -1;}

?

從所獲得的信息當中得到video stream序號,后續可以通過stream序號來對packet進行篩選。

videoStream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);

?

?

Decode

創建一個用于存儲以及維護解碼信息結構體。

pCodecCtx = avcodec_alloc_context3(NULL);

?

把demux時所獲得的視頻相關信息傳遞到解碼結構體中。

if(avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar)<0){fprintf(stderr, "copy param from format context to codec context failed\n");return -1;}

?

根據解碼器id來尋找對應的解碼器

pCodec = avcodec_find_decoder(pCodecCtx->codec_id);if(pCodec==NULL){fprintf(stderr, "Unsupported codec,codec id %d\n", pCodecCtx->codec_id);return -1;}else{fprintf(stdout, "codec id is %d\n", pCodecCtx->codec_id);}

?

打開該解碼器,主要目的是對解碼器進行初始化

if(avcodec_open2(pCodecCtx, pCodec, NULL)<0){fprintf(stderr, "open codec failed\n");return -1;}

?

創建一個用于維護所讀取的packet的結構體,一個用于維護解碼所得的frame的結構體

pPacket = av_packet_alloc();pFrame = av_frame_alloc();if(pFrame == NULL||pPacket == NULL){fprintf(stderr, "cannot get buffer of frame or packet\n");return -1;}

?

從視頻文件中讀取packet,如果所讀取的packet是video,則進行解碼,解碼所得的幀由pFrame進行維護。當然,并不是每次調用avcodec_decode_video2都會返回一幀,因為也可能會有需要多個packet才能解碼出一幀的情況,因此只有當指示一幀是否解碼完成的frameFinished為1才能對這一幀進行后續處理。

while(av_read_frame(pFormatCtx, pPacket)>=0){//Only deal with the video stream of the type "videoStream"if(pPacket->stream_index==videoStream){//Decode video frameavcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, pPacket);//fprintf(stdout, "Frame : %d ,pts=%lld, timebase=%lf\n", i, pFrame->pts, av_q2d(pFormatCtx->streams[videoStream]->time_base));if(frameFinished){if(i>=START_FRAME && i<=END_FRAME){SaveFrame2YUV(pFrame, pCodecCtx->width, pCodecCtx->height, i);i++;}else{i++;continue;}}}av_packet_unref(pPacket);}

當一個packet被解碼后就可以調用av_packet_unref來釋放該packet所占用的空間了。

?

?

Store

視頻文件解碼出來后通常都是YUV格式,Y、U、V三路分量分別存儲在AVFrame的data[0]、data[1]、data[2]所指向的內存區域。linesize[0]、linesize[1]、linesize[2]分別指示了Y、U、V一行所占用的字節數。下面把解碼所得的幀保存為YUV Planar格式。

void SaveFrame2YUV(AVFrame *pFrame, int width, int height, int iFrame){static FILE *pFile;char szFilename[32];int y;//Open fileif(iFrame==START_FRAME){sprintf(szFilename, "Video.yuv");pFile = fopen(szFilename, "wb");if(pFile==NULL)return;}//Write YUV Data, Only support YUV420//Yfor(y=0; y<height; y++){fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, pFrame->linesize[0], pFile);}//Ufor(y=0; y<(height+1)/2; y++){fwrite(pFrame->data[1]+y*pFrame->linesize[1], 1, pFrame->linesize[1], pFile);}//Vfor(y=0; y<(height+1)/2; y++){fwrite(pFrame->data[2]+y*pFrame->linesize[2], 1, pFrame->linesize[2], pFile);}//Close FIleif(iFrame==END_FRAME){fclose(pFile);} }

?

?

最后就是釋放內存,關閉decoder,關閉demuxer

av_free(pPacket);av_free(pFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);

轉載于:https://www.cnblogs.com/TaigaCon/p/9603854.html

總結

以上是生活随笔為你收集整理的[SimplePlayer] 1. 从视频文件中提取图像的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。