09-流媒体-FLV解复用

整体方案:

采集端:摄像头采集(YUV)->编码(YUV转H264)->写封装(H264转FLV)->RTMP推流

客户端:RTMP拉流->解封装(FLV转H264)->解码(H264转YUV)->YUV显示(SDL2)

cpp 复制代码
#include <stdio.h>
 
#define __STDC_CONSTANT_MACROS
 
 
extern "C"
{
#include "libavformat/avformat.h"
};
 
 
 
//封装格式MKV/MP4/FLV中如果有AAC的情况,首先这些封装格式中包含AudioSpecificConfig,
//保存在音频流的AVCodecContext->extradata 里面,需要解析然后封装ADTS即可
 
 
 
#define DEMUXER_AAC 1
#define DEMUXER_MP3 0
 
#define  ADTS_HEADER_SIZE (7)
 
//FLV封装音视频 AAC封装在第一个AAC TAG会封装一个AudioSpecificConfig结构 
//AudioSpecificConfig解析结果保存在该结构体中
typedef struct  
{
	int write_adts;  
	int objecttype;  
	int sample_rate_index;  
	int channel_conf;  
 
}ADTSContext;  
 
 
//解析AudioSpecificConfig
int aac_decode_extradata(ADTSContext *adts, unsigned char *pbuf, int bufsize)  
{  
	int aot, aotext, samfreindex;  
	int i, channelconfig;  
	unsigned char *p = pbuf;  
	if (!adts || !pbuf || bufsize<2)  
	{  
		return -1;  
	}  
	aot = (p[0]>>3)&0x1f;  
	if (aot == 31)  
	{  
		aotext = (p[0]<<3 | (p[1]>>5)) & 0x3f;  
		aot = 32 + aotext;  
		samfreindex = (p[1]>>1) & 0x0f;   
		if (samfreindex == 0x0f)  
		{  
			channelconfig = ((p[4]<<3) | (p[5]>>5)) & 0x0f;  
		}  
		else  
		{  
			channelconfig = ((p[1]<<3)|(p[2]>>5)) & 0x0f;  
		}  
	}  
	else  
	{  
		samfreindex = ((p[0]<<1)|p[1]>>7) & 0x0f;  
		if (samfreindex == 0x0f)  
		{  
			channelconfig = (p[4]>>3) & 0x0f;  
		}  
		else  
		{  
			channelconfig = (p[1]>>3) & 0x0f;  
		}  
	}  
#ifdef AOT_PROFILE_CTRL  
	if (aot < 2) aot = 2;  
#endif  
	adts->objecttype = aot-1;  
	adts->sample_rate_index = samfreindex;  
	adts->channel_conf = channelconfig;  
	adts->write_adts = 1;  
	return 0;  
}  
 
//添加ADTS头
int aac_set_adts_head(ADTSContext *acfg, unsigned char *buf, int size)  
{         
	unsigned char byte;    
	if (size < ADTS_HEADER_SIZE)  
	{  
		return -1;  
	}       
	buf[0] = 0xff;  
	buf[1] = 0xf1;  
	byte = 0;  
	byte |= (acfg->objecttype & 0x03) << 6;  
	byte |= (acfg->sample_rate_index & 0x0f) << 2;  
	byte |= (acfg->channel_conf & 0x07) >> 2;  
	buf[2] = byte;  
	byte = 0;  
	byte |= (acfg->channel_conf & 0x07) << 6;  
	byte |= (ADTS_HEADER_SIZE + size) >> 11;  
	buf[3] = byte;  
	byte = 0;  
	byte |= (ADTS_HEADER_SIZE + size) >> 3;  
	buf[4] = byte;  
	byte = 0;  
	byte |= ((ADTS_HEADER_SIZE + size) & 0x7) << 5;  
	byte |= (0x7ff >> 6) & 0x1f;  
	buf[5] = byte;  
	byte = 0;  
	byte |= (0x7ff & 0x3f) << 2;  
	buf[6] = byte;     
	return 0;  
}  
 
 
 
 
 
 
int main(int argc, char* argv[])
{
 
	AVFormatContext *ifmt_ctx = NULL;
	AVPacket pkt;
	int ret, i;
	int videoindex=-1,audioindex=-1;
	#if DEMUXER_AAC
	const char *in_filename    = "demo.flv";		//Input file URL
	const char *out_filename_v = "demo.h264";	//Output file URL
	const char *out_filename_a = "demo.aac";
	#endif
 
	#if DEMUXER_MP3
	const char *in_filename    = "demo.flv";		//Input file URL
	const char *out_filename_v = "demo.h264";	//Output file URL
	const char *out_filename_a = "demo.mp3";
	#endif
 
 
 
 
	av_register_all();
	//Input
	if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) 
	{
		printf( "Could not open input file.");
		return -1;
	}
	if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) 
	{
		printf( "Failed to retrieve input stream information");
		return -1;
	}
 
	videoindex=-1;
	for(i=0; i<ifmt_ctx->nb_streams; i++) 
	{
		if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
		{
			videoindex=i;
		}else if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
		{
			audioindex=i;
		}
	}
 
	FILE *fp_audio=fopen(out_filename_a,"wb+");  
	FILE *fp_video=fopen(out_filename_v,"wb+");  
 
	//FLV/MP4/MKV等结构中,h264需要h264_mp4toannexb处理。添加SPS/PPS等信息。FLV封装时,可以把
	//多个NALU放在一个VIDEO TAG中,结构为4B NALU长度+NALU1+4B NALU长度+NALU2+...,需要做的处理把4B
	//长度换成00000001或者000001
 
	AVBitStreamFilterContext* h264bsfc =  av_bitstream_filter_init("h264_mp4toannexb"); 
 
