FFmpeg转OpenCV Mat显示
? ? ? FFmpeg一般采用SDL進(jìn)行顯示,如果不追求復(fù)雜的界面、交互和多線程功能,當(dāng)然也可以使用OpenCV的imshow()方法進(jìn)行顯示了,而且實(shí)現(xiàn)起來比SDL更簡單。方法也很簡單,只需要把視頻幀的BGR格式的數(shù)據(jù)(如果是RGB格式,需要轉(zhuǎn)換)轉(zhuǎn)存到OpenCV的Mat矩陣?yán)铩penCV的Mat是一個(gè)類,由兩個(gè)數(shù)據(jù)部分組成:?矩陣頭(包含信息有矩陣的大小,用于存儲(chǔ)的方法,矩陣存儲(chǔ)的地址等信息) 和一個(gè)指向存儲(chǔ)所有像素值矩陣的指針。OpenCV的data屬性是一個(gè)uchar類型的指針,它指向Mat數(shù)據(jù)矩陣的首地址;利用該屬性,只需要把Mat的data指向FFmpeg的幀數(shù)據(jù)里即可,就可以用OpenCV的imshow()顯示了。
? ? 下面給出兩個(gè)方法,第一個(gè)是網(wǎng)上參考別人,并修改好的方法;第二個(gè)是鄙人再次精簡,更好理解的方法
方法一:
? ?先定義個(gè)out_buffer指針指向AVFrame幀數(shù)據(jù),注意存儲(chǔ)格式一定要選擇與OpenCV格式類似的AV_PIX_FMT_BGR24
int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);uint8_t *out_buffer = (uint8_t *)av_malloc(size);avpicture_fill((AVPicture *)pFrameBGR, out_buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);? ? 經(jīng)過FFmpeg的sws_scale()后,只需要在OpenCV初試化時(shí),傳入out_buffer即可,如下:
//Mat mRGB(pCodecCtx->height, pCodecCtx->width, CV_8UC3, out_buffer);//等效于下面Mat mRGB(Size(pCodecCtx->width,pCodecCtx->height), CV_8UC3);mRGB.data = out_buffer;方法二:
? ? ?前面已經(jīng)說明,OpenCV的Mat是一個(gè)類,由兩個(gè)數(shù)據(jù)部分組成: 矩陣頭(包含信息有矩陣的大小,用于存儲(chǔ)的方法,矩陣存儲(chǔ)的地址等信息) 和一個(gè)指向存儲(chǔ)所有像素值矩陣的指針。OpenCV的data屬性是一個(gè)uchar類型的指針,它指向Mat數(shù)據(jù)矩陣的首地址;利用該屬性,只需要把Mat的data指向FFmpeg的幀數(shù)據(jù)里即可,就可以用OpenCV的imshow()顯示了。
? ? ?因此,我們只需要在sws_scale后,把Mat地址直接指向AVFrame幀數(shù)據(jù)的首地址即可,不需要out_buffer,將上面的關(guān)鍵代碼改為:
Mat mRGB(Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3); mRGB.data =(uchar*)pFrameBGR->data[0];//注意不能寫為:(uchar*)pFrameBGR->data? ? 下面是完整的代碼:
#define __STDC_CONSTANT_MACROS #include <stdio.h> // Opencv #include <opencv/cv.h> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp>extern "C" { #include "libavutil/avutil.h" #include "libavcodec/avcodec.h" #include "libavformat/avformat.h"//新版里的圖像轉(zhuǎn)換結(jié)構(gòu)需要引入的頭文件 #include "libswscale/swscale.h" };using namespace cv;char* filename = "F:/FFmpeg/testvideo/屌絲男士.mov";;int main() {AVCodec *pCodec; //解碼器指針AVCodecContext* pCodecCtx; //ffmpeg解碼類的類成員AVFrame* pAvFrame; //多媒體幀,保存解碼后的數(shù)據(jù)幀AVFormatContext* pFormatCtx; //保存視頻流的信息av_register_all(); //注冊(cè)庫中所有可用的文件格式和編碼器pFormatCtx = avformat_alloc_context();if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0) { //檢查文件頭部printf("Can't find the stream!\n");}if (avformat_find_stream_info(pFormatCtx, NULL)<0) { //查找流信息printf("Can't find the stream information !\n");}int videoindex = -1;for (int i = 0; i < pFormatCtx->nb_streams; ++i) //遍歷各個(gè)流,找到第一個(gè)視頻流,并記錄該流的編碼信息{if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {videoindex = i;break;}}if (videoindex == -1) {printf("Don't find a video stream !\n");return -1;}pCodecCtx = pFormatCtx->streams[videoindex]->codec; //得到一個(gè)指向視頻流的上下文指針pCodec = avcodec_find_decoder(pCodecCtx->codec_id); //到該格式的解碼器if (pCodec == NULL) {printf("Cant't find the decoder !\n"); //尋找解碼器return -1;}if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { //打開解碼器printf("Can't open the decoder !\n");return -1;}pAvFrame = av_frame_alloc(); //分配幀存儲(chǔ)空間AVFrame* pFrameBGR = av_frame_alloc(); //存儲(chǔ)解碼后轉(zhuǎn)換的RGB數(shù)據(jù)// 保存BGR,opencv中是按BGR來保存的int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);uint8_t *out_buffer = (uint8_t *)av_malloc(size);avpicture_fill((AVPicture *)pFrameBGR, out_buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);AVPacket* packet = (AVPacket*)malloc(sizeof(AVPacket));printf("-----------輸出文件信息---------\n");av_dump_format(pFormatCtx, 0, filename, 0);printf("------------------------------");struct SwsContext *img_convert_ctx;img_convert_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,AV_PIX_FMT_BGR24, //設(shè)置sws_scale轉(zhuǎn)換格式為BGR24,這樣轉(zhuǎn)換后可以直接用OpenCV顯示圖像了SWS_BICUBIC, NULL, NULL, NULL);int ret;int got_picture;cvNamedWindow("RGB", 1);for (;;){if (av_read_frame(pFormatCtx, packet) >= 0){if (packet->stream_index == videoindex){ret = avcodec_decode_video2(pCodecCtx, pAvFrame, &got_picture, packet);if (ret < 0){printf("Decode Error.(解碼錯(cuò)誤)\n");return -1;}if (got_picture){//YUV to RGBsws_scale(img_convert_ctx, (const uint8_t* const*)pAvFrame->data, pAvFrame->linesize, 0, pCodecCtx->height,pFrameBGR->data, pFrameBGR->linesize);//Mat mRGB(pCodecCtx->height, pCodecCtx->width, CV_8UC3, out_buffer);//(1)等效于下面//Mat mRGB(Size(pCodecCtx->width,pCodecCtx->height), CV_8UC3);//(2)//mRGB.data = out_buffer;//memcpy(pCvMat.data, out_buffer, size);Mat mRGB(Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3);mRGB.data =(uchar*)pFrameBGR->data[0];//注意不能寫為:(uchar*)pFrameBGR->dataimshow("RGB", mRGB);waitKey(40);}}av_free_packet(packet);}else{break;}}av_free(out_buffer);av_free(pFrameBGR);av_free(pAvFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);sws_freeContext(img_convert_ctx);cvDestroyWindow("RGB");system("pause");return 0; }?方法三:為了方便使用,這里提供一個(gè)函數(shù)可以實(shí)現(xiàn)AVFrame到OpenCV Mat的轉(zhuǎn)換
cv::Mat avFrame2Mat(AVFrame* pAvFrame, AVCodecContext*pCodecCtx) {AVFrame* pFrameBGR = av_frame_alloc(); //存儲(chǔ)解碼后轉(zhuǎn)換的RGB數(shù)據(jù)// 保存BGR,opencv中是按BGR來保存的int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);uint8_t *out_buffer = (uint8_t *)av_malloc(size);avpicture_fill((AVPicture *)pFrameBGR, out_buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);struct SwsContext *img_convert_ctx;img_convert_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_BGR24, //設(shè)置sws_scale轉(zhuǎn)換格式為BGR24,這樣轉(zhuǎn)換后可以直接用OpenCV顯示圖像了SWS_BICUBIC,NULL, NULL, NULL);sws_scale(img_convert_ctx,(const uint8_t* const*)pAvFrame->data,pAvFrame->linesize,0,pCodecCtx->height,pFrameBGR->data,pFrameBGR->linesize);//Mat mRGB(pCodecCtx->height, pCodecCtx->width, CV_8UC3, out_buffer);//(1)等效于下面//Mat mRGB(Size(pCodecCtx->width,pCodecCtx->height), CV_8UC3);//(2)//mRGB.data = out_buffer;//memcpy(pCvMat.data, out_buffer, size);Mat mRGB(Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3);mRGB.data = (uchar*)pFrameBGR->data[0];//注意不能寫為:(uchar*)pFrameBGR->data//av_free(out_buffer);av_free(pFrameBGR);//av_free(pAvFrame);sws_freeContext(img_convert_ctx);return mRGB; }完整的程序如下:
#define __STDC_CONSTANT_MACROS #include <stdio.h> // Opencv #include <opencv/cv.h> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp>extern "C" { #include "libavutil/avutil.h" #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" };using namespace cv; cv::Mat avFrame2Mat(AVFrame* pAvFrame, AVCodecContext*pCodecCtx);char* filename = "F:/FFmpeg/testvideo/屌絲男士.mov";;int main() {AVCodec *pCodec; //解碼器指針AVCodecContext* pCodecCtx; //ffmpeg解碼類的類成員AVFrame* pAvFrame; //多媒體幀,保存解碼后的數(shù)據(jù)幀AVFormatContext* pFormatCtx; //保存視頻流的信息av_register_all(); //注冊(cè)庫中所有可用的文件格式和編碼器pFormatCtx = avformat_alloc_context();if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0) { //檢查文件頭部printf("Can't find the stream!\n");}if (avformat_find_stream_info(pFormatCtx, NULL)<0) { //查找流信息printf("Can't find the stream information !\n");}int videoindex = -1;for (int i = 0; i < pFormatCtx->nb_streams; ++i) //遍歷各個(gè)流,找到第一個(gè)視頻流,并記錄該流的編碼信息{if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {videoindex = i;break;}}if (videoindex == -1) {printf("Don't find a video stream !\n");return -1;}pCodecCtx = pFormatCtx->streams[videoindex]->codec; //得到一個(gè)指向視頻流的上下文指針pCodec = avcodec_find_decoder(pCodecCtx->codec_id); //到該格式的解碼器if (pCodec == NULL) {printf("Cant't find the decoder !\n"); //尋找解碼器return -1;}if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { //打開解碼器printf("Can't open the decoder !\n");return -1;}pAvFrame = av_frame_alloc(); //分配幀存儲(chǔ)空間// 保存BGR,opencv中是按BGR來保存的AVPacket* packet = (AVPacket*)malloc(sizeof(AVPacket));printf("-----------輸出文件信息---------\n");av_dump_format(pFormatCtx, 0, filename, 0);printf("------------------------------");int ret;int got_picture;cvNamedWindow("RGB", 1);for (;;){if (av_read_frame(pFormatCtx, packet) >= 0){if (packet->stream_index == videoindex){ret = avcodec_decode_video2(pCodecCtx, pAvFrame, &got_picture, packet);if (ret < 0){printf("Decode Error.(解碼錯(cuò)誤)\n");return -1;}if (got_picture){cv::Mat des = avFrame2Mat(pAvFrame, pCodecCtx);imshow("RGB", des);waitKey(40);}}av_free_packet(packet);}else{break;}}//av_free(out_buffer);//av_free(pFrameBGR);av_free(pAvFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);//sws_freeContext(img_convert_ctx);cvDestroyWindow("RGB");system("pause");return 0; }cv::Mat avFrame2Mat(AVFrame* pAvFrame, AVCodecContext*pCodecCtx) {AVFrame* pFrameBGR = av_frame_alloc(); //存儲(chǔ)解碼后轉(zhuǎn)換的RGB數(shù)據(jù)// 保存BGR,opencv中是按BGR來保存的int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);uint8_t *out_buffer = (uint8_t *)av_malloc(size);avpicture_fill((AVPicture *)pFrameBGR, out_buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);struct SwsContext *img_convert_ctx;img_convert_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_BGR24, //設(shè)置sws_scale轉(zhuǎn)換格式為BGR24,這樣轉(zhuǎn)換后可以直接用OpenCV顯示圖像了SWS_BICUBIC,NULL, NULL, NULL);sws_scale(img_convert_ctx,(const uint8_t* const*)pAvFrame->data,pAvFrame->linesize,0,pCodecCtx->height,pFrameBGR->data,pFrameBGR->linesize);//Mat mRGB(pCodecCtx->height, pCodecCtx->width, CV_8UC3, out_buffer);//(1)等效于下面//Mat mRGB(Size(pCodecCtx->width,pCodecCtx->height), CV_8UC3);//(2)//mRGB.data = out_buffer;//memcpy(pCvMat.data, out_buffer, size);Mat mRGB(Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3);mRGB.data = (uchar*)pFrameBGR->data[0];//注意不能寫為:(uchar*)pFrameBGR->data//av_free(out_buffer);av_free(pFrameBGR);//av_free(pAvFrame);sws_freeContext(img_convert_ctx);return mRGB; }參考資料:
【1】《利用ffmpeg和opencv進(jìn)行視頻的解碼播放》https://www.jianshu.com/p/6ef3c18d61b0
【2】《ffmpeg中avframe的YUV格式數(shù)據(jù)到OpenCV中Mat的BGR格式轉(zhuǎn)換》http://www.cnblogs.com/riddick/p/7719190.html
總結(jié)
以上是生活随笔為你收集整理的FFmpeg转OpenCV Mat显示的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vs项目中头文件(.h)静态库(.lib
- 下一篇: 将tensorflow训练好的模型移植到