關於ffmpeg的例子muxing.c

在ffmpeg的官方例子中有一個muxing.c,這個例子是演示如何用ffmpeg進行打包(muxing),但是這個例子有些問題,說好聽點是不完善,說不好聽就是有錯誤。ffmpeg.c是非常完善的,對比ffmpeg.c我發現主要有以下兩個錯誤:

1、在使用avcodec_encode_audio2/avcodec_encode_video2編碼前,沒有給定時間戳。

2、在main函數的for循環之後,沒有flush,也就是還有一些延遲的幀在緩衝中,沒有寫進輸出文件。在編碼時,並不是每一個輸入幀立即編碼得到輸出幀,而往往是輸入N多幀之後纔開始輸出幀,我見過最多輸入60幀之後纔出現第一個輸入幀的,那麼就出現了一個問題,以輸入爲循環體,輸入結束循環也結束,那麼就還有一些幀在緩存中,此時我們需要將其拿出來,編碼,再寫進輸出文件。

下面我以音頻爲例修改了代碼,首先是函數write_audio_frame

static void write_audio_frame(AVFormatContext *oc, AVStream *st)
{
    AVCodecContext *c;
    AVPacket pkt = { 0 }; // data and size must be 0;
    AVFrame *frame = NULL;
    int got_packet, ret, dst_nb_samples;
	AVRational r = {1, AV_TIME_BASE};

    av_init_packet(&pkt);
	pkt.data = NULL;
    pkt.size = 0;
    c = st->codec;

	if(!frame && !(frame = avcodec_alloc_frame()))
		return ;
	else
		avcodec_get_frame_defaults(frame);

    get_audio_frame((int16_t *)src_samples_data[0], src_nb_samples, c->channels);

    /* convert samples from native format to destination codec format, using the resampler */
    if (swr_ctx) {
        /* compute destination number of samples */
        dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, c->sample_rate) + src_nb_samples,
                                        c->sample_rate, c->sample_rate, AV_ROUND_UP);
        if (dst_nb_samples > max_dst_nb_samples) {
            av_free(dst_samples_data[0]);
            ret = av_samples_alloc(dst_samples_data, &dst_samples_linesize, c->channels,
                                   dst_nb_samples, c->sample_fmt, 0);
            if (ret < 0)
                exit(1);
            max_dst_nb_samples = dst_nb_samples;
            dst_samples_size = av_samples_get_buffer_size(NULL, c->channels, dst_nb_samples,
                                                          c->sample_fmt, 0);
        }

        /* convert to destination format */
        ret = swr_convert(swr_ctx,
                          dst_samples_data, dst_nb_samples,
                          (const uint8_t **)src_samples_data, src_nb_samples);
        if (ret < 0) {
            fprintf(stderr, "Error while converting\n");
            exit(1);
        }
    } else {
        dst_samples_data[0] = src_samples_data[0];
        dst_nb_samples = src_nb_samples;
    }

    frame->nb_samples = dst_nb_samples;
    avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, dst_samples_data[0], dst_samples_size, 0);
	//下面兩句我加的。編碼前一定要給frame時間戳
	frame->pts = lastpts;
	lastpts = frame->pts + frame->nb_samples;

    ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet);	//如果沒有前兩句,編碼之後的pts是無效的
    if (ret < 0) {
        fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
        exit(1);
    }
	

    if (!got_packet)
        return;

    pkt.stream_index = st->index;
	//下面兩句我加的,加了纔不是提示“encoder did not produce proper pts, make some up”錯誤
	pkt.pts = av_rescale_q(pkt.pts, st->codec->time_base, st->time_base);//
	pkt.dts = av_rescale_q(pkt.dts, st->codec->time_base, st->time_base);//
	pkt.duration = av_rescale_q(pkt.duration, st->codec->time_base, st->time_base);
    /* Write the compressed frame to the media file. */
    ret = av_interleaved_write_frame(oc, &pkt);
    if (ret != 0) {
        fprintf(stderr, "Error while writing audio frame: %s\n",
                av_err2str(ret));
        exit(1);
    }
    avcodec_free_frame(&frame);
}
修改main函數中的代碼,在for(;;)循環之後:

//下面幾行是獲得delay幀
	c = audio_st->codec;
	for(got_output=1; got_output>0; i++)
	{
		av_init_packet(&pkt);  
		pkt.data = NULL;
        pkt.size = 0;
		ret = avcodec_encode_audio2(c, &pkt, NULL, &got_output);  
		if (ret < 0) 
		{  
			fprintf(stderr, "Error encoding frame\n");  
			exit(1); 
		}
		if (got_output) 
		{ 
		//	audio_st->pts.val += 1024;//av_rescale_q(1, audio_st->codec->time_base, audio_st->time_base);
			pkt.pts = av_rescale_q(pkt.pts, c->time_base, audio_st->time_base);//audio_st->pts.val;
			pkt.dts = av_rescale_q(pkt.dts, c->time_base, audio_st->time_base);//audio_st->pts.val;
			pkt.duration = av_rescale_q(pkt.duration, c->time_base, audio_st->time_base);
			ret = av_interleaved_write_frame(oc, &pkt);
			av_free_packet(&pkt);  
		}
	}

這裏僅僅修改了音頻方面的處理,視頻方面可以家有完全參考以上代碼。另外我還將原來由程序生成的原始圖像,改爲了從BMP文件讀取畫面,成功。有興趣的朋友可以交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章