1. 程式人生 > >Freeswitch的mod_av模組優化

Freeswitch的mod_av模組優化

最近在解決一個Freeswitch錄製視訊後出現視訊檔案播放花屏問題,大概梳理mod_av模組的錄製流程,並做了一些流程上的優化。


mod_av的錄製流程如下:

收到的RTP音視訊包-> 解碼 -> 然後寫檔案(首先是編碼-> 然後呼叫ffmpeg的音視訊混合介面輸出為mp4檔案)


首先做的流程優化,去掉畫中橫線的兩個流程,直接將接收到的編碼幀寫檔案:

收到的RTP音視訊包-> 解碼 -> 然後寫檔案(首先是編碼-> 呼叫ffmpeg的音視訊混合介面輸出為mp4檔案)


然後把錄製出來的檔案播放,發現還是花屏,多次嘗試後發現,是在呼叫av_packet_rescale_ts後,才呼叫av_interleaved_write_frame,而av_packet_rescale_ts函式則是把AVCodecContext 的time_base轉換為AVStream的time_base,先看看time_base的理解:

ffmpeg存在多個時間基準(time_base),對應不同的階段(結構體),每個time_base具體的值不一樣,ffmpeg提供函式在各個time_base中進行切換。


AVStream->time_base比AVCodecContext->time_base精度要高(數值要小),比如AVStream->time_base為1/90000,而AVCodecContext->time_base為1/30(假設frame_rate為30);同樣的pts和dts,以AVStream->time_base為單位,數值要比以AVCodecContext->time_base為單位要大。

編碼後,pkt.pts、pkt.dts使用AVCodecContext->time_base為單位,後通過呼叫"av_packet_rescale_ts"轉換為AVStream->time_base為單位。


首先修改time_base的初始化賦值:

原來的值:

		mst->st->time_base.den = 1000;
		mst->st->time_base.num = 1;
		c->time_base.den = 1000;
		c->time_base.num = 1;


修改為:

		mst->st->time_base.den = 90000;
		mst->st->time_base.num = 1;
		c->time_base.den = 25;
		c->time_base.num = 1;



從網路接收到的rtp包是編碼後的包,首先需要把多個rtp包組成一個完整的frame的包,然後pts需要重設,程式碼:

        	       if (frame->m) {
                		AVPacket *pkt = (AVPacket *)malloc(sizeof(AVPacket));
                		uint32_t size = switch_buffer_inuse(context_temp->nalu_buffer);
                            if (size > 0){
                                uint8_t *temp_data; 	    
                                static uint8_t ff_input_buffer_padding[FF_INPUT_BUFFER_PADDING_SIZE] = { 0};

			                    switch_buffer_write(context_temp->nalu_buffer, ff_input_buffer_padding, sizeof(ff_input_buffer_padding));  //lyz delete

                                switch_buffer_peek_zerocopy(context_temp->nalu_buffer, (const void **)&temp_data);

                                av_init_packet(pkt);
                                av_new_packet(pkt, size);
                                pkt->size = size;

                                if (switch_test_flag(frame, SFF_WAIT_KEY_FRAME)){
                                        pkt->flags        |= AV_PKT_FLAG_KEY;
                                }

                                pkt->pts = context_temp->pts++;//context_temp->pts + 40;//((frame->timestamp - context_temp->last_received_timestamp)/90);
                                pkt->dts = pkt->pts; 
                                //pkt->duration = 40;

                                //context_temp->pts = pkt->pts;
                                
                                memcpy((unsigned char *)pkt->data, (unsigned char *)temp_data, size);
                                /*
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "write video stream,pkt:%08x, data:%08x, pkt->pts:%d, data[0]:%02x, data[1]:%02x, data[2]:%02x, data[3]:%02x, data[4]:%02x\n", 
                                    pkt, 
                                    pkt->data, 
                                    pkt->pts, 
                                    pkt->data[0],
                                    pkt->data[1],
                                    pkt->data[2],
                                    pkt->data[3],
                                    pkt->data[4]);
                                    */

                        	    context_temp->last_received_timestamp = frame->timestamp;
                        	    context_temp->last_received_complete_picture = frame->m ? SWITCH_TRUE : SWITCH_FALSE;

                                switch_queue_push(context->eh.video_queue, pkt);
                            }
                        
                		switch_buffer_zero(context_temp->nalu_buffer);
        	       }


註釋掉mod_av原來設定pts的程式碼,

if (switch_queue_pop(eh->video_queue, &pop) == SWITCH_STATUS_SUCCESS) {
                     switch_img_free(&img);

			if (!pop) {
				goto endfor;
			}

                    pkt = (AVPacket *)pop;



            ret = write_frame(eh->fc, &eh->video_st->st->codec->time_base, eh->video_st->st, pkt);

後來發現,不修改錄製流程,只修改編碼時pts的值也能解決這個問題,只需要把eh->video_st->frame->pts每次累加即可。