音視頻處理FFMPeg開發實戰(11) -- 一個簡單的播放器

用FFMPeg庫實現一個視頻播放器,涉及到以下環節:

1、圖片格式變換

由於解碼出來的圖片不一定是需要的格式,由於在界面上顯示一般是用位圖形式,可能也有第三方的顯示庫如SDL,不需要轉化直接顯示各種格式的位圖。

圖片格式轉換,可以用濾鏡。本文直接調用sws* 函數進行轉換。

初始化SwsContext:


	g_pSwsContext = sws_getContext(g_decoderVideoCodecContext->width, g_decoderVideoCodecContext->height,
		g_decoderVideoCodecContext->pix_fmt,
		g_nWindowWidth, g_nWindowHeight,
		AV_PIX_FMT_RGBA,
		SWS_BICUBIC, NULL, NULL, NULL
		);

	int len = av_image_get_buffer_size(AV_PIX_FMT_RGBA, g_nWindowWidth, g_nWindowHeight, 1);
	g_rgb_buffer = (uint8_t *)av_malloc(len);
	av_image_fill_arrays(g_ConvertedFrame.data,
		g_ConvertedFrame.linesize,
		g_rgb_buffer,
		AV_PIX_FMT_RGBA,
		g_nWindowWidth,
		g_nWindowHeight, 1);

解碼後的轉換:

int ret = sws_scale(g_pSwsContext,
						(const uint8_t* const*)g_pSrcVideoFrame->data,
						g_pSrcVideoFrame->linesize,
						0,
						g_decoderVideoCodecContext->height,
						g_ConvertedFrame.data,
						g_ConvertedFrame.linesize);
					memcpy(image, g_rgb_buffer, g_ConvertedFrame.linesize[0]*g_nWindowHeight);

2、音頻格式變換

由於解碼出來的音頻格式不一定是需要的格式,也需要轉化。也可以用濾鏡轉換,本文也直接調用Swr類的庫函數轉換了指定的格式:

如果解碼出來的不是AV_SAMPLE_FMT_S16,則調用swr_alloc()和swr_init()初始化一個轉換用的SwrContext對象。

	if (fmt != AV_SAMPLE_FMT_S16)
	{
		if (g_pSwrContext)
		{
			swr_free(&g_pSwrContext);
			g_pSwrContext = nullptr;
		}

		int channel_layout = g_inputFormatContext->streams[g_nAudioIndex]->codec->channel_layout;
		int sample_rate = g_inputFormatContext->streams[g_nAudioIndex]->codec->sample_rate;
		g_pSwrContext = swr_alloc();
		av_opt_set_int(g_pSwrContext, "in_channel_layout", channel_layout, 0);
		av_opt_set_int(g_pSwrContext, "out_channel_layout", channel_layout, 0);
		av_opt_set_int(g_pSwrContext, "in_sample_rate", sample_rate, 0);
		av_opt_set_int(g_pSwrContext, "out_sample_rate", sample_rate, 0);
		av_opt_set_sample_fmt(g_pSwrContext, "in_sample_fmt", fmt, 0);
		av_opt_set_sample_fmt(g_pSwrContext, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
		swr_init(g_pSwrContext);
	}

解碼後的格式轉換:

if (g_pSwrContext)
{
	int srcNbSamples = g_pSrcAudioFrame->nb_samples;
	int srcRate = g_inputFormatContext->streams[g_nAudioIndex]->codec->sample_rate;
	int dstRate = srcRate;
	int dstNbSamples = av_rescale_rnd(srcNbSamples, dstRate, srcRate, AV_ROUND_UP);
	AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;
	int64_t channel_layout = g_inputFormatContext->streams[g_nAudioIndex]->codec->channel_layout;
	int dstNbChannels = av_get_channel_layout_nb_channels(channel_layout);
	dstNbChannels = dstNbChannels > 0 ? dstNbChannels : 1;
	int ret = av_samples_alloc_array_and_samples(&dst_data, &dstLinesize, dstNbChannels, dstNbSamples, dst_sample_fmt, 0);

	ret = swr_convert(g_pSwrContext, dst_data, dstNbSamples, (const uint8_t **)g_pSrcAudioFrame->data, srcNbSamples);

	memcpy(g_audio_data_buffer + nAudioBufferIndex, *dst_data, dstLinesize);
	nAudioBufferIndex += dstLinesize;
	av_freep((void*)&dst_data[0]);
					audio = 1;
}

3、播放時音視頻同步

本例中以音頻時間去同步視頻,由於人對音頻更敏感,視頻不太敏感。

4、工程源碼下載:

本例在Debug – x86下編譯成功
附帶開發環境,本例僅是學習探討FFPMEG庫函數,其中還有不太完善地方需要改進。

下載源碼

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