前言:
今天开始记录一些之前学习ffmpeg的一些基础知识,也好加深自己的理解!
一、从媒体文件中抽取音频数据:
操作步骤:
-
1、处理一些参数:比如说从哪个多媒体文件中抽取音频数据;抽取的音频是什么格式。
-
2、打开多媒体文件
-
3、从多媒体文件中找到音频流
-
4、打开目的文件的上下文
-
5、为目的文件创建一个新的音频流
-
6、设置输出音频参数
-
7、写多媒体文件头到目的文件(这个是意思是读取到媒体文件头,就可以判断是音频流还是视频流)
-
8、从源多媒体文件中读取到音频数据到目的文件
-
9、写多媒体文件尾到文件中
-
10、将申请的资源释放掉
实例代码:
#include <stdio.h>
#include <libavutil/log.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
int main(int agrc , char *argv[])
{
int ret = -1 ; //返回值变量
int index = -1; //多媒体文件索引值
//1、处理一些参数
char *src = NULL; //源文件
char *dst = NULL; //目的文件
//定义源文件上下文
AVFormatContext *pFmCtx = NULL;
//输出目的文件的上下文变量
AVFormatContext *oFmCtx = NULL;
//定义输出格式变量
AVOutputFormat *outFmt = NULL;
//目的文件输出流
AVStream *outStream = NULL;
//输入音频流
AVstream *inStream = NULL;
// 读取到的数据
AVPacket pkt;
//设置ffmpeg的log级别
av_log_set_level(AV_LOG_DEBUG);
//1、输入参数判断
if(argc < 3)
{
av_log(NULL,AV_LOG_INFO,"arguments must be more than 3\r\n");
exit(-1);
}
src = argv[1];
dst = argv[2];
//2、开始打开多媒体文件
/*
int avformat_open_input(AVFormatContext **ps, const char *url,\
const AVInputFormat *fmt, AVDictionary **options)
*/
if( (ret = avformat_open_input(&pFmCtx,src,NULL,NULL)) < 0)
{
//通过ffmpeg接口来打印ret的值
av_log(NULL,AV_LOG_ERROR,"%s\n",av_err2str(ret));
exit(-1);
}
//3、从多媒体文件中找到音频流
/*
int av_find_best_stream(AVFormatContext *ic, enumAVMediaType type,\
int wanted_stream_nb, int related_stream, const AVCodec **decoder_ret,\
int flags)
返回值index就是在多媒体文件中的索引值
*/
index = av_find_best_stream(pFmCtx,AVMEDIA_TYPE_AUDIO,-1,-1,0);
if(index < 0)
{
av_log(pFmCtx,AV_LOG_ERROR,"not audio\n");
goto _ERROR;
}
//打开目的文件的上下文
/*
AVFormatContext *avformat_alloc_context(viod)
对目的文件的上下文进行分配空间
*/
oFmCtx = avformat_alloc_context();
//判断是否分配内存空间成功
if(!oFmCtx)
{
av_log(NULL, AV_LOG_ERROR, "NO Memory!\n");
goto _ERROR;
}
//设置目的文件输出音频参数
/*
const AVOutputFormat *av_guess_format(const char * short_name,\
const char *filename , const char *mime_type)
*/
outFmt = av_guess_format(NULL,dst,NULL);
//把输出文件格式设置给输出目的文件的上下文
oFmCtx->ofomat = outFmt;
//5、为目的文件,创建一个新的视频流
/*
AVStream *avformat_new_stream(AVFormatContext *s,\
const AVCodec *c)
*/
outStream = avformat_new_stream(oFmCtx, NULL);
//6、设置输出音频参数(注意这里并没有修改原本的音频格式,只是简单的抽取)
/*
int avcodec_parameters_copy(AVCodecParameters *dst,\
const AVCodecParameters *src)
*/
// pFmtCtx->stream,这个的意思拿到了所有的流,包括音频流和视频流、字幕流
//这里的index是我们上面已经获取到的音频流索引值
inStream = pFmtCtx->stream[index]
avcodec_parameters_copy(outStream->codecpar,inStream->codecpar);
//这个值设置为0的意义是,根据多媒体文件来自动适配编解码器
outStream->codecpar->codec_tag = 0;
//绑定输出上下文和目的文件
/*
int avio_open2(AVIOContext **s, const char *url,\
int flags,const AVIOInterruptCB *int_cb, AVDictionary **options)
*/
ret = avio_open2(&oFmCtx->pb,dst,AVIO_FLAG_WRITE,NULL,NULL);
if( ret <0 )
{
av_log(oFmCtx,AV_LOG_ERROR,"%s",av_err2str(ret));
goto _ERROR;
}
//7、写多媒体文件头到目的文件
/*
int avformat_write_header(AVFormatContext *s,\
AVDictionary **options)
*/
ret = avformaat_write_header(oFmCtx,NULL);
if(ret <0)
{
av_log(oFmCtx,AV_LOG_ERROR,"%s",av_err2str(ret));
goto _ERROR;
}
//8、从源多媒体文件中读到音频数据到目的文件中
/*
int av_read_frame(AVFormatContext *s,\
AVPacket *pkt)
*/
while(av_read_frame(oFmCtx, &pkt) >=0) //说明已经获取到了数据了
{
//判断是否读取到音频流,为什么这样做,因为读取的数据里面有三种流
if(pkt.stream_index == idx)
{
/*
int av_interleaved_write_frame(AVFormatContext *s,\
AVPacket *pkt)
*/
//对于音频的pts和dts的时间戳是一样的
//但是对视频来说,pts和dts是有可能不一样的
//需要改变一下时间戳
/*
int64_t av_rescale_q_rnd(int64_t a, AVRational bq,\
AVRational cq, enum AVRounding rnd)
a:是原始的pts
bq:是输入流的时间基
cq:是输出流的时间基
rnd:进行近视运算,当时间基进行转换除不尽的话
*/
pkt.pts = av_rescale_q_rnd(pkt.pts, inStream->time_base,\
outStream->time_base,\
(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX) );
pkt.dts = pkt.pts;
//音频帧的时长是多少
/*
int64_t av_rescale_q(int64_t a, AVRational bq,\
AVRational cq )
*/
pkt.duration = av_rescale_q(pkt.duration,inStream->time_base,outStream->time_base);
pkt.stream_indext = 0 ;//检查stream_index 是否有变化,而我们抽取的都是一路音频流,所以设置为0
//相对位置,设置成-1,让ffmpeg自动去计算
pkt.pos = -1;
av_interleaved_write_frame(oFmCtx, &pkt);//把音频数据写到目的文件中
//写完之后,就不再需要这个pkt了,所以调用下面这个接口,来减少pkt包的移动计数
//这样再进入while循环的时候,就是一个新的pkt包了
/*
void av_packet_unref(AVPacket *pkt)
*/
av_packet_unref(&pkt);
}
}
//9、写多媒体文件尾到文件中
/*
int av_write_trailer(AVFormatContext *s)
*/
av_write_trailer(oFmCtx);
//10、将申请的资源释放掉
_ERROR:
//如果pFmCtx有内存资源,需要进行释放
if(pFmCtx)
{
avformat_close_input(&pFmCtx);
//释放完之后进行指向NULL
pFmCtx = NULL;
}
if(oFmCtx->pb)//不为空,说明打开了目标文件
{
avio_close(oFmCtx->pb);
}
if(oFmCtx)
{
avformat_free_context(&oFmCtx);
//释放完之后进行指向NULL
oFmCtx = NULL;
}
return 0;
}