FFMPEG SDK流媒体开发2---分离.mp4等输入流音视频并且进行解码输出

对于FFMPEG SDK  提供的Demuxing 为我们实现多路复用  提供了很多方便,下面的案案例 实现的是 分离一个媒体文件的音频 视频流 并且解码输出 到  不同的文件中。

对于音频被还原回了 PCM格式  对于视频 被还原成了 YUV420等原生 格式

注意我用的FFMPEG SDK是最新版   API接口稍有改变。

每天更新 博客 记录自己学习的点点滴滴,写完了 上班去

#include "stdafx.h"
/************************************************************************/
/* 利用分流器分流MP4文件音视频并进行解码输出
Programmer小卫-USher 2014/12/17
/************************************************************************/
//打开
#define __STDC_FORMAT_MACROS
#ifdef _CPPRTTI
extern "C"
{
#endif
#include "libavutil/imgutils.h"    //图像工具
#include "libavutil/samplefmt.h"  // 音频样本格式
#include "libavutil/timestamp.h"  //时间戳工具可以 被用于调试和日志目的
#include "libavformat/avformat.h" //Main libavformat public API header  包含了libavf I/O和   Demuxing  和Muxing 库
#ifdef _CPPRTTI
};
#endif

//音视频编码器上下文
static AVCodecContext *pVideoContext,*pAudioContext;
static FILE *fVideoFile,*fAudioFile;  //输出文件句柄
static AVStream *pStreamVideo,*pStreamAudio; //媒体流
static unsigned char * videoDstData[4];  //视频数据
static int videoLineSize[4]; //
static int videoBufferSize; //视频缓冲区大小
static AVFormatContext *pFormatCtx=NULL; //格式上下文
static AVFrame*pFrame=NULL ; //
static AVPacket pkt;  //解码媒体包
static int ret=0; //状态
static int gotFrame; //获取到的视频流
//音视频流的索引
static int videoStreamIndex,audioStreamIndex;
//解码媒体包
int indexFrameVideo=0;
static int decode_packet(int* gotFrame, int param2)
{
	int ret  = 0 ;
	//解码数据大小
	int decodedSize=pkt.size ;
	//初始化获取的数据帧为0
	*gotFrame=0;
	//如果是视频流那么 解包视频流
	if(pkt.stream_index==videoStreamIndex)
	{
		if((ret=avcodec_decode_video2(pVideoContext,pFrame,gotFrame,&pkt))<0)
		{
			//解码视频帧失败
			return ret ;
		}
		indexFrameVideo++;

		//copy 解压后的数据到我们分配的空间中
		if(*gotFrame)
		{
			av_image_copy(videoDstData,videoLineSize, (const uint8_t **)(pFrame->data), pFrame->linesize,pVideoContext->pix_fmt, pVideoContext->width, pVideoContext->height);
			//写入数据到缓冲区
			fwrite(videoDstData[0], 1, videoBufferSize, fVideoFile);
			printf("输出当前第%d帧,大小:%d\n",indexFrameVideo,videoBufferSize);
		}else
		{
			printf("第%d帧,丢失\n",indexFrameVideo);
		}
	}
	//音频流解包
	else if(pkt.stream_index==audioStreamIndex)
	{
		//解码音频信息
		if((ret=avcodec_decode_audio4(pAudioContext,pFrame,gotFrame,&pkt))<0)
			return ret ;
		decodedSize = FFMIN(ret, pkt.size);
		//算出当前帧的大小
		size_t unpadded_linesize = pFrame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)pFrame->format);
		///写入数据到音频文件
		fwrite(pFrame->extended_data[0], 1, unpadded_linesize, fAudioFile);
	}
	//取消所有引用  并且重置frame字段
	av_frame_unref(pFrame);
	return decodedSize ;
}

