ffmpeg入门-CSDN博客

ffmpeg入门

ffmpeg入——安装

Fmpeg地址

FFmpeg源码地址:GitHub - FFmpeg/FFmpeg: Mirror of https://git.ffmpeg.org/ffmpeg.git

FFmpeg可执行文件地址:Download FFmpeg

Windows平台

Windows平台下载解压后如图所示(文件名称以-share结尾的是开发库)

FFmpeg库介绍

◆ ffmpeg.exe:用于音视频转码, 也可以从url/现场音频/视频源抓取输入源。

◆ ffplay.exe:一个非常简单和可移植的媒体播放器,使用FFmpeg库和SDL库。

◆ ffprobe.exe:查看多媒体文件的信息

将FFmpeg可执行文件加入系统环境变量,如下图所示:

Linux

ffmpeg官方 GitHub - FFmpeg/FFmpeg: Mirror of https://git.ffmpeg.org/ffmpeg.git

下载压缩包 或者 直接clone

  1. 直接clone:git clone https://github.com/FFmpeg/FFmpeg.git
  2. 下载完成,执行./configure

        如果出现了错误 nasm/yasm not found or too old. Use --disable-x86asm for a crippled build.

        这是因为 FFMPEG为了提高编译速度,使用了汇编指令,如MMX和SSE等。如果系统中没有yasm指令的话,就会该错误。

        1)下载:wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz

        2)解压:tar zxvf yasm-1.3.0.tar.gz

        3)切换路径: cd yasm-1.3.0

        4)执行配置: ./configure

        5)编译:make

        6)安装:make install

3.make && sudo make install 

测试代码:

#include<stdio.h>
#include<libavutil/log.h>


int main()
{
	av_log_set_level(AV_LOG_DEBUG);            //设置日志等级
	av_log(NULL, AV_LOG_INFO, "Hello~~~\n");       // INFO等级,输出hello

	return 0;
}

 编译链接: gcc -o ff_log ff_log.c -lavutil
        或者 gcc -o ff_log ff_log.c `pkg-config --cflags --libs libavutil`

(“pkg-config工具:1)会检查库的版本号。如果所需要的库的版本不满足要求,它会打印出错误信息,避免链接错误版本的库文件;2)会获得编译预处理参数,如宏定义,头文件的位置。3)还会获得链接参数,如库及依赖的其它库的位置,文件名及其它一些连接参数

        选项--cflags 它是用来指定程序在编译时所需要头文件所在的目录, 选项 --libs则是指定程序在链接时所需要的动态链接库的目录。

ffmpeg——log日志系统

log日志位于libavutil模块,log level的声明位于log.h

日志级别:

// 静默模式,不打印日志
#define AV_LOG_QUIET    -8
 
// 立即崩溃,退出程序
#define AV_LOG_PANIC     0
 
// 严重出错,无法修复
#define AV_LOG_FATAL     8
 
// 程序出错
#define AV_LOG_ERROR    16
 
// 警告
#define AV_LOG_WARNING  24
 
// 信息
#define AV_LOG_INFO     32
 
// 详细信息
#define AV_LOG_VERBOSE  40
 
// 调试日志
#define AV_LOG_DEBUG    48
 
// 跟踪日志
#define AV_LOG_TRACE    56

设置日志等级的方法set_log_level()位于log.c,其中av_log_level是个静态全局变量,具体如下

static int av_log_level = AV_LOG_INFO;
 
void av_log_set_level(int level)
{
    av_log_level = level;
}

常用的打印日志方法av_log(),声明位于log.h:

/** 
 * 发送特定消息到小于等于当前等级的日志,默认全部发送到stderr
 *
 * @param avcl  指向任意结构体的指针,结构体第一个变量为AVClass或NULL
 * @param level 日志等级
 * @param fmt   字符串格式
 */
void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4);

ffmpeg——文件的删除与重命名 

  • avpriv_io_delete() //删除
  • avpriv_io_move()  //重命名

编译链接: gcc xxx.c -o xxx   -lavformat -lavutil

#include <libavformat/avformat.h>
 
