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

简介: 对于FFMPEG SDK  提供的Demuxing 为我们实现多路复用  提供了很多方便,下面的案案例 实现的是 分离一个媒体文件的音频 视频流 并且解码输出 到  不同的文件中。 对于音频被还原回了 PCM格式  对于视频 被还原成了 YUV420等原生 格式 注意我用的FFMPEG SDK是最新版   API接口稍有改变。

对于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文件 被分离开了。。。



目录
相关文章
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
1568 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
编解码 语音技术 内存技术
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
《FFmpeg开发实战:从零基础到短视频上线》一书中的“5.1.2 把音频流保存为PCM文件”章节介绍了将媒体文件中的音频流转换为原始PCM音频的方法。示例代码直接保存解码后的PCM数据,保留了原始音频的采样频率、声道数量和采样位数。但在实际应用中,有时需要特定规格的PCM音频。例如,某些语音识别引擎仅接受16位PCM数据,而标准MP3音频通常采用32位采样,因此需将32位MP3音频转换为16位PCM音频。
475 0
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
|
XML Java Android开发
FFmpeg开发笔记(五十二)移动端的国产视频播放器GSYVideoPlayer
GSYVideoPlayer是一款国产移动端视频播放器,支持弹幕、滤镜、广告等功能,采用IJKPlayer、Media3(EXOPlayer)、MediaPlayer及AliPlayer多种内核。截至2024年8月,其GitHub星标数达2万。集成时需使用新版Android Studio,并按特定步骤配置依赖与权限。提供了NormalGSYVideoPlayer、GSYADVideoPlayer及ListGSYVideoPlayer三种控件,支持HLS、RTMP等多种直播链接。
596 18
FFmpeg开发笔记(五十二)移动端的国产视频播放器GSYVideoPlayer
|
XML 开发工具 Android开发
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
ExoPlayer最初是为了解决Android早期MediaPlayer控件对网络视频兼容性差的问题而推出的。现在,Android官方已将其升级并纳入Jetpack的Media3库,使其成为音视频操作的统一引擎。新版ExoPlayer支持多种协议,解决了设备和系统碎片化问题,可在整个Android生态中一致运行。通过修改`build.gradle`文件、布局文件及Activity代码,并添加必要的权限,即可集成并使用ExoPlayer进行网络视频播放。具体步骤包括引入依赖库、配置播放界面、编写播放逻辑以及添加互联网访问权限。
1341 1
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
317 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
Linux 开发工具 Android开发
FFmpeg开发笔记(五十三)移动端的国产直播录制工具EasyPusher
EasyPusher是一款国产RTSP直播录制推流客户端工具,支持Windows、Linux、Android及iOS等系统。尽管其GitHub仓库(安卓版:https://github.com/EasyDarwin/EasyPusher-Android)已多年未更新,但通过一系列改造,如升级SDK版本、迁移到AndroidX、指定本地NDK版本及更新Gradle版本等,仍可在最新Android Studio上运行。以下是针对Android Studio Dolphin版本的具体改造步骤。
293 3
FFmpeg开发笔记(五十三)移动端的国产直播录制工具EasyPusher
|
编解码 移动开发 安全
FFmpeg开发笔记(五十)聊聊几种流媒体传输技术的前世今生
自互联网普及以来,流媒体技术特别是视频直播技术不断进步,出现了多种传输协议。早期的MMS由微软主导,但随WMV格式衰落而减少使用。RTSP由网景和RealNetworks联合提出,支持多种格式,但在某些现代应用中不再受支持。RTMP由Adobe开发,曾广泛用于网络直播,但因HTML5不支持Flash而受影响。HLS由苹果开发,基于HTTP,适用于点播。SRT和RIST均为较新协议,强调安全与可靠性,尤其SRT在电视直播中应用增多。尽管RTMP仍占一定市场,但SRT等新协议正逐渐兴起。
567 8
FFmpeg开发笔记(五十)聊聊几种流媒体传输技术的前世今生
|
Web App开发 Java 视频直播
FFmpeg开发笔记(四十九)助您在毕业设计中脱颖而出的几个流行APP
对于软件、计算机等专业的毕业生,毕业设计需实现实用软件或APP。新颖的设计应结合最新技术,如5G时代的音视频技术。示例包括: 1. **短视频分享APP**: 集成FFmpeg实现视频剪辑功能,如添加字幕、转场特效等。 2. **电商购物APP**: 具备直播带货功能,使用RTMP/SRT协议支持流畅直播体验。 3. **同城生活APP**: 引入WebRTC技术实现可信的视频通话功能。这些应用不仅实用,还能展示开发者紧跟技术潮流的能力。
275 4
FFmpeg开发笔记(四十九)助您在毕业设计中脱颖而出的几个流行APP
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
610 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库

热门文章

最新文章