【FFmpeg(2016)】AVCodecContext解码网络流

10 篇文章 1 订阅
6 篇文章 0 订阅

【前言】

一般读取视音频文件解码时,我们会使用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);


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是使用FFmpeg API实现视频解码的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sys/time.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio // 视频解码线程 void *decode_video_thread(void *arg) { AVFormatContext *pFormatCtx = NULL; AVCodecContext *pCodecCtx = NULL; AVCodec *pCodec = NULL; AVFrame *pFrame = NULL; AVPacket packet; int videoStream = -1; int frameFinished = 0; struct SwsContext *img_convert_ctx = NULL; // 打开视频 if (avformat_open_input(&pFormatCtx, "rtsp://xxx.xxx.xxx.xxx:xxxx/xxx", NULL, NULL) != 0) { printf("Couldn't open input stream.\n"); return NULL; } // 查找视频信息 if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { printf("Couldn't find stream information.\n"); return NULL; } // 查找视频 for (int i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStream = i; break; } } if (videoStream == -1) { printf("Couldn't find a video stream.\n"); return NULL; } // 获取视频解码器 pCodec = avcodec_find_decoder(pFormatCtx->streams[videoStream]->codecpar->codec_id); if (pCodec == NULL) { printf("Codec not found.\n"); return NULL; } // 初始化解码器上下文 pCodecCtx = avcodec_alloc_context3(pCodec); if (avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar) < 0) { printf("Couldn't copy codec context.\n"); return NULL; } // 打开解码器 if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { printf("Couldn't open codec.\n"); return NULL; } // 分配视频帧内存 pFrame = av_frame_alloc(); if (pFrame == NULL) { printf("Couldn't allocate video frame.\n"); return NULL; } // 初始化图像转换上下文 img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL); if (img_convert_ctx == NULL) { printf("Couldn't initialize sws context.\n"); return NULL; } // 读取视频 while (av_read_frame(pFormatCtx, &packet) >= 0) { if (packet.stream_index == videoStream) { // 解码视频帧 avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); if (frameFinished) { // 转换图像格式 uint8_t *out_buffer = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1)); av_image_fill_arrays(pFrame->data, pFrame->linesize, out_buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1); sws_scale(img_convert_ctx, (const uint8_t *const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrame->data, pFrame->linesize); // 处理图像数据 // ... av_free(out_buffer); } } av_packet_unref(&packet); } // 释放资源 sws_freeContext(img_convert_ctx); av_frame_free(&pFrame); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); return NULL; } // 主函数 int main(int argc, char *argv[]) { // 初始化FFmpeg库 av_register_all(); avformat_network_init(); // 创建视频解码线程 pthread_t tid; pthread_create(&tid, NULL, decode_video_thread, NULL); // 等待线程结束 pthread_join(tid, NULL); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值