int main(int argc,char *argv[])
{
        int ret;
        set_av_log_level(AV_LOG_DEBUG);
        ret = avpriv_io_move("111.txt","222.txt");
        if(ret < 0)
        {
                av_log(NULL,AV_LOG_ERROR,"Failed to move file:%s\n","111.txt");
                return -1;
        }
 
        

        ret = avpriv_io_delete("./mytestfile.txt");
        if(ret < 0)
        {
                av_log(NULL,AV_LOG_ERROR,"Failed to delete file:%s\n","mytestfile.txt");
                return -1;
        }
 
        av_log(NULL,AV_LOG_DEBUG,"Success to delete file:%s\n","mytestfile.txt");
 
        return 0;
}

ffmpeg——目录操作

重要结构体:

        AVIODirContext  操作目录的上下文(记录avio_open_dir打开目录信息)

        AVIODirEntry  目录项。用于存放文件名,文件大小等信息

操作目录API:

  • avio_open_dir()
  • avio_read_dir()//读取目录中每一个文件的属性
  • avio_close_dir()

编译链接: gcc xxx.c -o xxx   -lavformat -lavutil

// 实现简单的ls命令

#include <libavutil/log.h>
#include <libavformat/avformat.h>
 
int main(int argc,char* argv[])
{
        int ret;
        AVIODirContext *ctx = NULL;
        AVIODirEntry *entry = NULL;
        av_log_set_level(AV_LOG_INFO);
 
        ret = avio_open_dir(&ctx,"./",NULL);
        if(ret < 0){
                av_log(NULL,AV_LOG_ERROR,"Cant open dir:%s\n",av_err2str(ret));
                return -1;
        }
 
        while(1){
                ret = avio_read_dir(ctx,&entry);//malloc entry
                if(ret < 0){
                        av_log(NULL,AV_LOG_ERROR,"Cant read dir:%s\n",av_err2str(ret));
                        avio_close_dir(&ctx);
                        return -1;
                }
                if(!entry){
                        //the end
                        break;
                }
 
                av_log(NULL,AV_LOG_INFO,"%12"PRId64" %s\n",
                        entry->size,
                        entry->name);
 
                avio_free_directory_entry(&entry);//free entry
        }
 
        avio_close_dir(&ctx);
        return 0;
}

ffmpeg——重要的结构体

  • AVIOContext(I/O上下文)                           // 实现文件或者流的I/O操作
  • AVFormatContext(封装格式上下文)          // 管理媒体文件的格式和封装信息
  • AVCodecContext(编解码器上下文)          // 控制和配置音视频编解码器的行为
  • AVCodecParameters (编解码器参数)      // 保存音视频流的基本参数信息
  • AVStream(音视频流)                               //  描述音视频流的属性
  • AVPacket (数据包)                                  // 用于在媒体文件、流之间传递数据
  • AVFrame(帧)                                         // 存储和传递音视频数据
  • AVDictionary(字典)                                 // 存储配置选项和元数据
  • AVFilterContext(滤镜上下文)                  // 处理音视频数据,应用滤镜效果。
  • SwsContext(图像转换上下文)                // 进行图像处理和转换
  • SwrContext(音频重采样上下文)            // 进行音频处理和转换

AVIOContext结构体,用于管理媒体文件或者网络流的输入和输出操作 

AVCodecContext结构体,包含了音频和视频编解码器的配置信息,如编码参数、解码参数、码率控制等。部分字段说明:

codec编解码器的AVCodec,比如指向AVCodec ff_aac_latm_decoder
width, height图像的宽高(只针对视频)
pix_fmt像素格式(只针对视频)
sample_rate采样率(只针对音频)
channels声道数(只针对音频)
sample_fmt采样格式(只针对音频

AVFormatContext结构体,是与多媒体文件格式相关的结构体,用于打开、读取和写入媒体文件。它包含了文件的格式信息、音视频流、文件I/O操作等。部分字段说明:

struct AVInputFormat* iformat输入媒体的AVInputFormat(输入数据的封装格式),比如指向AVInputFormatff_flv_demuxer
struct AVOutputFormat *oformat用于指定输出文件的格式以及文件写入的操作函数
AVIOContext *pb用于读取和写入媒体数据的 I/O 上下文
unsigned int nb_streams输入媒体的AVStream 个数
AVStream** streams输入媒体的AVStream []数组
int64_t start_time, duration媒体文件的起始时间戳和持续时间(以微秒为单位),计算方式可以参考av_dump_format()函数
int bit_rate输入媒体的码率

AVStream结构体,代表了音频或视频流,包括编解码参数、时间基准、时间戳等信息。部分字段说明 

index标识该视频/音频流,流索引
time_base流的时间基准,用于将时间戳转换为实际时间,时间戳示例为“以timebase为单位每秒cnt个units”。
avg_frame_rate该流的帧率
pts、dts、duration该流中第一个样本和最后一个样本的时间戳及时长。
codecpar该流的编码参数,包含音频或视频编码器特定的参数信息,如分辨率、像素格式、采样率、声道数、编码类型等(该流对应的AVCodecContext)
// AVStream 结构体主要成员

int index:				   // 标识该视频/音频流
AVCodecContext *codec:	   // 指向该视频/音频流的AVCodecContext(它们是一一对应的关系)
AVRational time_base:	   // 时基。通过该值可以把PTS,DTS转化为真正的时间。FFMPEG其他结构体中也有这个字段,但是根据我的经验,只有AVStream中的time_base是可用的。PTS*time_base=真正的时间
int64_t duration:		   // 该视频/音频流长度
AVDictionary *metadata:   // 元数据信息
AVRational avg_frame_rate:// 帧率(注:对视频来说,这个挺重要的)
AVPacket attached_pic:	   // 附带的图片。比如说一些MP3,AAC音频文件附带的专辑封面。

AVCodecParameters结构体保存音视频流的基本参数信息。该结构体通常会在AVCodecContext中被填充并使用。

部分字段说明:

odec_type媒体类型,比如AVMEDIA_TYPE_VIDEO(视频) AVMEDIA_TYPE_AUDIO(音频)等
codec_id编解码器类型, 比如AV_CODEC_ID_H264(H264编码) AV_CODEC_ID_AAC(AAC编码)

因为AVCodecContext结构体包含的参数太多,AVCodecParameters将编码器的参数从AVCodecContext分离出来,AVCodecParameters结构体中部分重要的参数如下: 

enum AVMediaType codec_type; 编解码器的类型 
const struct AVCodec *codec; 编解码器,初始化后不可更改
enum AVCodecID codec_id; 编解码器的id
int64_t bit_rate; 平均比特率
uint8_t *extradata; int extradata_size; 针对特定编码器包含的附加信息
AVRational time_base; 根据该参数可以将pts转化为实践
int width, height; 每一帧的宽和高
int gop_size; 一组图片的数量,编码时用户设置,解码时不使用
enum AVPixelFormat pix_fmt; 像素格式,编码时用户设置,解码时可由用户指定,但是在分析数据会被覆盖用户的设置
int refs; 参考帧的数量
enum AVColorSpace colorspace; YUV色彩空间类型
enum AVColorRange color_range; MPEG JPEG YUV范围
int sample_rate; 采样率 仅音频
int channels; 声道数(音频)
enum AVSampleFormat sample_fmt; //采样格式
int frame_size; 每个音频帧中每个声道的采样数量
int profile;配置类型
int level;级别

AVPacket结构体,用于存储音频或视频数据包,包括编码后的数据和时间戳。部分字段说明:

pts显示时间戳
dts解码时间戳
data压缩编码数据
size压缩编码数据大小
pos数据的偏移地址
stream_index所属的AVStream

AVFrame结构体,用于存储音频或视频帧的数据。它包括像素数据、采样数据、时间戳等。部分字段说明:

data解码后的图像像素数据(音频采样数据)
linesize对视频来说是图像中一行像素的大小;对音频来说是整个音频帧的大小
width, height图像的宽高(只针对视频)
key_frame是否为关键帧(只针对视频) 
pict_type帧类型(只针对视频) 。例如I, P, B
sample_rate音频采样率(只针对音频)
nb_samples音频每通道采样数(只针对音频)
pts显示时间戳

AVDictionary结构体,用于存储键值对,通常用于配置选项的传递和存储。

SwrContext结构体,用于音频重采样,支持不同采样率、通道数之间的转换

SwsContext结构体,用于图像转换,支持不同的像素格式和大小之间的转换

AVFilterContext结构体,用于配置和管理滤镜,包括音频滤镜和视频滤镜。

 播放器框架

有关参考常用api:


ffmpeg——多媒体文件简单操作

        多媒体文件是一个容器(也可以理解为包装盒,常见的mp4、flv、mkv都是不同的包装盒 ),可以存放很多数据,最常见的有字幕数据、视频数据、音频数据。

        在容器中有很多流(Stream/Track),流媒体文件中不同的流之数据不会相交的,比如音频流和视频流就是各自独立的。

        每种流是由不同的编码器编码的,每条流数据在多媒体文件中是经过压缩的,但是其使用压缩的编码器都是不一样的。比如音频有可能是MP3,有可能是AAC,视频常见的压缩器有可能是H264或者H265。

        从流中读出的数据称为包,在一个包中包含着一个或者多个帧。

 

ffmpeg操作流数据的基本步骤:

 从多媒体文件提取音、视频数据:

//提取多媒体文件的音频数据

//编译链接:gcc -o extra_audio extra_audio.c `pkg-config --libs --cflags libavutil libavformat libavcodec`

//执行 ./extra_audio test.mp4 1.aac

#include<stdio.h>
#include<libavutil/log.h>
#include <libavformat/avformat.h>


int main(int argc, char* argv[])
{
	
	int ret = -1;
	int idx = -1;
	
	// 1. 处理输入参数
	char* src, * dst;

	AVFormatContext* pFmtCtx = NULL;	// 多媒体上下文
	AVFormatContext* oFmtCtx = NULL;	// 目标文件上下文信息

	const AVOutputFormat* outFmt = NULL;		// 输出文件格式信息
	AVStream* outStream = NULL;			//输出文件的流
	AVStream* inStream = NULL;			//输入文件的流

	AVPacket pkt;		// 包


	av_log_set_level(AV_LOG_DEBUG);
	if (argc < 3) {	//该可执行程序  源文件   目标文件
		av_log(NULL, AV_LOG_INFO, "Arguments must be more than 3.");
		exit(-1);
	}
	src = argv[1];
	dst = argv[2];

	// 2、打开多媒体文件(包含文件头和文件体)
	if ((ret = avformat_open_input(&pFmtCtx, src, NULL, NULL)))
	{
		av_log(NULL, AV_LOG_ERROR, "%s\n", av_err2str(ret));
		exit(-1);
	}
	
	// 3、从多媒体文件中找到音频流
	idx = av_find_best_stream(pFmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);	//  在文件中找到“最佳”流。AVMEDIA_TYPE_AUDIO 提取音频 //视频 AVMEDIA_TYPE_VIDEO
	if (idx < 0) {
		av_log(pFmtCtx, AV_LOG_ERROR, "Does not include audio stream\n");
		goto _ERROR;
	}

	// 4、打开目的文件的上下文
	oFmtCtx = avformat_alloc_context();
	if (!oFmtCtx) {
		av_log(NULL, AV_LOG_ERROR, "No Memory!\n");
		goto _ERROR;
	}
		//设置输出文件的参数
	outFmt = av_guess_format(NULL, dst, NULL);	// 返回与所提供参数最匹配的已注册输出格式列表中的输出格式(NULL-系统匹配)
	oFmtCtx->oformat = outFmt;
	
	// 5、为目的文件创建一个新的音频流
	outStream = avformat_new_stream(oFmtCtx, NULL);
	
	// 6、设置输出音频参数
	inStream = pFmtCtx->streams[idx];
	avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);	//将源文件的内容复制到目的文件 
	outStream->codecpar->codec_tag = 0;	// 根据多媒体文件自动识别编解码器
	
		//上下文信息与输出文件绑定
	ret = avio_open2(&oFmtCtx->pb, dst, AVIO_FLAG_WRITE, NULL,NULL);
	if (ret < 0) {
		av_log(NULL, AV_LOG_ERROR, "%s", av_err2str(ret));
		goto _ERROR;

	}

	// 7、写多媒体文件头(包含多媒体的类型、版本等信息)到目标文件
	ret = avformat_write_header(oFmtCtx, NULL);
	if (ret < 0) {
		av_log(oFmtCtx, AV_LOG_ERROR, "%s", av_err2str(ret));
		goto _ERROR;

	}

	// 8、从源多媒体文件中读到音频数据
	while (av_read_frame(pFmtCtx, &pkt) >= 0) {  // 从多媒体文件读取到帧数据
		if (pkt.stream_index == idx) {	// 判断是否是我们需要的流(之前找到的音频流)
			// 修改目标文件时间戳 pts dts 时长 duration
			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;	// 音频文件dts 和 pts 相等,视频需要通过av_rescale_q_rnd()计算dts
			pkt.duration = av_rescale_q(pkt.duration, inStream->time_base, outStream->time_base);
			pkt.stream_index = 0;	// 流编号信息
			pkt.pos = -1;			// 偏移位置
			av_interleaved_write_frame(oFmtCtx, &pkt);		// 将音频帧写入目标文件中
			av_packet_unref(&pkt);
		}
	}
	// 9、写多媒体文件尾到文件中
	av_write_trailer(oFmtCtx);

	// 10、将申请的资源释放掉
_ERROR:
	if (pFmtCtx) {
		avformat_close_input(&pFmtCtx);
		pFmtCtx = NULL;
	}
	if (oFmtCtx->pb) {
		avio_close(oFmtCtx->pb);
	}
	if (oFmtCtx) {
		avformat_free_context(oFmtCtx);
		oFmtCtx = NULL;
	}
	return 0;
}
//提取多媒体文件的视频数据