///根据样本格式 提示样本信息
static int get_format_from_sample_fmt(const char **fmt,
	enum AVSampleFormat sample_fmt)
{
	int i;
	struct sample_fmt_entry
	{
		enum AVSampleFormat sample_fmt;
		const char *fmt_be, *fmt_le;
	} sample_fmt_entries[] =
	{
		{ AV_SAMPLE_FMT_U8, "u8", "u8" },
		{ AV_SAMPLE_FMT_S16, "s16be", "s16le" },
		{ AV_SAMPLE_FMT_S32, "s32be", "s32le" },
		{ AV_SAMPLE_FMT_FLT, "f32be", "f32le" },
		{ AV_SAMPLE_FMT_DBL, "f64be", "f64le" },
	};
	*fmt = NULL;
	for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++)
	{
		struct sample_fmt_entry *entry = &sample_fmt_entries[i];
		if (sample_fmt == entry->sample_fmt) {
			*fmt = AV_NE(entry->fmt_be, entry->fmt_le);
			return 0;
		}
	}
	fprintf(stderr,"sample format %s is not supported as output format\n",av_get_sample_fmt_name(sample_fmt));
	return -1;
}
int _tmain(int argc,char*argv[])
{
	if(argc<4)
	{
		printf("Parameter Error!\n");
		return 0;
	}

	//注册所有混流器 过滤器
	av_register_all();
	//注册所有编码器
	avcodec_register_all();
	//媒体输入源头
	char*pInputFile=argv[1];
	//视频输出文件
	char*pOutputVideoFile=argv[2];
	//音频输出文件
	char*pOutputAudioFile=argv[3];
	//分配环境上下文
	pFormatCtx=avformat_alloc_context() ;
	//打开输入源  并且读取输入源的头部
	if(avformat_open_input(&pFormatCtx,pInputFile,NULL,NULL)<0)
	{
		printf("Open Input Error!\n");
		return 0 ;
	}
	//获取流媒体信息
	if(avformat_find_stream_info(pFormatCtx,NULL)<0)
	{
		printf("获取流媒体信息失败!\n");
		return 0;
	}
	//打印媒体信息
	av_dump_format(pFormatCtx,0,pInputFile,0);
	for(unsigned i=0;i<pFormatCtx->nb_streams;i++)
	{
		AVStream *pStream=pFormatCtx->streams[i];
		AVMediaType mediaType=pStream->codec->codec_type;
		//提取不同的编解码器
		if(mediaType==AVMEDIA_TYPE_VIDEO)
		{
			videoStreamIndex=i ;
			pVideoContext=pStream->codec;
			pStreamVideo=pStream;
			fVideoFile=fopen(pOutputVideoFile,"wb");
			if(!fVideoFile)
			{
				printf("con't open file!\n");
				goto end;
			}

			int ret=av_image_alloc(videoDstData,videoLineSize,pVideoContext->width,pVideoContext->height,pVideoContext->pix_fmt,1);
			if(ret<0)
			{
				printf("Alloc video buffer error!\n");
				goto end ;
			}
			videoBufferSize=ret ;
		}
		else if(mediaType==AVMEDIA_TYPE_AUDIO)
		{
			audioStreamIndex=i;
			pAudioContext=pStream->codec ;
			pStreamAudio=pStream;
			fAudioFile=fopen(pOutputAudioFile,"wb");
			if(!fAudioFile)
			{
				printf("con't open file!\n");
				goto end;
			}
			//分配视频帧
			pFrame=av_frame_alloc();
			if(pFrame==NULL)
			{
				av_freep(&videoDstData[0]);
				printf("alloc audio frame error\n");
				goto end ;
			}
		}
		AVCodec *dec;
		//根据编码器id查找编码器
		dec=avcodec_find_decoder(pStream->codec->codec_id);
		if(dec==NULL)
		{
			printf("查找编码器失败!\n");
			goto end;
		}
		if(avcodec_open2(pStream->codec, dec, nullptr)!=0)
		{
			printf("打开编码器失败!\n");
			goto end;
		}

	}
	av_init_packet(&pkt);
	pkt.data=NULL;
	pkt.size=0;

	//读取媒体数据包  数据要大于等于0
	while(av_read_frame(pFormatCtx,&pkt)>=0)
	{
		AVPacket oriPkt=pkt ;
		do
		{
			//返回每个包解码的数据
			ret=decode_packet(&gotFrame,0);
			if(ret<0)
				break;
			//指针后移  空闲内存减少
			pkt.data+=ret ;
			pkt.size-=ret ;
			//
		}while(pkt.size>0);
		//释放之前分配的空间  读取完毕必须释放包
		av_free_packet(&oriPkt);
	}

end:
	//关闭视频编码器
	avcodec_close(pVideoContext);
	//关闭音频编码器
	avcodec_close(pAudioContext);
	avformat_close_input(&pFormatCtx);
	fclose(fVideoFile);
	fclose(fAudioFile);
	//释放编码帧
	avcodec_free_frame(&pFrame);
	//释放视频数据区
	av_free(videoDstData[0]);
	return  0;
}
程序运行如下图所示



我们发现 MP4文件 被分离开了。。。

时间: 2024-10-15 02:55:07

FFMPEG SDK流媒体开发2---分离.mp4等输入流音视频并且进行解码输出的相关文章