#if DEMUXER_AAC
	ADTSContext stADTSContext;
	unsigned char pAdtsHead[7];
#endif
 
	while(av_read_frame(ifmt_ctx, &pkt)>=0)
	{
		if(pkt.stream_index==videoindex)
		{
 
			av_bitstream_filter_filter(h264bsfc, ifmt_ctx->streams[videoindex]->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
 
			printf("Write Video Packet. size:%d\tpts:%lld\n",pkt.size,pkt.pts);
			fwrite(pkt.data,1,pkt.size,fp_video);
		}
		else if(pkt.stream_index==audioindex)
		{
			//AAC在封装结构MKV/FLV/MP4结构中,需要手动添加ADTS
	#if DEMUXER_AAC
			aac_decode_extradata(&stADTSContext, ifmt_ctx->streams[audioindex]->codec->extradata, ifmt_ctx->streams[audioindex]->codec->extradata_size);
			aac_set_adts_head(&stADTSContext, pAdtsHead, pkt.size);
			fwrite(pAdtsHead, 1, 7, fp_audio);
	#endif
			//一般结构如MP3直接写文件即可
			
			printf("Write Audio Packet. size:%d\tpts:%lld\n",pkt.size,pkt.pts);
			fwrite(pkt.data,1,pkt.size,fp_audio);
		}
		av_free_packet(&pkt);
	}
 
 
	av_bitstream_filter_close(h264bsfc);  
 
 
	fclose(fp_video);
	fclose(fp_audio);
 
	avformat_close_input(&ifmt_ctx);
 
	if (ret < 0 && ret != AVERROR_EOF) 
	{
		printf( "Error occurred.\n");
		return -1;
	}
	return 0;
}
相关推荐
浮华落定6 小时前
DeepSeek+即梦 做AI视频
人工智能·chatgpt·音视频
Black蜡笔小新11 小时前
从中心化到点对点:视频通话SDK组件EasyRTC如何通过WebP2P技术实现低延迟通信
网络协议·音视频·p2p
清月电子16 小时前
BT401双模音频蓝牙模块如何开启ble的透传,有什么注意事项
单片机·嵌入式硬件·物联网·音视频
深圳市青牛科技实业有限公司16 小时前
芯麦 GC1808:高性能、低成本的立体声音频模数转换器
人工智能·嵌入式硬件·算法·音视频·能源·新能源·电动工具
cuijiecheng201816 小时前
音视频入门基础:RTP专题(9)——FFmpeg接收RTP流的原理和内部实现
ffmpeg·音视频
京河小蚁17 小时前
微信云开发小程序音频播放踩坑记录 - 从熄屏播放到iOS静音
微信·小程序·音视频
9527华安18 小时前
FPGA实现SDI视频解码转GTY光口传输,基于GS2971+Aurora 8b/10b编解码架构,提供工程源码和技术支持
fpga开发·架构·音视频·8b/10b·sdi·gty·gs2971
shawn·xiao18 小时前
Android:播放Rtsp视频流的两种方式
android·音视频·视频
忘不了情19 小时前
前端如何播放二进制音频数据
javascript·react.js·音视频
lucky-billy1 天前
Qt 中使用 ffmpeg 获取采集卡数据录制视频
qt·ffmpeg·音视频