多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁 > 互聯(lián)網(wǎng) > ffmpeg和opencv 播放視頻文件并顯示

ffmpeg和opencv 播放視頻文件并顯示

來源:程序員人生   發(fā)布時(shí)間:2014-11-05 08:48:44 閱讀次數(shù):4648次

   ffmpeg是基于最新版本,在官網(wǎng)下載http://ffmpeg.zeranoe.com/builds/。編譯時(shí)VS2010配置相干頭文件及庫的路徑便可。opencv的搭建參考上1個(gè)博客。

       首先簡單介紹以下視頻文件的相干知識(shí)。我們平時(shí)看到的視頻文件有許多格式,比如 avi, mkv, rmvb, mov, mp4等等,這些被稱為容器Container), 不同的容器格式規(guī)定了其中音視頻數(shù)據(jù)的組織方式(也包括其他數(shù)據(jù),比如字幕等)。容器中1般會(huì)封裝有視頻和音頻軌,也稱為視頻流(stream)和音頻 流,播放視頻文件的第1步就是根據(jù)視頻文件的格式,解析(demux)出其中封裝的視頻流、音頻流和字幕(如果有的話),解析的數(shù)據(jù)讀到包 (packet)中,每一個(gè)包里保存的是視頻幀(frame)或音頻幀,然后分別對(duì)視頻幀和音頻幀調(diào)用相應(yīng)的解碼器(decoder)進(jìn)行解碼,比如使用 H.264編碼的視頻和MP3編碼的音頻,會(huì)相應(yīng)的調(diào)用H.264解碼器和MP3解碼器,解碼以后得到的就是原始的圖象(YUV or RGB)和聲音(PCM)數(shù)據(jù),然后根據(jù)同步好的時(shí)間將圖象顯示到屏幕上,將聲音輸出到聲卡,終究就是我們看到的視頻。

好好看下下面的文章,很不錯(cuò)的。轉(zhuǎn)載1部份: 

有人會(huì)疑惑,為何解碼后的pFrame不直接用于顯示,而是調(diào)用swscale()轉(zhuǎn)換以后進(jìn)行顯示?

如果不進(jìn)行轉(zhuǎn)換,而是直接調(diào)用SDL進(jìn)行顯示的話,會(huì)發(fā)現(xiàn)顯示出來的圖象是混亂的。關(guān)鍵問題在于解碼后的pFrame的linesize里存儲(chǔ)的不是圖象的寬度,而是比寬度大1些的1個(gè)值。其緣由目前還沒有仔細(xì)調(diào)查(大概是出于性能的斟酌)。例如分辨率為480x272的圖象,解碼后的視頻的linesize[0]為512,而不是480。以第1行亮度像素(pFrame->data[0])為例,從0⑷80存儲(chǔ)的是亮度數(shù)據(jù),而從480⑸12則存儲(chǔ)的是無效的數(shù)據(jù)。因此需要使用swscale()進(jìn)行轉(zhuǎn)換。轉(zhuǎn)換后去除無效數(shù)據(jù),linesize[0]變成480。就能夠正常顯示了。



100行代碼實(shí)現(xiàn)最簡單的基于FFMPEG+SDL的視頻播放器(SDL1.x)

