zoukankan      html  css  js  c++  java
  • 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
    ",indexFrameVideo,videoBufferSize);
    		}else
    		{
    			printf("第%d帧,丢失
    ",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
    ",av_get_sample_fmt_name(sample_fmt));
    	return -1;
    }
    int _tmain(int argc,char*argv[])
    {   
    	if(argc<4)
    	{
    		printf("Parameter Error!
    ");
    		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!
    ");
    		return 0 ;
    	}
    	//获取流媒体信息
    	if(avformat_find_stream_info(pFormatCtx,NULL)<0)
    	{
    		printf("获取流媒体信息失败!
    ");
    		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!
    ");
    				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!
    ");
    				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!
    ");
    				goto end;
    			}
    			//分配视频帧
    			pFrame=av_frame_alloc();
    			if(pFrame==NULL)
    			{   
    				av_freep(&videoDstData[0]);
    				printf("alloc audio frame error
    ");
    				goto end ;
    			}
    		}
    		AVCodec *dec;
    		//依据编码器id查找编码器
    		dec=avcodec_find_decoder(pStream->codec->codec_id);
    		if(dec==NULL)
    		{   
    			printf("查找编码器失败!
    ");
    			goto end;
    		}
    		if(avcodec_open2(pStream->codec, dec, nullptr)!=0)
    		{
    			printf("打开编码器失败!
    ");
    			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文件 被分离开了。

    。。



  • 相关阅读:
    Redis面试题
    spring boot错误: 找不到或无法加载主类
    JAVA的高并发编程
    Redis多机多节点集群实验
    Redis单机多节点集群实验
    Redis集群概述
    Redis的持久化之AOF方式
    Redis的持久化之RDB方式
    Redis持久化介绍
    Redis Keys的通用操作
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/6858071.html
Copyright © 2011-2022 走看看