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等级,输出helloreturn 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 entryif(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 endbreak;}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:

  • 官方api文档:FFmpeg: Main Page
  • 常用api: https://www.cnblogs.com/linuxAndMcu/p/12041359.html
  • 音视频八股文(6)-- ffmpeg大体介绍和内存模型 https://blog.51cto.com/moonfdd/6229017

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_VIDEOif (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 时长 durationpkt.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()计算dtspkt.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 时长 durationpkt.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);	// 释放packetcontinue;}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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/1464.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入剖析Spring框架:循环依赖的解决机制

你好&#xff0c;我是柳岸花开。 什么是循环依赖&#xff1f; 很简单&#xff0c;就是A对象依赖了B对象&#xff0c;B对象依赖了A对象。 在Spring中&#xff0c;一个对象并不是简单new出来了&#xff0c;而是会经过一系列的Bean的生命周期&#xff0c;就是因为Bean的生命周期所…

如何添加所有未跟踪文件到暂存区?

文章目录 如何将所有未跟踪文件添加到Git暂存区&#xff1f;步骤与示例代码1. 打开命令行或终端2. 列出所有未跟踪的文件3. 添加所有未跟踪文件到暂存区4. 验证暂存区状态 如何将所有未跟踪文件添加到Git暂存区&#xff1f; 在版本控制系统Git中&#xff0c;当我们首次创建新文…

安全狗云眼的主要功能有哪些?

"安全狗云眼"是一款综合性的网络安全产品&#xff0c;主要用于实时监控和保护企业的网络安全。其核心功能包括威胁检测、漏洞扫描、日志管理和合规性检查等。 以下是安全狗云眼的主要功能详细介绍&#xff1a; 1、资产管理 定期获取并记录主机上的Web站点、Web容器、…

【科大讯飞笔试题汇总】2024-04-21-科大讯飞春招笔试题-三语言题解(CPP/Python/Java)

&#x1f36d; 大家好这里是KK爱Coding &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新科大讯飞近期的春秋招笔试题汇总&#xff5e; &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&…

卷积神经网络 (CNN)

计算机视觉最常见的机器学习模型体系结构之一是卷积神经网络 (CNN)。 CNN 使用筛选器从图像中提取数值特征图&#xff0c;然后将特征值馈送到深度学习模型中以生成标签预测。 例如&#xff0c;在图像分类方案中&#xff0c;标签表示图像的主要主题&#xff08;换句话说&#xf…

微信有关白名单IP

一、商家支付 二、公众号

开启智慧之旅,AI与机器学习驱动的微服务设计模式探索

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#x1f525;&#xff1a;探索设计模式的魅力&#xff1a;开启智慧…

基于SpringBoot + Vue实现的时装购物管理系统设计与实现+毕业论文+开题报告+答辩PPT

介绍 系统包含用户、管理员两个角色 管理员&#xff1a;首页、个人中心、用户管理、商品分类管理、颜色管理、商品信息管理、商品评价管理、系统管理、订单管理 用户:首页、个人中心、商品评价管理、我的收藏管理、订单管理 前台首页:首页、商品信息、商品资讯、个人中心、后台…

【MySQL】查询(进阶)

文章目录 前言1、新增2、聚合查询2.1聚合函数2.1.1count2.1.2sum2.1.3avg2.1.4max和min 2.2、GROUP BY子句2.3HAVING 3、联合查询/多表查询3.1内连接和外连接3.2自连接3.3子查询3.4合并查询 前言 在前面的内容中我们已经把查询的基本操作介绍的差不多了&#xff0c;接下来我们…

Llama 3 实测效果炸裂,一秒写数百字(附镜像站)

这几天大火的llama 3刚刚在https://askmanyai.cn上线了&#xff01; 玩了一会儿&#xff0c;这个生成速度是真的亚麻呆住。文案写作和代码生成直接爽到起飞&#xff0c;以往gpt要写一两分钟的千字文&#xff0c;llama 3几秒钟就写完了。而且效果甚至感觉更好&#xff1f; 效果惊…