下面直接看代碼吧!

    
/*File : playvideo.cpp *Auth : sjin *Date : 20141029 *Mail : 413977243@qq.com */ #include <stdio.h> #include <cv.h> #include <cxcore.h> #include <highgui.h> #ifdef __cplusplus extern "C" { #endif #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> #include <libswscale/swscale.h> #pragma comment (lib, "Ws2_32.lib") #pragma comment (lib, "avcodec.lib") #pragma comment (lib, "avdevice.lib") #pragma comment (lib, "avfilter.lib") #pragma comment (lib, "avformat.lib") #pragma comment (lib, "avutil.lib") #pragma comment (lib, "swresample.lib") #pragma comment (lib, "swscale.lib") #ifdef __cplusplus } #endif static void CopyDate(AVFrame *pFrame,int width,int height,int time) { if(time <=0 ) time = 1; int nChannels; int stepWidth; uchar * pData; cv::Mat frameImage(cv::Size(width, height), CV_8UC3, cv::Scalar(0)); stepWidth = frameImage.step; nChannels = frameImage.channels(); pData = frameImage.data; for(int i = 0; i < height; i++){ for(int j = 0; j < width; j++){ pData[i * stepWidth + j * nChannels + 0] = pFrame->data[0][i * pFrame->linesize[0] + j * nChannels + 2]; pData[i * stepWidth + j * nChannels + 1] = pFrame->data[0][i * pFrame->linesize[0] + j * nChannels + 1]; pData[i * stepWidth + j * nChannels + 2] = pFrame->data[0][i * pFrame->linesize[0] + j * nChannels + 0]; } } cv::namedWindow("Video", CV_WINDOW_AUTOSIZE); cv::imshow("Video", frameImage); cv::waitKey(time); } static void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) { FILE *pFile; char szFilename[32]; int y; // Open file sprintf(szFilename, "frame%d.ppm", iFrame); pFile=fopen(szFilename, "wb"); if(pFile==NULL) return; // Write header fprintf(pFile, "P6 %d %d 255 ", width, height); // Write pixel data for(y=0; y<height; y++) fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile); // Close file fclose(pFile); } int main(int argc, char * argv[]) { AVFormatContext *pFormatCtx = NULL; int i, videoStream; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVFrame *pFrame; AVFrame *pFrameRGB; AVPacket packet; int frameFinished; int numBytes; uint8_t *buffer; /*注冊(cè)了所有的文件格式和編解碼器的庫*/ av_register_all(); // 打開視頻文件 /*這個(gè)函數(shù)讀取文件的頭部并且把信息保存到我們給的AVFormatContext結(jié)構(gòu)體中。 *第2個(gè)參數(shù) 要打開的文件路徑 */ if(avformat_open_input(&pFormatCtx, "test.264", NULL, NULL)!=0){ return ⑴; // Couldn't open file } // 讀取數(shù)據(jù)包獲得流媒體文件的信息 if(avformat_find_stream_info(pFormatCtx,NULL)<0){ return ⑴; // Couldn't find stream information } //打印輸入或輸出格式的詳細(xì)信息,如時(shí)間、比特率,溪流,容器,程序,元數(shù)據(jù),基礎(chǔ)數(shù)據(jù),編解碼器和時(shí)間。 av_dump_format(pFormatCtx, 0, "test.264", false); //查找第1個(gè)視頻流 videoStream=⑴; for(i = 0; i < pFormatCtx->nb_streams; i++){ if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){ videoStream = i; break; } } if(videoStream == ⑴){//未發(fā)現(xiàn)視頻流退出 return ⑴; } //取得視頻編解碼器的上下文 pCodecCtx = pFormatCtx->streams[videoStream]->codec; // 找到視頻解碼器 pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec == NULL){//未發(fā)現(xiàn)視頻編碼器 return ⑴; } // 打開視頻解碼器 if(avcodec_open2(pCodecCtx, pCodec, 0) < 0){ return ⑴; //打開失潰退出 } /*有些人可能會(huì)從舊的指點(diǎn)中記得有兩個(gè)關(guān)于這些代碼其它部份: *添加CODEC_FLAG_TRUNCATED到 pCodecCtx->flags和添加1個(gè)hack來粗糙的修正幀率。 *這兩個(gè)修正已不在存在于ffplay.c中。因此,我必須假定它們不再必 要。我們移除 *了那些代碼后還有1個(gè)需要指出的不同點(diǎn):pCodecCtx->time_base現(xiàn)在已保存了幀率 *的信息。time_base是1 個(gè)結(jié)構(gòu)體,它里面有1個(gè)份子和分母 (AVRational)。我們使 *用分?jǐn)?shù)的方式來表示幀率是由于很多編解碼器使用非整數(shù)的幀率(例如NTSC使用29.97fps)。 * *if(pCodecCtx->time_base.num > 1000 && pCodecCtx->time_base.den == 1){ * pCodecCtx->time_base.den = 1000; *} */ // 分配保存視頻幀的空間 pFrame = avcodec_alloc_frame(); // 分配1個(gè)AVFrame結(jié)構(gòu) /*準(zhǔn)備輸出保存24位RGB色的PPM文件,我們必須把幀的格式從原來的轉(zhuǎn)換為RGB。FFMPEG將為我們做這些轉(zhuǎn)換*/ pFrameRGB = avcodec_alloc_frame(); if(pFrameRGB==NULL){ return ⑴; } /*即便我們申請(qǐng)了1幀的內(nèi)存,當(dāng)轉(zhuǎn)換的時(shí)候,我們依然需要1個(gè)地方來放置原始的數(shù)據(jù)。 *我們使用avpicture_get_size來取得我們需要的大小,然后手工申請(qǐng)內(nèi)存空間: */ numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); buffer = (uint8_t *)av_malloc( numBytes*sizeof(uint8_t)); // 基于指定的圖象參數(shù),設(shè)置圖片字段所提供的圖象數(shù)據(jù)緩沖區(qū)。 avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height); /*讀取數(shù)據(jù)幀 *通過讀取包來讀取全部視頻流,然后把它解碼成幀,最好后轉(zhuǎn)換格式并且保存。 */ i=0; long prepts = 0; while(av_read_frame(pFormatCtx, &packet) >= 0){ if(packet.stream_index == videoStream){/*判斷讀取的是不是為需要的視頻幀數(shù)據(jù)*/ // 解碼視頻幀 avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); if(frameFinished){ static struct SwsContext *img_convert_ctx; #if 0 //就的轉(zhuǎn)換模式已廢除 img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height); #endif if(img_convert_ctx == NULL) { int w = pCodecCtx->width; int h = pCodecCtx->height; //分配和返回1個(gè)SwsContext。您需要履行縮放/轉(zhuǎn)換操作使用sws_scale()。 img_convert_ctx = sws_getContext(w, h, pCodecCtx->pix_fmt, w, h, PIX_FMT_RGB24, SWS_BICUBIC,NULL, NULL, NULL); if(img_convert_ctx == NULL) { fprintf(stderr, "Cannot initialize the conversion context! "); exit(1); } } ////轉(zhuǎn)換圖象格式,將解壓出來的YUV420P的圖象轉(zhuǎn)換為BRG24的圖象 sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); //保存前5幀數(shù)據(jù) if(i++ <= 5){ SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i); } CopyDate(pFrameRGB, pCodecCtx->width, pCodecCtx->height,packet.pts-prepts); prepts = packet.pts; } } //釋放空間 av_free_packet(&packet); } //釋放空間 av_free(buffer); av_free(pFrameRGB); // 釋放 YUV frame av_free(pFrame); //關(guān)閉解碼器 avcodec_close(pCodecCtx); //關(guān)閉視頻文件 avformat_close_input(&pFormatCtx); system("Pause"); return 0; }
運(yùn)行以下圖:



參考資料:
http://www.cnblogs.com/dyllove98/archive/2013/07/03/3170272.html
http://blog.csdn.net/hsp494980719/article/details/7986400
生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 国产亚洲精品国看不卡 | 亚洲免费高清视频 | 欧美高清在线视频在线99精品 | 中文字幕一区久久久久 | 精品国产乱码久久久久久一区二区 | 久久99精品久久久久久黑人 | 日本免费一二区视频 | 直接在线观看的三级网址 | 欧美色图狠狠干 | 波多野结衣在线观看3人 | 中文字幕日韩一区二区不卡 | www.国产成人| 性色a | 高清中文字幕视频在线播 | 久久精品久久久 | 动漫精品一级毛片动漫 | 最近中文字幕mv免费视频 | 亚洲欧美视频一级 | 精品成人一区二区 | 国产精品国产三级国产专不∫ | 曰本www | 精品视频一区二区三区四区五区 | 国产精品夜色视频一区二区 | 在线亚洲播放 | 国产成人三级视频在线观看播放 | 欧美老师| 福利视频一二区 | 综合欧美一区二区三区 | 日韩欧美精品 | 中文字幕在线精品 | 久久77777| 国产中的精品一区的 | 一级做a爱片 | 欧美日韩亚洲国产精品 | 国产美女网站视频 | 亚洲小说图片 | 午夜在线视频 | 我想看一级毛片 | 欧美九九视频 | 欧美人与禽xoxo牲伦交 | 国产精品系列在线一区 |