ffmpeg4.4 学习笔记 -(1)将输入的视频文件转换为一帧一帧的ppm 文件_商少-优快云博客
参考上述文章,通过ffmpeg 读取文件中的音频和读取文件中的视频的差异在于,我们应该处理AVFormatContext->streams中codec_type 为 AVMEDIA_TYPE_AUDIO 的流,即音频流。之后的找到逻辑类似,找到对应流的编码器并初始化该编码器,之后循环从AVFormatContext 中读取packet,如果packet 的streamindex 和 音频流的streamindex一样,我们就得到了一个音频packet,之后将该packet 交给音频解码器,即可得到音频frame。
视频frame 我们通过sdl或者MFC 做显示,音频frame 当前我们通过sdl 做播放。
实际实现如下:
1. 找到音频流
// Find the first audio stream
int audioStream = -1;
for (int i = 0; i < avFormatContext->nb_streams; i++)
if (avFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
audioStream = i;
break;
}
if (audioStream == -1)
return ; // Didn't find a audio stream
2. 初始化音频codec
AVCodecContext* avCodecContext = avcodec_alloc_context3(NULL);
result = avcodec_parameters_to_context(avCodecContext, avFormatContext->streams[audioStream]->codecpar);
if (result < 0)
{
assert(false);
}
avCodecContext->pkt_timebase = avFormatContext->streams[audioStream]->time_base;
AVCodec* codec = avcodec_find_decoder(avCodecContext->codec_id);
avCodecContext->codec_id = codec->id;
result = avcodec_open2(avCodecContext, codec, nullptr);
if (result < 0)
{
msgBoxFFmpegError(result);
assert(false);
}
3. 循环读取音频packet
while (true)
{
//Sleep(33);
int readResult = -1;
if (!bReadEof)
{
readResult = av_read_frame(avFormatContext, &packet);
if (readResult < 0) {
::MessageBoxA(0, 0, GetFFmpegErorString(readResult).c_str(), 0);
bReadEof = true;
}
else if (readResult == 0) {
static int iCnt = 0;
if (packet.stream_index == audioStream) {
++iCnt;
}
CString str;
str.Format(L"cunt[%d]\r\n", iCnt);
OutputDebugStringW(str.GetBuffer());
}
}
4. 解码音频packet ,得到音频frame
if (bReadEof)
{
// 需要给刷空
avcodec_send_packet(avCodecContext, NULL);
}
else
{
// Is this a packet from the video stream?
if (packet.stream_index == audioStream) {
// Decode video frame
avcodec_send_packet(avCodecContext, &packet);
}
}
int receiveResult = avcodec_receive_frame(avCodecContext, pFrame);
5. 通过SDL播放得到的frame
最简单的基于FFMPEG+SDL的音频播放器 ver2 (采用SDL2.0)_雷霄骅(leixiaohua1020)的专栏-优快云博客_ffmpeg sdl 音频
参考上述文章,sdl 播放音频的基本步骤为:
初始化
1. SDL_Init() 初始化SDL
2. SDL_OpenAudio 根据参数(SDL_AudioSpec) 打开音频设备
3. SDL_PauseAudio() 播放音频数据
循环播放数据
//Buffer:
//|-----------|-------------|
//chunk-------pos---len-----|
static Uint8* audio_chunk;
static Uint32 audio_len;
static Uint8* audio_pos;
/* The audio function callback takes the following parameters:
* stream: A pointer to the audio buffer to be filled
* len: The length (in bytes) of the audio buffer
*/
void fill_audio(void* udata, Uint8* stream, int len) {
//SDL 2.0
SDL_memset(stream, 0, len);
if (audio_len == 0)
return;
len = (len > audio_len ? audio_len : len); /* Mix as much data as possible */
SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
audio_pos += len;
audio_len -= len;
}
//-----------------
const uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
int out_nb_samples = avCodecContext->frame_size;
AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
int out_sample_rate = 44100;
int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
int out_buffer_size = av_samples_get_buffer_size(nullptr, out_channels, out_nb_samples, out_sample_fmt, 1);
uint8_t* out_buffer = (uint8_t*)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
int i = 0;
SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER);
SDL_AudioSpec audioSpec = {};
audioSpec.freq = out_sample_rate;
audioSpec.format = AUDIO_S16SYS;
audioSpec.channels = out_channels;
audioSpec.silence = 0;
audioSpec.samples = out_nb_samples;
audioSpec.callback = fill_audio;
audioSpec.userdata = avCodecContext;
if (SDL_OpenAudio(&audioSpec, nullptr) < 0) {
return ;
}
//FIX:Some Codec's Context Information is missing
int64_t in_channel_layout = av_get_default_channel_layout(avCodecContext->channels);
//Swr
SwrContext* au_convert_ctx = swr_alloc();
au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, out_channel_layout, out_sample_fmt, out_sample_rate,
in_channel_layout, avCodecContext->sample_fmt, avCodecContext->sample_rate, 0, NULL);
swr_init(au_convert_ctx);
//Play
SDL_PauseAudio(0);
播放:
swr_convert(au_convert_ctx, &out_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t**)pFrame->data, pFrame->nb_samples);
while (audio_len > 0)//Wait until finish
SDL_Delay(1);
//Set audio buffer (PCM data)
audio_chunk = (Uint8*)out_buffer;
//Audio buffer length
audio_len = out_buffer_size;
audio_pos = audio_chunk;