操屁眼的视频在线免费看,日本在线综合一区二区,久久在线观看免费视频,欧美日韩精品久久综

新聞資訊

    在接著使用libavdevice來采集屏幕的圖像。

    在Windows系統使用libavdevice抓取屏幕數據有兩種方法:gdigrab和dshow。

    1. gdigrab

    gdigrab是FFmpeg專門用于抓取Windows桌面的設備。非常適合用于屏幕錄制。它通過不同的輸入URL支持兩種方式的抓取:

    (1)“desktop”:抓取整張桌面。或者抓取桌面中的一個特定的區域。

    (2)“title={窗口名稱}”:抓取屏幕中特定的一個窗口(目前中文窗口還有亂碼問題)。

    gdigrab另外還支持一些參數,用于設定抓屏的位置:

    offset_x:抓屏起始點橫坐標。

    offset_y:抓屏起始點縱坐標。

    video_size:抓屏的大小。

    framerate:抓屏的幀率。

    參考的代碼如下:

    1


    2


    3


    4


    5


    6


    7


    8


    9


    10


    11


    12


    13


    14


    15


    16


    17


    //Use gdigrab

    AVDictionary* options = NULL;

    //Set some options

    //grabbing frame rate

    //av_dict_set(&options,"framerate","5",0);

    //The distance from the left edge of the screen or desktop

    //av_dict_set(&options,"offset_x","20",0);

    //The distance from the top edge of the screen or desktop

    //av_dict_set(&options,"offset_y","40",0);

    //Video frame size. The default is to capture the full screen

    //av_dict_set(&options,"video_size","640x480",0);

    AVInputFormat *ifmt=av_find_input_format( "gdigrab" );

    if (avformat_open_input(&pFormatCtx, "desktop" ,ifmt,&options)!=0){

    printf ("Couldn't open input stream.

    ");

    return -1;

    }

    2. dshow

    使用dshow抓屏需要安裝抓屏軟件:screen-capture-recorder

    軟件地址:http://sourceforge.net/projects/screencapturer/

    下載軟件安裝完成后,可以指定dshow的輸入設備為“screen-capture-recorder”即可。有關dshow設備的使用方法在上一篇文章中已經有詳細敘述,這里不再重復。參考的代碼如下:

    1


    2


    3


    4


    5


    6


    7


    8


    9


    10


    11


    //Use dshow //

    //這里需要先安裝 screen-capture-recorder 才能使用dshow采集屏幕

    //screen-capture-recorder

    //Website: http://sourceforge.net/projects/screencapturer/

    //

    AVInputFormat *ifmt=av_find_input_format( "dshow" );

    if (avformat_open_input(&pFormatCtx, "video=screen-capture-recorder" ,ifmt,NULL)!=0){

    printf ("Couldn't open input stream.

    ");

    return -1;

    }

    gdigrab支持的參數多一些,可以單獨獲取某個窗口的圖像,也能直接獲取某個部分的圖像。

    而dshow只能截取整個屏幕(至少目前為止我沒找到其他方法),并且dshow還需要安裝screen-capture-recorder這個軟件。

    然而這里,我們卻是選擇使用dshow來采集桌面而不是gdigrab。

    原因是dshow采集的效率比gdigrab高,至少我是這么覺得。

    看了上面的代碼,我想大家也都知道了接下來要怎么辦了吧。

    是的,和上次我們打開攝像頭相比,唯一的不同恐怕就是個名字了。

    因此,代碼就不說了,直接上完整工程。

    我們還是一樣,把他轉換成yuv420然后保存到文件,用yuvplayer來播放。

    我的桌面是1920x1080的,保存了100張圖片發現大小達到了296M。

    yuv420圖像是原始的圖像數據,每張圖像的大小都是 1920x1080x3/2(為什么是這個值后面再講)≈ 2.96M 所以100張圖像就是296M了。

    大的驚人吧! 所以這就是我們在第一節中講的為什么圖像要編碼了。

    ————————————————

    版權聲明:本文為CSDN博主「雲天之巔」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。

    原文鏈接:https://blog.csdn.net/qq214517703/article/details/52693408

    最近發現很多人問怎么用FFmpeg采集攝像頭圖像,事實上FFmpeg很早就支持通過DShow獲取采集設備(攝像頭、麥克風)的數據了,只是網上提供的例子比較少。如果能用FFmpeg實現采集、編碼和錄制(或推流),那整個實現方案就簡化很多,正因為這個原因,我想嘗試做一個FFmpeg采集攝像頭視頻和麥克風音頻的程序。經過一個星期的努力,終于做出來了。我打算把開發的心得和經驗分享給大家。我分三部分來講述:首先第一部分介紹如何用FFmpeg的官方工具(ffmpeg.exe)通過命令行來枚舉DShow設備和采集攝像頭圖像,這部分是基礎,能夠快速讓大家熟悉怎么用FFmpeg測試攝像頭采集;第二部分介紹我寫的采集程序的功能和用法;第三部分講解各個模塊包括采集、編碼、封裝和錄制是如何實現的。

    (該例子代碼的下載地址:http://download.csdn.net/download/zhoubotong2012/10252187)

    1.用命令行枚舉采集設備和采集數據

    打開Cmd命令行控制臺,進入FFmpeg的Bin目錄,輸入如下命令:

    ffmpeg -list_devices true -f dshow -i dummy

    則在我的機器上顯示如下結果:

    在上面的命令行窗口中列出了兩個設備,一個是視頻采集設備,另外是一個音頻采集設備。另外,我們發現:音頻設備的名稱有亂碼,因為其中有中文名稱,后面在講到用API采集數據的時候會提到解決這個問題的方法。

    接著我們輸入另外一個命令行:

    ffmpeg -list_options true -f dshow -i video="USB 2861 Device"

    這個命令行的作用是獲取指定視頻采集設備支持的分辨率、幀率和像素格式等屬性,返回的是一個列表,結果如下:

    這里我們看到采集設備支持的最大分辨率是720x576,輸出像素格式是yuyv422,支持的幀率為29.97和25FPS。

    下面我們執行另外一條命令,將攝像頭的圖像和麥克風的音頻錄制保存成一個文件。命令如下:

    ffmpeg -f dshow -i video="USB 2861 Device" -f dshow -i audio="線路 (3- USB Audio Device)" -vcodec libx264 -acodec aac -strict -2 mycamera.mkv

    上面的命令行用video=指定視頻設備,用audio=指定音頻設備,后面的參數是定義編碼器的格式和屬性,輸出為一個名為mycamera.mkv的文件。

    命令運行之后,控制臺打印FFmpeg的運行日志,按“Q”鍵則中止命令。

    這里有些讀者可能會問:采集設備不是支持多個分辨率嗎?怎么設置采集時用哪一種分辨率輸出?答案是用“-s”參數設置,若在上面的命令行加上“-s 720x576”,則FFmpeg就會以720x576的分辨率進行采集,如果不設置,則以默認的分辨率輸出

    注意:如果你運行上面命令ffmpeg報如下錯誤:Could not run filter

    Video=XXX:Input/output error

    則說明該版本的ffmpeg不支持該采集設備。這是由于舊版本的FFmpeg一個Bug引起的,不支持需要連接crossbar連接的視頻采集設備,新版本的FFmpeg(avdevice-58)修復了這個問題。所以如果你遇到這個問題,可以通過升級FFmpeg來解決。

    好,關于命令行的內容就介紹完了。

    2.采集程序的使用

    這個程序叫“AVCapture”,能從視頻采集設備(攝像頭,采集卡)獲取圖像,支持圖像預覽;還可以采集麥克風音頻;支持對視頻和音頻編碼,支持錄制成文件。這是一個MFC開發的窗口程序,界面比較簡潔,如下圖:

    開始采集前需要選擇設備,點擊文件菜單的“打開設備”,彈出一個設備選擇對話框,如下圖所示:

    在對話框里選擇任意一個視頻設備和音頻設備,如果想啟用某種設備,必須勾選右邊的“啟用”選項,但如果只需要用其中一種采集設備,則可以把其中一個禁用掉。

    按“確定”則開始采集數據了。視頻和音頻會編碼后保存到一個文件中,這個文件的路徑是在配置文件中設置的,打開程序目錄下的Config.ini文件,則顯示如下字段:

    [Client]

    file_path = D:\camera.mkv

    File_path就是錄制文件的路徑。

    采集的圖像默認顯示到中間的窗口中,如果不想預覽,可以在主菜單欄的“編輯”菜單中取消勾選“預覽視頻”。

    3.功能模塊實現

    該采集程序實現了枚舉采集設備,采集控制、顯示圖像、視頻/音頻編碼和錄制的功能,其中輸入(Input)、輸出(Output)和顯示(Paint)這三個模塊分別用一個單獨的類進行封裝:CAVInputStream,CAVOutputStream,CImagePainter。CAVInputStream負責從采集設備獲取數據,提供接口獲取采集設備的屬性,以及提供回調函數把數據傳給上層。CAVOutputStream負責對采集的視頻和音頻流進行編碼、封裝,保存成一個文件。而CImagePainter則用來顯示圖像,使用了GDI繪圖,把圖像顯示到主界面的窗口。

    3.1 枚舉采集設備

    采集前我們需要先選擇設備,把所有的設備名稱列出來,其中一個方法可以用第一節介紹的運行ffmpeg命令行工具來列舉,但是這樣有兩個問題:第一,假如設備名稱帶中文,則顯示的名稱有亂碼,因此,我們不知道它真實的名稱。第二,ffmpeg沒有API返回系統中安裝的采集設備列表,雖然FFmpeg提供了API把設備名稱列舉出來,但是是打印到控制臺的,不是通過參數來返回,如下面這段代碼只能打印輸出結果到控制臺。但是對于窗口界面程序,沒有控制臺,怎么獲取命令行結果呢?

        AVFormatContext *pFmtCtx = avformat_alloc_context();
        AVDictionary* options = NULL;
        av_dict_set(&options, "list_devices", "true", 0);
        AVInputFormat *iformat = av_find_input_format("dshow");
        //printf("Device Info=============\n");
        avformat_open_input(&pFmtCtx, "video=dummy", iformat, &options);
       //printf("========================\n");

    我用了一種最傳統的做法來解決,就是通過Directshow的COM接口來枚舉設備,工程里面的EnumDevice接口就實現了枚舉設備的功能,函數原型如下:

    //枚舉指定類型的所有采集設備的名稱
    ENUMDEVICE_API HRESULT EnumDevice(CAPTURE_DEVICE_TYPE type, char * deviceList[], int nListLen, int & iNumCapDevices);

    當然,如果讀者用的采集設備是固定一種,那么可以固定采集設備的名稱,這樣做可以省點事。

    3.2 注冊FFmpeg庫

     av_register_all(); 
     avdevice_register_all();

    這兩個API可以在程序的構造函數和窗口初始化里面調用。

    相關視頻推薦

    FFmpeg:音視頻開發必需掌握的第一個開源項目_嗶哩嗶哩_bilibili

    90分鐘搞定直播邏輯-推流-流媒體服務器-拉流|ffmpeg|webrtc|rtmp|rtsp_嗶哩嗶哩_bilibili

    學習地址:【免費】FFmpeg/WebRTC/RTMP/NDK/Android音視頻流媒體高級開發-學習視頻教程-騰訊課堂

    【文章福利】需要C/C++ Linux服務器架構師及音視頻學習資料加群812855908(資料包括C/C++,Linux,golang技術,內核,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,音視頻,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK,ffmpeg,大廠面試題 等)

    3.3 打開輸入設備

    首先需要指定采集設備的名稱。如果是視頻設備類型,則名稱以“video=”開頭;如果是音頻設備類型,則名稱以“audio=”開頭。調用avformat_open_input接口打開設備,將設備名稱作為參數傳進去,注意這個設備名稱需要轉成UTF-8編碼。然后調用avformat_find_stream_info獲取流的信息,得到視頻流或音頻流的索引號,之后會頻繁用到這個索引號來定位視頻和音頻的Stream信息。接著,調用avcodec_open2打開視頻解碼器或音頻解碼器,實際上,我們可以把設備也看成是一般的文件源,而文件一般采用某種封裝格式,要播放出來需要進行解復用,分離成裸流,然后對單獨的視頻流、音頻流進行解碼。雖然采集出來的圖像或音頻都是未編碼的,但是按照FFmpeg的常規處理流程,我們需要加上“解碼”這個步驟。

           int i;
           m_pInputFormat = av_find_input_format("dshow");
           ASSERT(m_pInputFormat != NULL);
     
    	if(!m_video_device.empty())
    	{
    		int res = 0;
     
    		string device_name = "video=" + m_video_device;
     
    		string device_name_utf8 = AnsiToUTF8(device_name.c_str(), device_name.length());  //轉成UTF-8,解決設備名稱包含中文字符出現亂碼的問題
     
    		 //Set own video device's name
    		if ((res = avformat_open_input(&m_pVidFmtCtx, device_name_utf8.c_str(), m_pInputFormat, &device_param)) != 0)
    		{
    			ATLTRACE("Couldn't open input video stream.(無法打開輸入流)\n");
    			return false;
    		}
    		//input video initialize
    		if (avformat_find_stream_info(m_pVidFmtCtx, NULL) < 0)
    		{
    			ATLTRACE("Couldn't find video stream information.(無法獲取流信息)\n");
    			return false;
    		}
    		m_videoindex = -1;
    		for (i = 0; i < m_pVidFmtCtx->nb_streams; i++)
    		{
    			if (m_pVidFmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
    			{
    				m_videoindex = i;
    				break;
    			}
    		}
     
    		if (m_videoindex == -1)
    		{
    			ATLTRACE("Couldn't find a video stream.(沒有找到視頻流)\n");
    			return false;
    		}
    		if (avcodec_open2(m_pVidFmtCtx->streams[m_videoindex]->codec, avcodec_find_decoder(m_pVidFmtCtx->streams[m_videoindex]->codec->codec_id), NULL) < 0)
    		{
    			ATLTRACE("Could not open video codec.(無法打開解碼器)\n");
    			return false;
    		}
    	}
     
        //
     
    	if(!m_audio_device.empty())
    	{
    		string device_name = "audio=" + m_audio_device;
     
    		string device_name_utf8 = AnsiToUTF8(device_name.c_str(), device_name.length());  //轉成UTF-8,解決設備名稱包含中文字符出現亂碼的問題
     
    		//Set own audio device's name
    		if (avformat_open_input(&m_pAudFmtCtx, device_name_utf8.c_str(), m_pInputFormat, &device_param) != 0){
     
    			ATLTRACE("Couldn't open input audio stream.(無法打開輸入流)\n");
    			return false;
    		}
     
    		//input audio initialize
    		if (avformat_find_stream_info(m_pAudFmtCtx, NULL) < 0)
    		{
    			ATLTRACE("Couldn't find audio stream information.(無法獲取流信息)\n");
    			return false;
    		}
    		m_audioindex = -1;
    		for (i = 0; i < m_pAudFmtCtx->nb_streams; i++)
    		{
    			if (m_pAudFmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
    			{
    				m_audioindex = i;
    				break;
    			}
    		}
    		if (m_audioindex == -1)
    		{
    			ATLTRACE("Couldn't find a audio stream.(沒有找到音頻流)\n");
    			return false;
    		}
    		if (avcodec_open2(m_pAudFmtCtx->streams[m_audioindex]->codec, avcodec_find_decoder(m_pAudFmtCtx->streams[m_audioindex]->codec->codec_id), NULL) < 0)
    		{
    			ATLTRACE("Could not open audio codec.(無法打開解碼器)\n");
    			return false;
    		}
    	}

    3.4 初始化輸出流

    前面我們已經初始化了InputStream,現在需要對OutputStream進行初始化,而要初始化輸出流需要知道視頻采集的分辨率,幀率,輸出像素格式等信息,還有音頻采集設備的采樣率,聲道數,Sample格式,而這些信息可通過CAVInputStream類的接口來獲取到。下面是初始化OutputStream的代碼:

    	m_InputStream.SetVideoCaptureCB(VideoCaptureCallback);
    	m_InputStream.SetAudioCaptureCB(AudioCaptureCallback);
     
    	bool bRet;
    	bRet = m_InputStream.OpenInputStream(); //初始化采集設備
    	if(!bRet)
    	{
    		MessageBox(_T("打開采集設備失敗"), _T("提示"), MB_OK|MB_ICONWARNING);
    		return 1;
    	}
     
    	int cx, cy, fps;
    	AVPixelFormat pixel_fmt;
    	if(m_InputStream.GetVideoInputInfo(cx, cy, fps, pixel_fmt)) //獲取視頻采集源的信息
    	{
    		m_OutputStream.SetVideoCodecProp(AV_CODEC_ID_H264, fps, 500000, 100, cx, cy); //設置視頻編碼器屬性
    	}
     
           int sample_rate = 0, channels = 0;
    	AVSampleFormat  sample_fmt;
    	if(m_InputStream.GetAudioInputInfo(sample_fmt, sample_rate, channels)) //獲取音頻采集源的信息
    	{
    		m_OutputStream.SetAudioCodecProp(AV_CODEC_ID_AAC, sample_rate, channels, 32000); //設置音頻編碼器屬性
    	}
     
    	//從Config.INI文件中讀取錄制文件路徑
    	P_GetProfileString(_T("Client"), "file_path", m_szFilePath, sizeof(m_szFilePath));
     
    	bRet  = m_OutputStream.OpenOutputStream(m_szFilePath); //設置輸出路徑
    	if(!bRet)
    	{
    		MessageBox(_T("初始化輸出失敗"), _T("提示"), MB_OK|MB_ICONWARNING);
    		return 1;
    	}

    在上面的代碼片段中,首先設置了視頻和音頻的數據回調函數。當采集開始時,視頻和音頻數據就會傳遞給相應的函數去處理,在該程序中,回調函數主要對圖像或音頻進行編碼,然后封裝成FFmpeg支持的容器(例如mkv/avi/mpg/ts/mp4)。另外,需要初始化OutputStream的VideoCodec和AudioCodec的屬性,在我的程序中,視頻編碼器是H264,音頻編碼器用AAC,通過CAVInputStream對象獲得輸入流的信息之后再賦值給輸出流相應的參數。最后調用m_OutputStream對象的OpenOutputStream成員函數打開編碼器和錄制的容器,其中我們需要傳入一個輸出文件路徑作為參數,這個為錄制的文件路徑,路徑是在Config.ini文件里配置的。如果OpenOutputStream函數返回true,則表示初始化輸出流成功。

    3.5 讀取采集數據

    接著,我們就可以開始采集了。開始采集的函數實現如下:

    bool  CAVInputStream::StartCapture()
    {
    	if (m_videoindex == -1 && m_audioindex == -1)
    	{
    		ATLTRACE("錯誤:你沒有打開設備\n");
    		return false;
    	}
     
        m_start_time = av_gettime();
     
    	m_exit_thread = false;
     
    	if(!m_video_device.empty())
    	{
    		m_hCapVideoThread = CreateThread(
            NULL,                   // default security attributes
            0,                      // use default stack size  
            CaptureVideoThreadFunc,       // thread function name
            this,          // argument to thread function 
            0,                      // use default creation flags 
            NULL);   // returns the thread identifier 
    	}
     
    	if(!m_audio_device.empty())
    	{
    		m_hCapAudioThread = CreateThread(
            NULL,                   // default security attributes
            0,                      // use default stack size  
            CaptureAudioThreadFunc,       // thread function name
            this,          // argument to thread function 
            0,                      // use default creation flags 
            NULL);   // returns the thread identifier 
    	}
     
    	return true;
    }

    StartCapture函數分別建立了一個讀取視頻包和讀取音頻包的線程,兩個線程各自獨立工作,分別從視頻采集設備,音頻采集設備獲取到數據,然后進行后續的處理。(注意:兩個線程同時向一個文件寫數據可能會有同步的問題,FFmpeg內部可能沒有做多線程安全訪問的處理,所以最好在自己線程里加一個鎖進行互斥,從而保護臨界區的安全)

    其中,讀取攝像頭數據的線程的處理代碼如下:

    DWORD WINAPI CAVInputStream::CaptureVideoThreadFunc(LPVOID lParam)
    {
    	CAVInputStream * pThis = (CAVInputStream*)lParam;
     
    	pThis->ReadVideoPackets();
     
    	return 0;
    }
     
    int  CAVInputStream::ReadVideoPackets()
    {
    	if(dec_pkt == NULL)
    	{
    		prepare before decode and encode
    		dec_pkt = (AVPacket *)av_malloc(sizeof(AVPacket));
    	}
     
    	int encode_video = 1;
    	int ret;
     
    	//start decode and encode
     
    	while (encode_video)
    	{
    		if (m_exit_thread)
    			break;
     
    		AVFrame * pframe = NULL;
    		if ((ret = av_read_frame(m_pVidFmtCtx, dec_pkt)) >= 0)
    		{
    			pframe = av_frame_alloc();
    			if (!pframe) 
    			{
    				ret = AVERROR(ENOMEM);
    				return ret;
    			}
    			int dec_got_frame = 0;
    			ret = avcodec_decode_video2(m_pVidFmtCtx->streams[dec_pkt->stream_index]->codec, pframe, &dec_got_frame, dec_pkt);
    			if (ret < 0) 
    			{
    				av_frame_free(&pframe);
    				av_log(NULL, AV_LOG_ERROR, "Decoding failed\n");
    				break;
    			}
    			if (dec_got_frame)
    			{
                    if(m_pVideoCBFunc)
    				{
    					CAutoLock lock(&m_WriteLock);
     
    					m_pVideoCBFunc(m_pVidFmtCtx->streams[dec_pkt->stream_index], m_pVidFmtCtx->streams[m_videoindex]->codec->pix_fmt, pframe, av_gettime() - m_start_time);
    				}
     
    				av_frame_free(&pframe);
    			}
    			else 
    			{
    				av_frame_free(&pframe);
    			}
     
    			av_free_packet(dec_pkt);
    		}
    		else
    		{
    			if (ret == AVERROR_EOF)
    				encode_video = 0;
    			else
    			{
    				ATLTRACE("Could not read video frame\n");
    				break;
    			}
    		}
    	}
     
    	return 0;
    }

    在CAVInputStream::ReadVideoPackets()函數中不停地調用 av_read_frame讀取采集到的圖像幀,接著調用avcodec_decode_video2進行“解碼”,這樣獲得了原始的圖像,圖像可能是RGB或YUV格式。解碼后的圖像通過m_pVideoCBFunc指向的回調函數回調給上層處理,回調函數里可進行后續的一些操作,比如對視頻幀編碼或直接顯示。

    3.6 編碼、封裝成文件

    CAVInputStream的工作線程里面讀取到的視頻幀和音頻包通過回調函數傳給CAVOuputStream類去處理。下面是兩個回調函數的實現:

    //采集到的視頻圖像回調
    LRESULT CALLBACK VideoCaptureCallback(AVStream * input_st, enum PixelFormat pix_fmt, AVFrame *pframe, INT64 lTimeStamp)
    {
    	if(gpMainFrame->IsPreview())
    	{
    	   gpMainFrame->m_Painter.Play(input_st, pframe);
    	}
     
    	gpMainFrame->m_OutputStream.write_video_frame(input_st, pix_fmt, pframe, lTimeStamp);
    	return 0;
    }
     
    //采集到的音頻數據回調
    LRESULT CALLBACK AudioCaptureCallback(AVStream * input_st, AVFrame *pframe, INT64 lTimeStamp)
    {
    	gpMainFrame->m_OutputStream.write_audio_frame(input_st, pframe, lTimeStamp);
    	return 0;
    }

    視頻回調函數調用了CAVOutputStream的成員函數write_video_frame,這個函數對傳入的圖像幀進行編碼(H264),并且寫到指定的封裝文件;而音頻回調函數則調用了CAVOutputStream的另外一個成員函數write_audio_frame,這個函數負責對音頻編碼(AAC),然后輸出到指定的封裝文件。下面是Write_video_frame函數的實現代碼:

    //input_st -- 輸入流的信息
    //input_frame -- 輸入視頻幀的信息
    //lTimeStamp -- 時間戳,時間單位為/1000000
    //
    int CAVOutputStream::write_video_frame(AVStream * input_st, enum PixelFormat pix_fmt, AVFrame *pframe, INT64 lTimeStamp)
    {
    	if(video_st == NULL)
    	   return -1;
     
    	//ATLTRACE("Video timestamp: %ld \n", lTimeStamp);
     
       if(m_first_vid_time1 == -1)
       {
    	   TRACE("First Video timestamp: %ld \n", lTimeStamp);
    	   m_first_vid_time1 = lTimeStamp;
       }
     
    	AVRational time_base_q = { 1, AV_TIME_BASE };
     
    	if(img_convert_ctx == NULL)
    	{
    		//camera data may has a pix fmt of RGB or sth else,convert it to YUV420
    		img_convert_ctx = sws_getContext(m_width, m_height,
    			pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
    	}
     
        sws_scale(img_convert_ctx, (const uint8_t* const*)pframe->data, pframe->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
        pFrameYUV->width = pframe->width;
        pFrameYUV->height = pframe->height;
        pFrameYUV->format = PIX_FMT_YUV420P;
     
        enc_pkt.data = NULL;
        enc_pkt.size = 0;
        av_init_packet(&enc_pkt);
     
        int ret;
        int enc_got_frame = 0;
        ret = avcodec_encode_video2(pCodecCtx, &enc_pkt, pFrameYUV, &enc_got_frame);
     
        if (enc_got_frame == 1)
    	{
            //printf("Succeed to encode frame: %5d\tsize:%5d\n", framecnt, enc_pkt.size);
           
    		if(m_first_vid_time2 == -1)
    		{
    			m_first_vid_time2 = lTimeStamp;
    		}
     
            enc_pkt.stream_index = video_st->index;						
    	
    		//enc_pkt.pts= av_rescale_q(lTimeStamp, time_base_q, video_st->time_base);
    		enc_pkt.pts = (INT64)video_st->time_base.den * lTimeStamp/AV_TIME_BASE;
     
            m_vid_framecnt++;
    		
            ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt);
    		if(ret < 0)	
    		{
    			char tmpErrString[128] = {0};
    			ATLTRACE("Could not write video frame, error: %s\n", av_make_error_string(tmpErrString, AV_ERROR_MAX_STRING_SIZE, ret));
    			av_packet_unref(&enc_pkt);
    			return ret;
    		}
     
            av_free_packet(&enc_pkt);
    	}
    	else if(ret == 0)
    	{
    		ATLTRACE("Buffer video frame, timestamp: %I64d.\n", lTimeStamp); //編碼器緩沖幀
    	}
     
    	return 0;
    }

    Write_video_frame和write_audio_frame是CAVOutputStream的兩個很重要的函數,其中對音頻包的處理略為復雜一些,主要是因為輸入的音頻和編碼后的音頻的frame_size不一樣,中間需要一個Fifo作緩沖隊列。另外時間戳PTS的計算也是很關鍵的,弄得不好保存的文件播放視音頻就不同步了,具體怎么實現你們看代碼吧。

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有