基于QT+FFMPEG的音视频开发(二)——解码
我的大部分学习都来自雷神,没有基础去雷神博客转转,每次都有很多收获。
https://blog.csdn.net/leixiaohua1020/article/details/42658139
一、解码一般步骤
avformat_open_input(); //解封装
avformat_find_stream_info(); //获取流信息
avcodec_find_decoder(); //寻找解码器
avcodec_alloc_context3(); //打开解码器上下文
avcodec_parameters_to_context(); //复制
avcodec_open2(); //打开解码器
av_read_frame(); //读取音视频流
avcodec_send_packet();
avcodec_receive_frame(); //最后为解码
主要流程图可以去雷神那看看,对于像我这样的初学者很有帮助。
二、avi解码yuv、pcm
我的Demo视频的封装格式为avi,视频轨为mpeg4,音频轨为mp3,解码以后输出像素文件yuv和音频裸流pcm。
1. 解码yuv、
根据步骤,先是解封装和获取音视频流信息。
const char *file = "E:/workspace/my.avi";
AVFormatContext *ic;
ic = avformat_alloc_context();
if(avformat_open_input(&ic, file, NULL, NULL) != 0)
{
cout << "cannot open file : " << file;
return -1;
}
if(avformat_find_stream_info(ic, NULL) < 0)
{
cout << "cannot find stream infomation" << endl;
return -1;
}
然后遍历流信息,分离音视频。
int video_index = -1;
int i;
for(i = 0; i < ic->nb_streams; i++)
{
if(ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
video_index = i;
break;
}
}
if(video_index == -1)
{
cout << "not find video stream" << endl;
return -1;
}
接下来就是寻找并打开解码器
AVCodec *vCodec = avcodec_find_decoder(ic->streams[video_index]->codecpar->codec_id);
if(vCodec == NULL)
{
cout << "not find video decoder" << endl;
return -1;
}
AVCodecContext *vCC = avcodec_alloc_context3(vCodec);
avcodec_parameters_to_context(vCC, ic->streams[video_index]->codecpar);
if(avcodec_open2(vCC, vCodec, NULL) < 0)
{
cout << "cannot open video decoder" << endl;
return -1;
}
最后读取帧数据并解码
while(av_read_frame(ic, pkt) >= 0)
{
if(pkt->stream_index == video_index)
{
int ret = avcodec_send_packet(vCC, pkt);
if(ret < 0)
{
cout << "send packet for decoding video failed" << endl;
return -1;
}
ret = avcodec_receive_frame(vCC, frame);
if(ret < 0)
{
cout << "error during decoding video : " << ret << endl;
return -1;
}
}
av_packet_unref(pkt);
}
2. 音频同理
const char *file = "E:/workspace/my.avi";
AVFormatContext *ic;
ic = avformat_alloc_context();
if(avformat_open_input(&ic, file, NULL, NULL) != 0)
{
cout << "cannot open file : " << file;
return -1;
}
if(avformat_find_stream_info(ic, NULL) < 0)
{
cout << "cannot find stream infomation" << endl;
return -1;
}
int audio_index = -1;
int i;
for(i = 0; i < ic->nb_streams; i++)
{
if(ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
audio_index =i;
break;
}
}
if(audio_index == -1)
{
cout << "not find audio stream" << endl;
return -1;
}
AVCodec *aCodec = avcodec_find_decoder(ic->streams[audio_index]->codecpar->codec_id);
if(aCodec == NULL)
{
cout << "not find audio decoder" << endl;
return -1;
}
AVCodecContext *aCC = avcodec_alloc_context3(aCodec);
avcodec_parameters_to_context(aCC, ic->streams[audio_index]->codecpar);
if(avcodec_open2(aCC, aCodec, NULL) < 0)
{
cout << "cannot open audio decoder" << endl;
return -1;
}
while(av_read_frame(ic, pkt) >= 0)
{
if(pkt->stream_index == audio_index)
{
int ret = avcodec_send_packet(aCC, pkt);
if(ret < 0)
{
cout << "send packet for decoding audio failed" << endl;
return -1;
}
ret = avcodec_receive_frame(aCC, frame);
if(ret < 0)
{
cout << "error during decoding audio : " << ret << endl;
return -1;
}
}
av_packet_unref(pkt);
}
源码
最后,将音视频解码放一起,并输出yuv与pcm文件。
别忘了最后释放内存。
int main(int argc, char *argv[])
{
const char *file = "E:/workspace/my.avi";
AVFormatContext *ic;
ic = avformat_alloc_context();
if(avformat_open_input(&ic, file, NULL, NULL) != 0)
{
cout << "cannot open file : " << file;
return -1;
}
if(avformat_find_stream_info(ic, NULL) < 0)
{
cout << "cannot find stream infomation" << endl;
return -1;
}
int video_index = -1;
int audio_index = -1;
int i;
for(i = 0; i < ic->nb_streams; i++)
{
if(ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
video_index = i;
else if(ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
audio_index =i;
}
if(video_index == -1)
{
cout << "not find video stream" << endl;
return -1;
}
if(audio_index == -1)
{
cout << "not find audio stream" << endl;
return -1;
}
AVCodec *vCodec = avcodec_find_decoder(ic->streams[video_index]->codecpar->codec_id);
if(vCodec == NULL)
{
cout << "not find video decoder" << endl;
return -1;
}
AVCodecContext *vCC = avcodec_alloc_context3(vCodec);
avcodec_parameters_to_context(vCC, ic->streams[video_index]->codecpar);
if(avcodec_open2(vCC, vCodec, NULL) < 0)
{
cout << "cannot open video decoder" << endl;
return -1;
}
AVCodec *aCodec = avcodec_find_decoder(ic->streams[audio_index]->codecpar->codec_id);
if(aCodec == NULL)
{
cout << "not find audio decoder" << endl;
return -1;
}
AVCodecContext *aCC = avcodec_alloc_context3(aCodec);
avcodec_parameters_to_context(aCC, ic->streams[audio_index]->codecpar);
if(avcodec_open2(aCC, aCodec, NULL) < 0)
{
cout << "cannot open audio decoder" << endl;
return -1;
}
AVFrame *frame = av_frame_alloc(); //暂存解码数据
AVFrame *yuv = av_frame_alloc();
int yuv_bufferSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 800,
600, 1);
uint8_t *yuv_buffer = (uint8_t*)av_malloc(yuv_bufferSize);
av_image_fill_arrays(yuv->data, yuv->linesize, yuv_buffer, AV_PIX_FMT_YUV420P,
800, 600, 1);
int channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
int samples = aCC->frame_size;
int audio_bufferSize = av_samples_get_buffer_size(NULL, channels, samples,
AV_SAMPLE_FMT_S16, 1);
uint8_t *audio_buffer = (uint8_t*)av_malloc(192000 * 2);
AVPacket *pkt = (AVPacket*)av_malloc(sizeof(AVPacket));
FILE *yuv_file = fopen("out.yuv", "wb");
FILE *pcm_file = fopen("out.pcm", "wb");
while(av_read_frame(ic, pkt) >= 0)
{
if(pkt->stream_index == video_index)
{
int ret = avcodec_send_packet(vCC, pkt);
if(ret < 0)
{
cout << "send packet for decoding video failed" << endl;
return -1;
}
ret = avcodec_receive_frame(vCC, frame);
if(ret < 0)
{
cout << "error during decoding video : " << ret << endl;
return -1;
}
SwsContext *sws = sws_alloc_context();
sws = sws_getCachedContext(sws, vCC->width, vCC->height,
vCC->pix_fmt, 800, 600,
AV_PIX_FMT_YUV420P, SWS_BICUBIC,
NULL, NULL, NULL);
sws_scale(sws, (const uint8_t*const*)frame->data, frame->linesize,
0, vCC->height, yuv->data, yuv->linesize);
int y_size = 800 * 600;
fwrite(yuv->data[0], 1, y_size, yuv_file);
fwrite(yuv->data[1], 1, y_size / 4, yuv_file);
fwrite(yuv->data[2], 1, y_size / 4, yuv_file);
cout << "1 video frame ok" << endl;
}
else if(pkt->stream_index == audio_index)
{
int ret = avcodec_send_packet(aCC, pkt);
if(ret < 0)
{
cout << "send packet for decoding audio failed" << endl;
return -1;
}
ret = avcodec_receive_frame(aCC, frame);
if(ret < 0)
{
cout << "error during decoding audio : " << ret << endl;
return -1;
}
int64_t in_channel_layout = av_get_default_channel_layout(aCC->channels);
SwrContext *swr1 = swr_alloc();
swr1 = swr_alloc_set_opts(swr1, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16,
44100, in_channel_layout, aCC->sample_fmt,
aCC->sample_rate, 0, NULL);
swr_init(swr1);
swr_convert(swr1, &audio_buffer, 192000,
(const uint8_t**)frame->data, frame->nb_samples);
fwrite(audio_buffer, 1, audio_bufferSize, pcm_file);
cout << "1 audio frame ok" << endl;
}
av_packet_unref(pkt);
}
fclose(yuv_file);
fclose(pcm_file);
av_free(audio_buffer);
av_free(yuv_buffer);
av_frame_free(&yuv);
av_frame_free(&frame);
avcodec_free_context(&vCC);
avcodec_free_context(&aCC);
sws_freeContext(sws);
swr_free(&swr);
avformat_close_input(&ic);
return 0;
}
不足之处请多多指教