//编译链接:gcc -o extra_video extra_video.c `pkg-config --libs --cflags libavutil libavformat libavcodec`

//执行 ./extra_video test.mp4 2.h264

#include<stdio.h>
#include<libavutil/log.h>
#include <libavformat/avformat.h>


int main(int argc, char* argv[])
{

	int ret = -1;
	int idx = -1;

	// 1. 处理输入参数
	char* src, * dst;

	AVFormatContext* pFmtCtx = NULL;	// 多媒体上下文
	AVFormatContext* oFmtCtx = NULL;	// 目标文件上下文信息

	const AVOutputFormat* outFmt = NULL;		// 输出文件格式信息
	AVStream* outStream = NULL;			//输出文件的流
	AVStream* inStream = NULL;			//输入文件的流

	AVPacket pkt;		// 包


	av_log_set_level(AV_LOG_DEBUG);
	if (argc < 3) {	//该可执行程序  源文件   目标文件
		av_log(NULL, AV_LOG_INFO, "Arguments must be more than 3.");
		exit(-1);
	}
	src = argv[1];
	dst = argv[2];

	// 2、打开多媒体文件(包含文件头和文件体)
	if ((ret = avformat_open_input(&pFmtCtx, src, NULL, NULL)))
	{
		av_log(NULL, AV_LOG_ERROR, "%s\n", av_err2str(ret));
		exit(-1);
	}

	// 3、从多媒体文件中找到视频流
	idx = av_find_best_stream(pFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);	//  在文件中找到“最佳”流。
	if (idx < 0) {
		av_log(pFmtCtx, AV_LOG_ERROR, "Does not include audio stream\n");
		goto _ERROR;
	}

	// 4、打开目的文件的上下文
	oFmtCtx = avformat_alloc_context();
	if (!oFmtCtx) {
		av_log(NULL, AV_LOG_ERROR, "No Memory!\n");
		goto _ERROR;
	}
	//设置输出文件的参数
	outFmt = av_guess_format(NULL, dst, NULL);	// 返回与所提供参数最匹配的已注册输出格式列表中的输出格式(NULL-系统匹配)
	oFmtCtx->oformat = outFmt;

	// 5、为目的文件创建一个新的视频流
	outStream = avformat_new_stream(oFmtCtx, NULL);

	// 6、设置输出视频参数
	inStream = pFmtCtx->streams[idx];
	avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);	//将源文件的内容复制到目的文件 
	outStream->codecpar->codec_tag = 0;	// 根据多媒体文件自动识别编解码器

	//上下文信息与输出文件绑定
	ret = avio_open2(&oFmtCtx->pb, dst, AVIO_FLAG_WRITE, NULL, NULL);
	if (ret < 0) {
		av_log(NULL, AV_LOG_ERROR, "%s", av_err2str(ret));
		goto _ERROR;

	}

	// 7、写多媒体文件头(包含多媒体的类型、版本等信息)到目标文件
	ret = avformat_write_header(oFmtCtx, NULL);
	if (ret < 0) {
		av_log(oFmtCtx, AV_LOG_ERROR, "%s", av_err2str(ret));
		goto _ERROR;

	}

	// 8、从源多媒体文件中读到视频数据
	while (av_read_frame(pFmtCtx, &pkt) >= 0) {  // 从多媒体文件读取到帧数据
		if (pkt.stream_index == idx) {	// 判断是否是我们需要的流(之前找到的视频流)
			// 修改目标文件时间戳 pts dts 时长 duration
			pkt.pts = av_rescale_q_rnd(pkt.pts, inStream->time_base, outStream->time_base, (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
			pkt.dts = av_rescale_q_rnd(pkt.dts, inStream->time_base, outStream->time_base, (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
			pkt.duration = av_rescale_q(pkt.duration, inStream->time_base, outStream->time_base);
			pkt.stream_index = 0;	// 流编号信息
			pkt.pos = -1;			// 偏移位置
			av_interleaved_write_frame(oFmtCtx, &pkt);		// 将视频帧写入目标文件中
			av_packet_unref(&pkt);
		}
	}
	// 9、写多媒体文件尾到文件中
	av_write_trailer(oFmtCtx);

	// 10、将申请的资源释放掉
_ERROR:
	if (pFmtCtx) {
		avformat_close_input(&pFmtCtx);
		pFmtCtx = NULL;
	}
	if (oFmtCtx->pb) {
		avio_close(oFmtCtx->pb);
	}
	if (oFmtCtx) {
		avformat_free_context(oFmtCtx);
		oFmtCtx = NULL;
	}
	return 0;
}

用到API:

/* 打开一个输入流并读取报头。编解码器没有打开。流必须用avformat_close_input()关闭。


参数:
    @param ps指向用户提供的AVFormatContext(由avformat_alloc_context)。可能是指向NULL的指针,在这种情况下,AVFormatContext由this分配函数并写入ps。注意,用户提供的AVFormatContext将被释放失败。

    @param url打开的流的url。

    @param fmt如果非null,该参数强制使用特定的输入格式。否则格式自动检测。

    @param options一个由AVFormatContext和demuxer-private填充的字典选项。返回时,此参数将被销毁并替换为包含未找到的选项的字典。可能为NULL。


成功时返回0,失败时返回负AVERROR。
*/

int avformat open input(AVFormatContext *ps, 
                        const char *url, 
                        const AVinputFormat *fmt,
                        AVDictionary **options)
//分配一个AVFormatContext。
// avformat_free_context()可以用来释放上下文和框架在其中分配的所有内容。

AVFormatContext *avformat_alloc_context(void);
/*
    在文件中找到“最佳”流。最佳流是根据各种启发式确定的,因为最可能是用户期望的。如果decoder参数非null, av_find_best_stream将为流的编解码器找到默认的解码器;找不到解码器的流将被忽略。


参数:

    *  @param ic                 媒体文件句柄
    * @param type                 视频,音频,字幕等
    * @param wanted_stream_nb    用户请求的流编号或-1为自动选择
    * @param related_stream    尝试找到一个流相关(例如:在同一个程序中)到这个,如果没有,则为-1
    * @param decoder_ret       如果非null,返回所选流的解码器
    * @param flags             当前没有定义


@return
    如果成功,返回非负的流号,如果没有找到请求类型的流,返回AVERROR_STREAM_NOT_FOUND,如果找到流但没有解码器,返回AVERROR_DECODER_NOT_FOUND

@note
如果av_find_best_stream返回成功并且decoder_ret不是NULL,那么*decoder_ret保证被设置为有效的AVCodec。

*/
int av_find_best_stream(AVFormatContext *ic,
                        enum AVMediaType type,
                        int wanted_stream_nb,
                        int related_stream,
                        const AVCodec **decoder_ret,
                        int flags);
/*返回与所提供参数最匹配的已注册输出格式列表中的输出格式,如果没有匹配则返回NULL。

@param short_name    if non-NULL检查short_name是否与注册格式的名称匹配
@param filename     if non-NULL检查filename是否以注册格式的扩展名结束
@param mime_type     if non-NULL检查mime_type是否与注册格式的MIME类型匹配

*/

const AVOutputFormat *av_guess_format(const char *short_name,
                                      const char *filename,
                                      const char *mime_type);

/*向媒体文件添加新的流。
    在解模时,由read_header()中的解模器调用。如果标志AVFMTCTX_NOHEADER在s.ctx_flags中设置,那么它也可以在read_packet()中被调用

    当复用时,应该在avformat_write_header()之前由用户调用。
    用户需要调用avformat_free_context()来清理分配,由avformat_new_stream()。

参数:
    @param s  媒体文件句柄
    @param c  未使用,不做任何事情

@return 新创建的流,错误时返回NULL。

*/

AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c);
/*
***创建并初始化一个AVIOContext来访问url表示的资源。

参数:
  @param s      用于返回指向创建的AVIOContext的指针。在失败的情况下,指向的值被设置为NULL。
  @param url     要访问的资源
  @param flags   控制如何打开url所指示的资源的标志
  @param int_cb   在协议级别使用的中断回调
  @param options 私有选项的字典,返回时此参数将被销毁并替换为包含未找到选项的字典可能为NULL。
  @return >= 0如果成功,则为an对应的负值失败时的AVERROR代码


注:当url所指示的资源以读+写方式打开时,AVIOContext只能用于写。

*/
int avio_open2(AVIOContext **s, const char *url, int flags,
               const AVIOInterruptCB *int_cb, AVDictionary **options);
/*
*** 返回流的下一帧。此函数返回存储在文件中的内容,而不验证是否存在用于解码器的有效帧。它将把存储在文件中的内容分割成帧,并为每次调用返回一个帧。它不会省略有效帧之间的无效数据,以便为解码器提供可能用于解码的最大信息。
    如果成功,返回的数据包将被引用计数(pkt->但已设置)并无限期有效。当不再需要该数据包时,必须使用av_packet_unref()释放该数据包。对于视频,数据包只包含一帧。对于音频,如果每帧有一个已知的固定大小(例如PCM或ADPCM数据),它包含一个整数帧数。如果音频帧具有可变大小(例如MPEG音频),则它包含一个帧。
    pkt->pts, pkt->dts和pkt->duration在AVStream中总是设置为正确的值。Time_base单位(并猜测格式是否不能提供它们)。如果视频格式有b帧,pkt->pts可以是AV_NOPTS_VALUE,所以如果不解压缩有效载荷,最好依赖pkt->dts。

return: 如果OK则返回0,错误或文件结束时返回< 0。如果出现错误,pkt将为空白(就好像它来自av_packet_alloc())。

注:注意PKT将被初始化,所以它可能是未初始化的,但它不能包含需要释放的数据。
*/
int av_read_frame(AVFormatContext *s, AVPacket *pkt);

多媒体文件格式转封装:

音视频解码流程: 

转换多媒体文件的格式代码: 

//转换多媒体文件的格式

//编译链接:gcc -o remux remux.c `pkg-config --libs --cflags libavutil libavformat libavcodec`

//执行 ./remux test.mp4 test.flv

#include<stdio.h>
#include<libavutil/log.h>
#include <libavformat/avformat.h>


int main(int argc, char* argv[])
{

	int ret = -1;
	int idx = -1;
	int i = 0;
	int stream_idx = 0;

	// 处理输入参数
	char* src, * dst;

	int* stream_map = NULL;

	AVFormatContext* pFmtCtx = NULL;	// 多媒体上下文
	AVFormatContext* oFmtCtx = NULL;	// 目标文件上下文信息

	const AVOutputFormat* outFmt = NULL;		// 输出文件格式信息

	AVPacket pkt;		// 包


	av_log_set_level(AV_LOG_DEBUG);
	if (argc < 3) {	//该可执行程序  源文件   目标文件
		av_log(NULL, AV_LOG_INFO, "Arguments must be more than 3.");
		exit(-1);
	}
	src = argv[1];
	dst = argv[2];

	// 打开多媒体文件(包含文件头和文件体)
	if ((ret = avformat_open_input(&pFmtCtx, src, NULL, NULL)))
	{
		av_log(NULL, AV_LOG_ERROR, "%s\n", av_err2str(ret));
		exit(-1);
	}

	

	// 打开目的文件的上下文
	avformat_alloc_output_context2(&oFmtCtx, NULL, NULL, dst);
	if (!oFmtCtx) {
		av_log(NULL, AV_LOG_ERROR, "NO Memory!\n");
		goto _ERROR;
	}
	
	stream_map = av_calloc(pFmtCtx->nb_streams, sizeof(int));
	if (!stream_map) {
		av_log(NULL, AV_LOG_ERROR, "NO Memory!\n");
		goto _ERROR;
	}

	// 遍历源文件每一条流
	for (i = 0; i < pFmtCtx->nb_streams; i++) {
		AVStream* outStream = NULL;
		AVStream* inStream = pFmtCtx->streams[i];
		AVCodecParameters* inCodecPar = inStream->codecpar;

		// 只处理音、视频、字幕数据
		if (inCodecPar->codec_type != AVMEDIA_TYPE_AUDIO &&
			inCodecPar->codec_type != AVMEDIA_TYPE_VIDEO &&
			inCodecPar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
				stream_map[i] = -1;
				continue;
		}
		stream_map[i] = stream_idx++;
		// 为目的文件创建一个新的视频流
		outStream = avformat_new_stream(oFmtCtx, NULL);
		if (!outStream) {
			av_log(oFmtCtx, AV_LOG_ERROR, "NO Memory!\n");
			goto _ERROR;
		}

		avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);	//将源文件的内容复制到目的文件 
		outStream->codecpar->codec_tag = 0;	// 根据多媒体文件自动识别编解码器


	}

	//上下文信息与输出文件绑定
	ret = avio_open2(&oFmtCtx->pb, dst, AVIO_FLAG_WRITE, NULL, NULL);
	if (ret < 0) {
		av_log(NULL, AV_LOG_ERROR, "%s", av_err2str(ret));
		goto _ERROR;

	}

	// 写多媒体文件头(包含多媒体的类型、版本等信息)到目标文件
	ret = avformat_write_header(oFmtCtx, NULL);
	if (ret < 0) {
		av_log(oFmtCtx, AV_LOG_ERROR, "%s", av_err2str(ret));
		goto _ERROR;

	}

	// 从源多媒体文件中读到音、视频、字幕数据
	while (av_read_frame(pFmtCtx, &pkt) >= 0) {  // 从多媒体文件读取到帧数据,读取码流中的音频若干帧或者视频一帧
		AVStream* inStream, * outStream;

		inStream = pFmtCtx->streams[pkt.stream_index];
		if (stream_map[pkt.stream_index] < 0) {		// 流编号为-1, 不是音、视频、字幕流数据
			av_packet_unref(&pkt);	// 释放packet
			continue;
		}
		pkt.stream_index = stream_map[pkt.stream_index];

		outStream = oFmtCtx->streams[pkt.stream_index];
		av_packet_rescale_ts(&pkt, inStream->time_base, outStream->time_base);	// 修改时间戳
		pkt.pos = -1;			// 偏移位置
		av_interleaved_write_frame(oFmtCtx, &pkt);		// 将视频帧写入目标文件中
		av_packet_unref(&pkt);
		
	}
	// 写多媒体文件尾到文件中
	av_write_trailer(oFmtCtx);

	// 将申请的资源释放掉
_ERROR:
	if (pFmtCtx) {
		avformat_close_input(&pFmtCtx);
		pFmtCtx = NULL;
	}
	if (oFmtCtx->pb) {
		avio_close(oFmtCtx->pb);
	}
	if (oFmtCtx) {
		avformat_free_context(oFmtCtx);
		oFmtCtx = NULL;
	}
	if (stream_map) {
		av_free(stream_map);
	}
	return 0;
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值