【前言】
一般读取视音频文件解码时,我们会使用AVFormatContext。
以下不用AVFormatContext,而是使用 AVCodecContext 和 AVCodecParserContext
【对比】
简单记录一下这个只使用libavcodec的“纯净版”视频解码器和使用libavcodec+libavformat的视频解码器的不同。
(1)下列与libavformat相关的函数在“纯净版”视频解码器中都不存在。
av_register_all():注册所有的编解码器,复用/解复用器等等组件。其中调用了avcodec_register_all()注册所有编解码器相关的组件。
avformat_alloc_context():创建AVFormatContext结构体。
avformat_open_input():打开一个输入流(文件或者网络地址)。其中会调用avformat_new_stream()创建AVStream结构体。avformat_new_stream()中会调用avcodec_alloc_context3()创建AVCodecContext结构体。
avformat_find_stream_info():获取媒体的信息。
av_read_frame():获取媒体的一帧压缩编码数据。其中调用了av_parser_parse2()。
(2)新增了如下几个函数。
avcodec_register_all():只注册编解码器有关的组件。比如说编码器、解码器、比特流滤镜等,但是不注册复用/解复用器这些和编解码器无关的组件。
avcodec_alloc_context3():创建AVCodecContext结构体。
av_parser_init():初始化AVCodecParserContext结构体。
av_parser_parse2():使用AVCodecParser从输入的数据流中分离出一帧一帧的压缩编码数据。
(3)程序的流程发生了变化。
在“libavcodec+libavformat”的视频解码器中,使用avformat_open_input()和avformat_find_stream_info()就可以解析出输入视频的信息(例如视频的宽、高)并且赋值给相关的结构体。因此我们在初始化的时候就可以通过读取相应的字段获取到这些信息。
在“纯净”的解码器则不能这样,由于没有上述的函数,所以不能在初始化的时候获得视频的参数。“纯净”的解码器中,可以通过avcodec_decode_video2()获得这些信息。因此我们只有在成功解码第一帧之后,才能通过读取相应的字段获取到这些信息。
【代码】默认音频是AV_SAMPLE_FMT_FLT类型
解码AAC音频
AVCodecContext *pCodecCtx = NULL;
AVCodecParserContext *pCodecParserCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = NULL;
AVPacket packet;
AVCodecID codec_id = AV_CODEC_ID_AAC;
int ret;
av_register_all();
avcodec_register_all();
/* 初始化AVCodec */
pCodec = avcodec_find_decoder(codec_id);
/* 初始化AVCodecContext,只是分配,还没打开 */
pCodecCtx = avcodec_alloc_context3(pCodec);
/* 初始化AVCodecParserContext */
pCodecParserCtx = av_parser_init(AV_CODEC_ID_AAC);
if (!pCodecParserCtx)
{
qDebug() << "AVCodecParseContext error";
::exit(0);
}
/* we do not send complete frames,什么意思? */
if (pCodec->capabilities & CODEC_CAP_TRUNCATED)
pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;
/* 打开解码器 */
ret = avcodec_open2(pCodecCtx, pCodec, NULL);
if (ret < 0)
{
qDebug() << "avocodec_open2 error";
::exit(0);
}
pFrame = av_frame_alloc();
av_init_packet(&packet);
packet.size = 0;
packet.data = NULL;
/* 存储一帧可以PCM,L(一个采样点)RLRLR..... ,用于播放 */
int out_buf_size;
char* out_buf = NULL;
//FILE *fp = fopen("audio.pcm", "wb");
const int in_buffer_size = 4096;
/**
* AVPacket.buf.data 指向AVPacket.data ,AVPacket.buf.size = AVPacket.size + FF_INPUT_BUFFER_PADDING_SIZE
*/
uint8_t in_buffer[in_buffer_size + FF_INPUT_BUFFER_PADDING_SIZE] = { 0 };
uint8_t *cur_ptr;
int cur_size;
int got;
bool is_first_time = true;
while (1)
{
cur_size = recv(m_socket, (char *)in_buffer, in_buffer_size, 0);
//if (cur_size) continue;
if (cur_size == 0)
break;
cur_ptr = in_buffer;
while (cur_size > 0)
{
/* 返回解析了的字节数 */
int len = av_parser_parse2(pCodecParserCtx, pCodecCtx,
&packet.data, &packet.size, cur_ptr, cur_size,
AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);
cur_ptr += len;
cur_size -= len;
if (packet.size == 0)
continue;
ret = avcodec_decode_audio4(pCodecCtx, pFrame, &got, &packet);
if (ret < 0)
{
qDebug() << "decodec error";
::exit(0);
}
if (got)
{
if (is_first_time) //分配格式转换存储空间
{
out_buf_size = av_samples_get_buffer_size(
NULL,
pCodecCtx->channels,
pFrame->nb_samples, //读取一帧数据时每个声道读取的采样个数
pCodecCtx->sample_fmt,
1);
out_buf = (char *)malloc(out_buf_size);
if (out_buf == NULL)
{
qDebug() << "malloc out_buf error";
::exit(0);
}
is_first_time = false;
}
uint32_t *l = (uint32_t *)pFrame->extended_data[0];
uint32_t *r = (uint32_t *)pFrame->extended_data[1];
//这里是针对AV_SAMPLE_FMT_FLTP格式的写入方式,其他音频格式的需要其他方式
for (int i = 0, j = 0; i < out_buf_size; i += 8, j++)
{
out_buf[i ] = (char)(r[j] & 0xff);
out_buf[i + 1] = (char)(r[j] >> 8 & 0xff);
out_buf[i + 2] = (char)(r[j] >> 16 & 0xff);
out_buf[i + 3] = (char)(r[j] >> 24 & 0xff);
out_buf[i + 4] = (char)(l[j] & 0xff);
out_buf[i + 5] = (char)(l[j] >> 8 & 0xff);
out_buf[i + 6] = (char)(l[j] >> 16 & 0xff);
out_buf[i + 7] = (char)(l[j] >> 24 & 0xff);
}
std::string str(out_buf, out_buf_size);
emit this->signal_receive_one_audio_frame(str);
//fwrite(out_buf, out_buf_size, 1, fp);
}
}
}
av_free_packet(&packet);
av_frame_free(&pFrame);
avcodec_free_context(&pCodecCtx);
av_parser_close(pCodecParserCtx);
解码H264网络流
AVCodecContext *pCodecCtx = NULL;
AVCodecParserContext *pCodecParserCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = NULL; //yuv
AVPacket packet; //h264
AVPicture picture; //储存rgb格式图片
SwsContext *pSwsCtx = NULL;
AVCodecID codec_id = AV_CODEC_ID_H264;
int ret;
av_register_all();
avcodec_register_all();
/* 初始化AVCodec */
pCodec = avcodec_find_decoder(codec_id);
/* 初始化AVCodecContext,只是分配,还没打开 */
pCodecCtx = avcodec_alloc_context3(pCodec);
/* 初始化AVCodecParserContext */
pCodecParserCtx = av_parser_init(AV_CODEC_ID_H264);
if (!pCodecParserCtx)
{
qDebug() << "AVCodecParseContext error";
::exit(0);
}
/* we do not send complete frames,什么意思? */
if (pCodec->capabilities & CODEC_CAP_TRUNCATED)
pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;
/* 打开解码器 */
ret = avcodec_open2(pCodecCtx, pCodec, NULL);
if (ret < 0)
{
qDebug() << "avocodec_open2 error";
::exit(0);
}
pFrame = av_frame_alloc();
av_init_packet(&packet);
packet.size = 0;
packet.data = NULL;
const int in_buffer_size = 4096;
uint8_t in_buffer[in_buffer_size + FF_INPUT_BUFFER_PADDING_SIZE] = { 0 };
uint8_t *cur_ptr;
int cur_size;
int got;
bool is_first_time = true;
while (1)
{
cur_size = recv(m_socket, (char *)in_buffer, in_buffer_size, 0);
if (cur_size == 0)
break;
cur_ptr = in_buffer;
while (cur_size > 0)
{
/* 返回解析了的字节数 */
int len = av_parser_parse2(pCodecParserCtx, pCodecCtx,
&packet.data, &packet.size, cur_ptr, cur_size,
AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);
cur_ptr += len;
cur_size -= len;
if (packet.size == 0)
continue;
//switch (pCodecParserCtx->pict_type)
//{
// case AV_PICTURE_TYPE_I: printf("Type: I\t"); break;
// case AV_PICTURE_TYPE_P: printf("Type: P\t"); break;
// case AV_PICTURE_TYPE_B: printf("Type: B\t"); break;
// default: printf("Type: Other\t"); break;
//}
//printf("Output Number:%4d\t", pCodecParserCtx->output_picture_number);
//printf("Offset:%8ld\n", pCodecParserCtx->cur_offset);
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got, &packet);
if (ret < 0)
{
qDebug() << "decodec error";
::exit(0);
}
if (got)
{
if (is_first_time) //分配格式转换存储空间
{
pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
avpicture_alloc(&picture, AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);
is_first_time = false;
}
/* YUV转RGB */
sws_scale(pSwsCtx, pFrame->data, pFrame->linesize,
0, pCodecCtx->height,
picture.data, picture.linesize);
QImage img(picture.data[0], pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB32);
emit this->signal_receive_one_image(img);
}
}
}
av_free_packet(&packet);
av_frame_free(&pFrame);
avpicture_free(&picture);
sws_freeContext(pSwsCtx);
avcodec_free_context(&pCodecCtx);
av_parser_close(pCodecParserCtx);