el-menu 有一级二级三级菜单

效果如下 菜单代码如下 <el-menu:default-active"menuDefaultActive"class"el-menu-box":text-color"menuTextColor":active-text-color"menuActiveTextColor":unique-opened"true"><!-- 一级菜单 --><tem…

二、python+前端 实现MinIO分片上传

python前端 实现MinIO分片上传 一、背景二、流程图三、代码 一、背景 问题一&#xff1a;前端 -> 后端 ->对象存储 的上传流程&#xff0c;耗费带宽。 解决方案&#xff1a;上传流程需要转化为 前端 -> 对象存储&#xff0c;节省上传带宽 问题二&#xff1a;如果使用…

Crypto量化高频体验总结

Crypto量化高频体验总结 人工智能与量化交易算法知识库 2024-04-21 21:02 美国 以下文章来源于Quant搬砖工 &#xff0c;作者quant搬砖队工头 Quant搬砖工. 稳健的收益要一点一点赚&#xff0c;量化的板砖要一块一块搬&#xff01; 前言 前两天在翻历史文章的时候&#xf…

【高阶数据结构】并查集 -- 详解

一、并查集的原理 1、并查集的本质和概念 &#xff08;1&#xff09;本质 并查集的本质&#xff1a;森林。 &#xff08;2&#xff09;概念 在一些应用问题中&#xff0c;需要将 n 个不同的元素划分成一些不相交的集合。 开始时&#xff0c;每个元素自成一个单元素集合&…

SpringBoot 集成Nacos注册中心和配置中心-支持自动刷新配置

SpringBoot 集成Nacos注册中心和配置中心-支持自动刷新配置 本文介绍SpringBoot项目集成Nacos注册中心和配置中心的步骤&#xff0c;供各位参考使用 1、配置pom.xml 文件 在pom.xml文件中定义如下配置和引用依赖&#xff0c;如下所示&#xff1a; <properties><pr…

buuctf之ciscn_2019_c_1

ciscn_2019_c_1 一、查看属性二、静态分析三、动态分析四、思路五、exp 一、查看属性 首先还是必要的查看属性环节&#xff1a; 可以知道该文件是一个x86架构下的64位小端ELF文件&#xff0c;开启了栈不可执行&#xff08;NX&#xff09; 执行一下&#xff0c;先有一个选择&…

ROS2 王牌升级:Fast-DDS 性能直接碾压 zeroMQ 「下」

以下内容为本人的学习笔记&#xff0c;如需要转载&#xff0c;请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/aU1l3HV3a9YnwNtC1mTiOA 性能比较 下面就以官网的测试数据为准&#xff0c;让我们一起来看看它们的性能差别到底怎样。 本次比较仅针对 Fast RT…

60道计算机二级模拟试题选择题(含答案和解析)

点击下载《60道计算机二级模拟试题选择题&#xff08;含答案和解析&#xff09;》 1. 前言 本文设计了一份针对计算机二级考试的选择题&#xff0c;旨在考察考生对计算机基础知识和应用技能的掌握情况。试题涵盖了计算机基础知识、操作系统、办公软件、计算机网络等多个方面&…

【CVPR2023】《A2J-Transformer:用于从单个RGB图像估计3D交互手部姿态的锚点到关节变换网络

这篇论文的标题是《A2J-Transformer: Anchor-to-Joint Transformer Network for 3D Interacting Hand Pose Estimation from a Single RGB Image》&#xff0c;作者是Changlong Jiang, Yang Xiao, Cunlin Wu, Mingyang Zhang, Jinghong Zheng, Zhiguo Cao, 和 Joey Tianyi Zhou…

polkit服务启动失败

使用systemctl 命令报错 Authorization not available. Check if polkit service is running or see debug message for more information. 查看polkit状态是失败的状态&#xff0c;报缺少libstdc.so.6 systemctl status polkit 需要安装libstdc.so.6库 先加载所有安装包 …