轉自:http://blog.csdn.net/dzhuang123/article/details/45054497
需求:把兩路視頻合成一路,即一個畫面同時顯示兩路視頻,其中一路縮小成小視頻疊在大視頻上面,和電視機的畫中畫效果類似。
思路:用h264編碼的視頻舉例,文件中存儲的es流是h264,經過解碼成yuv,yuv可以轉換成rgb格式。把小視頻的rgb復制到大視頻需要被覆蓋的位置上。將重新合成的rgb轉換成yuv,利用ffmpeg 或 x264重新編碼出新的視頻即可。
方法:編解碼還是利用ffmpeg 。 ffmpeg 解碼兩路視頻,解碼后都是yuv。利用ffmpeg· 的sws_getContext 函數改變小圖的大小。之后利用OpenCV完成兩個圖片的合成(opencv這種高大上的庫被我用成了這樣····實在汗顏。其實此處的合并rgb可以自己寫算法實現,本質是把小圖的rgb復制到大圖的對應位置上。)合成好后將rbg轉成yuv格式,利用x264重新編碼成h264 ,就看到了大視頻左上角有個小視頻了。

代碼思路:
兩個線程,各自解碼,主視頻的線程解碼一幀后通知副視頻的線程進行解碼并轉化圖片大小,副視頻的線程解碼完成后通知主線程合成視頻并編碼。合成視頻的時候用opencv很簡單,直接把yuv轉化成rgb,之后在主視頻上設置敏感區,把小視頻疊加上就行。
主副線程解碼套路一樣,ffmpeg的基本使用套路。把yuv轉成opencv mat類型的rgb套路也一樣,上副視頻解碼線程的代碼進行說明。
全局變量:
[cpp] view plain copycv::Mat littlergb,bigrgb;//大小視頻的rgb cv::Mat littleframe,bigframe;//大小視頻的yuv副視頻解碼線程內的代碼:
[cpp] view plain copywhile(av_read_frame(pInputFormatContext, &InPack) >=0) { len = avcodec_decode_video2(pInputCodecContext, &OutFrame, &nComplete, &InPack);//解碼視頻 if (nComplete>0) { if (GetMessage(&msg, NULL, 0, 0)) { switch(msg.message) { case MY_MSG_DECODE: sws_scale(m_pSwsContext,OutFrame.data,OutFrame.linesize, 0,OutFrame.height,dst->data,dst->linesize);//轉換圖片大小 memcpy(littleframe.data,dst->data[0], 640*480); //以下將ffmpeg的yuv數據存到opencv的mat類型中。即opencv存儲的yuv數據 memcpy(littleframe.data+640*480,dst->data[1], 640*480/4); memcpy(littleframe.data+640*480*5/4,dst->data[2], 640*480/4); SetEvent(hEncodeEvent); break; } } } av_free_packet(&InPack); }合并圖片直接用opencv,代碼如下:
[cpp] view plain copycv::cvtColor(littleframe, littlergb,CV_YUV2BGR_I420); cv::cvtColor(bigframe, bigrgb,CV_YUV2BGR_I420); //以上yuv轉rgb Mat roi(bigrgb,Rect(0,0,640,480));//大圖上設置敏感區 littlergb.copyTo(roi); //把小圖拷貝過去 Mat outframe; cv::cvtColor(bigrgb, outframe,CV_BGR2YUV_I420); //rgb到yuv這樣就獲得了合并后的圖片的yuv。
之后進行編碼即可。編碼出來的視頻就是畫中畫了。
點擊打開鏈接 (樓主源碼鏈接失效了)
這個代碼,缺了dll和lib還有頭文件,空間不夠傳不上····
新聞熱點
疑難解答