基于FFMPEG SDK流媒体开发1---解码媒体文件流信息

最近项目涉及到流媒体等开发,由于有过开发经验深知其难度所在,没办法只能重新拾起,最新版的SDK被改的一塌糊涂,不过大体的开发思路都是一样的,看多少书查多少资料都无用,一步一步的编写代码 才是学好的关键.. 我会把每一天的学习经过,更新到博文上,希望能给更多想学习的人带来帮助,篇尾附上工程     以及最新版本SDK. FFMPEG被大多数的人命令行来使用,其实在真正的流媒体开发中,要想灵活运用其开发流媒体应用层序,必须使用官方SDK开发  ,实际上我们市面上好多产品 都是基于FFMPEG,比如

FFMPEG+SDL2.0流媒体开发3---简易MP4视频播放器,提取MP4的H264视频序列解码并且显示

简介 之前写了一遍提取MP4中的音视频并且解码,这一篇引入SDL2.0来显示解码后的视频序列 实现一个简易的 视频播放器. 我这里用的FFMPEG和SDL2.0都是最新版的 可能网上的资料不是很多,API接口也变了很多,不过大体的思路还是一样的. 分析几个FFMPEG函数 在这之前我们分析几个代码中可能引起疑问的FFMPEG几个函数的源代码,我已经尽我的能力添加了注释,因为实在没有文档可能有的地方也不是很详尽  不过大体还是能看懂的 av_image_alloc (分配图片缓冲区) 我们在FFM

【FFMpeg视频开发与应用基础】六、调用FFMpeg SDK实现视频文件的转封装

<FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK>视频教程已经在"CSDN学院"上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK 工程代码地址:FFmpeg_Tutorial 有时候我们可能会面对这样的一种需求,即我们不需要对视频内的音频或视频信号进行什么实际的操作,只是希望能把文件的封装格式进行转换,例如从avi转换为mp4格式或者flv格式等.实际上,转封

FFMPEG SDK 开发介绍

FFMPEG SDK 开发介绍 1.简介:     ffmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序. 使用ffmpeg能够完成如下功能:parse,demux,decode,filter(preprocessing),encode,mux,stream和player等. 2.下载和编译:     下载地址:  http://ffmpeg.org/download.html     编译:        1)windows平台static library/sh

【FFMpeg视频开发与应用基础】五、调用FFMpeg SDK封装音频和视频为视频文件

<FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK>视频教程已经在"CSDN学院"上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK 工程代码地址:FFmpeg_Tutorial 音频和视频的封装过程为解封装的逆过程,即将独立的音频数据和视频数据按照容器文件所规定的格式封装为一个完整的视频文件的过程.对于大多数消费者来说,视频封装的容器是大家最为熟悉的,因为它直接

FFMPEG SDK开发介绍

原文地址:FFMPEG SDK 开发介绍(原创)作者:阿东博客 本文是作者在使用ffmpeg sdk开发过程中的实际经验,现在与大家分享,欢迎学习交流. FFMPEG SDK 开发介绍 1.简介:     ffmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序. 使用ffmpeg能够完成如下功能:parse,demux,decode,filter(preprocessing),encode,mux,stream和player等. 2.下载和编译:     下载地址:

【FFMpeg视频开发与应用基础】二、调用FFmpeg SDK对YUV视频序列进行编码

<FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK>视频教程已经在"CSDN学院"上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK 工程代码地址:FFmpeg_Tutorial 视频由像素格式编码为码流格式是FFMpeg的一项基本功能.通常,视频编码器的输入视频通常为原始的图像像素值,输出格式为符合某种格式规定的二进制码流. 1.FFMpeg进行视频编码所需要的

【FFMpeg视频开发与应用基础】三、调用FFmpeg SDK对H.264格式的视频压缩码流进行解码

<FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK>视频教程已经在"CSDN学院"上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK 工程代码地址:FFmpeg_Tutorial 经过了上篇调用FFMpeg SDK对视频进行编码的过程之后,我们可以比较容易地理解本篇的内容,即上一篇的逆过程--将H.264格式的裸码流解码为像素格式的图像信息. 1.FFMpeg视频

【FFMpeg视频开发与应用基础】八、 调用FFMpeg SDK实现视频缩放

<FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK>视频教程已经在"CSDN学院"上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK Github工程代码地址:FFmpeg_Tutorial 视频缩放是视频开发中一项最基本的功能.通过对视频的像素数据进行采样或插值,可以将低分辨率的视频转换到更高的分辨率,或者将高分辨率的视频转换为更低的分辨率.通过FFMpeg提供