用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庫函數,其中還有不太完善地方需要改進。