#include<stdio.h>
//引入libavutil库中的 log.h 文件头
#include<libavutil/log.h>
// 主函数
int main(int argc,char* argv[]){
//设置log level 级别
av_log_set_level(AV_LOG_DEBUG);
//记录 Info 级别下的 av 日志
av_log(NULL,AV_LOG_INFO,"hello world! use libavutil/log.h!\n");
}
3.1 FFmpeg删除文件
//引入avformat.h 头文件
#include<libavformat/avformat.h>
int main(int argc,char* argv[]){
//定义记录操作结果变量
int ret;
ret = avpriv_io_delete("./testdel.txt");
if(ret<0) {
av_log(NULL,AV_LOG_ERROR,"Failed to delete file testdel.text!");
return -1;
}
return 0;
}
运行结果testdel.txt 不存在时:
testdel.txt 存在时
//引入avformat.h 头文件
#include<libavformat/avformat.h>
int main(int argc,char* argv[]){
//定义记录操作结果变量
int ret;
//重命名 testmove000.txt 为 testmove111.txt
ret = avpriv_io_move("testmove000.txt","testmove111.txt");
if (ret < 0){
av_log(NULL,AV_LOG_ERROR,"更新testmove000.txt 文件名失败!");
return -1;
}
av_log(NULL,AV_LOG_INFO,"更新testmove000.txt 文件名成功!");
ret = avpriv_io_delete("./testdel.txt");
if(ret<0) {
av_log(NULL,AV_LOG_ERROR,"Failed to delete file testdel.text!");
return -1;
}
return 0;
}
testmove000.txt 文件不存在
testmove000.txt 文件存在
AVFormatContext
格式上下文,连接多个Api的桥梁、读多媒体流的时候需要将 AVFormatContext 指针变量作为参数传递,获取相关数据。
AVStream
从 AVFormatContext 可以获取到AVStream->音频流、视频流
AVPacket
从 AVStream 可以获取到对应的 AVPacket,AVPacket 的组成内容是被压缩的帧。
AVFrame
从AVPacket 可以拿到具体的 1-n 个AVFrame
如下图所示:
#include <libavutil/log.h>
#include <libavformat/avformat.h>
//获取视频Meta信息
int main(int argc,char* argv[]){
int ret;
//1.定义格式上下文(容器)指针变量
AVFormatContext *fmt_ctx = NULL;
av_log_set_level(AV_LOG_INFO);
//2.注册音视频全局解码器
av_register_all();
//3.打开多媒体文件,注意第一个参数为指针变量的地址,而不是上下文容器 fmt_ctx 指向的上下文所存储的内存地址
//参数含义:[指向fmt_ctx指针变量的地址,要打开的视频文件,文件格式-无值程序会根据视频文件类型后缀自动识别,命令行参数]
ret = avformat_open_input(&fmt_ctx,"test.mp4",NULL,NULL);
if (ret < 0) {
av_log(NULL,AV_LOG_ERROR,"Can't open file: %s\n",av_err2str(ret));
return -1;
}
//4.[上下文指针,index 默认值为0,文件src,0表示输入类型文件|输出流则为1]
av_dump_format(fmt_ctx,0,"./test.mp4",0);
//5.关闭格式上下文,注意可以从格式上下文中可以获取到对应的AVStream
avformat_close_input(&fmt_ctx);
return 0;
}
当前目录下音视频文件如下所示:
注意:在这几天阅读源码的过程中,我注意到avreadframe()读出来的数据是 AVPacket 类型的变量,这使我很疑惑,最终我在讲师的课程中得到了答案,是因为FFmpeg的历史问题。
#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avformat.h>
//定义音频播放头信息
void adts_header(char *szAdtsHeader, int dataLen){
int audio_object_type = 2;
int sampling_frequency_index = 7;
int channel_config = 2;
int adtsLen = dataLen + 7;
szAdtsHeader[0] = 0xff; //syncword:0xfff 高8bits
szAdtsHeader[1] = 0xf0; //syncword:0xfff 低4bits
szAdtsHeader[1] |= (0 << 3); //MPEG Version:0 for MPEG-4,1 for MPEG-2 1bit
szAdtsHeader[1] |= (0 << 1); //Layer:0 2bits
szAdtsHeader[1] |= 1; //protection absent:1 1bit
szAdtsHeader[2] = (audio_object_type - 1)<<6; //profile:audio_object_type - 1 2bits
szAdtsHeader[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency index:sampling_frequency_index 4bits
szAdtsHeader[2] |= (0 << 1); //private bit:0 1bit
szAdtsHeader[2] |= (channel_config & 0x04)>>2; //channel configuration:channel_config 高1bit
szAdtsHeader[3] = (channel_config & 0x03)<<6; //channel configuration:channel_config 低2bits
szAdtsHeader[3] |= (0 << 5); //original:0 1bit
szAdtsHeader[3] |= (0 << 4); //home:0 1bit
szAdtsHeader[3] |= (0 << 3); //copyright id bit:0 1bit
szAdtsHeader[3] |= (0 << 2); //copyright id start:0 1bit
szAdtsHeader[3] |= ((adtsLen & 0x1800) >> 11); //frame length:value 高2bits
szAdtsHeader[4] = (uint8_t)((adtsLen & 0x7f8) >> 3); //frame length:value 中间8bits
szAdtsHeader[5] = (uint8_t)((adtsLen & 0x7) << 5); //frame length:value 低3bits
szAdtsHeader[5] |= 0x1f; //buffer fullness:0x7ff 高5bits
szAdtsHeader[6] = 0xfc;
}
//获取视频Meta信息 & 抽取音频数据
int main(int argc,char* argv[]){
int ret;
char* src = NULL;
char* dst = NULL;
int len;
//0.流的 index
int audio_index;
AVPacket pkt;
//1.定义格式上下文(容器)指针变量
AVFormatContext *fmt_ctx = NULL;
av_log_set_level(AV_LOG_INFO);
//2.注册音视频全局解码器
av_register_all();
//3.read two params from console ,从控制台读取输入的两个参数、param one is the read file and param two is the audio file
// 即从源音视频文件中抽离出来的音频文件
if (argc < 3) {
av_log(NULL,AV_LOG_ERROR,"the count of params should be more than three!\n");
return -1;
}
src = argv[1];
dst = argv[2];
if(!src || !dst) {
av_log(NULL,AV_LOG_ERROR,"src or dst is null\n");
}
//4.打开多媒体文件,注意第一个参数为指针变量的地址,而不是上下文容器 fmt_ctx 指向的上下文所存储的内存地址
//参数含义:[指向fmt_ctx指针变量的地址,要打开的视频文件,文件格式-无值程序会根据视频文件类型后缀自动识别,命令行参数]
ret = avformat_open_input(&fmt_ctx,src,NULL,NULL);
if (ret < 0) {
av_log(NULL,AV_LOG_ERROR,"Can't open file: %s\n",av_err2str(ret));
return -1;
}
//打开输出文件
FILE* dst_fd = fopen(dst,"wb");
if(!dst_fd) {
av_log(NULL,AV_LOG_ERROR,"can't open out file!\n");
avformat_close_input(&fmt_ctx);//关闭格式上下文
return -1;//打开音频输出文件出错
}
//5.[上下文指针,index 默认值为0,文件src,0表示输入类型文件|输出流则为1]
av_dump_format(fmt_ctx,0,src,0);
//6.获取流 stream 参数解释:格式上下文、音频类型-宏、音频流索引号、与音频流相关的视频流索引好,不知道的情况下都设置为-1、编解码器、flag、返回值是流的编号
ret = av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
if (ret < 0 ) {
av_log(NULL,AV_LOG_ERROR,"Can't find the best stream@\n");
avformat_close_input(&fmt_ctx);
fclose(dst_fd);
return -1;
}
audio_index = ret;
//7.初始化数据包
av_init_packet(&pkt);
//7.获取音频数据 fmt_ctx -> pkt,每次读取的帧数 > 0
while(av_read_frame(fmt_ctx,&pkt) >=0) {
//7.1 包中的从属流的index == best stream 中的index 则将读出的packet写入文件
if (pkt.stream_index == audio_index) {
//每写入一个pkt,顺带写入音频播放的头部信息
char adts_header_buf[7];
adts_header(adts_header_buf,pkt.size);
fwrite(adts_header_buf,1,7,dst_fd);
//7.2 将packet当中的数据每次以1个字节的方式写 packet.data 当中size 个数据,最终数据进入 dst_fd
len = fwrite(pkt.data,1,pkt.size,dst_fd);
if (len != pkt.size) {
av_log(NULL,AV_LOG_WARNING,"warning,lenth of data is not equal size of pkt");
}
}
//7.3 将读取包数据的空间释放掉 即 av_read_frame 申请的空间
av_packet_unref(&pkt);
}
//8. 关闭格式上下文,注意从格式上下文中可以获取到对应的AVStream
avformat_close_input(&fmt_ctx);
//9. 判断文件句柄是否仍然存在,存在则关闭
if (dst_fd) {
fclose(dst_fd);
}
return 0;
}
运行结果如下所示
播放音频效果如下所示
采用如下命令 ffplay test.aac 即可播放从 test.mp4 中抽取的音频文件。
完。