FFmpeg 工程之路-多媒体文件处理

章节

  • FFmpeg 代码结构
  • FFmpeg日志系统的使用
    • c 调用 FFmpeg 库avutil 的avlog 函数->testavlog.c
    • 运行结果
  • FFmpeg 文件的删除与重命名
    • FFmpeg删除文件-avpriv_io_delete()
    • FFmpeg 重命名文件-avpriv_io_move()
  • FFmpeg处理流数据
    • 多媒体文件的基本概念
    • 几个重要的结构体
  • FFmpeg 打印音/视频信息(Meta)
    • 获取视频文件 音视频流信息代码->mediainfo.c
    • 展示音/视频信息
  • 抽取音频数据
    • 抽取音频数据涉及到的Api
    • 实战从音视频文件中抽取音频数据

1. FFmpeg 代码结构

FFmpeg 代码结构

2. FFmpeg 日志系统使用

2.1 c 调用 FFmpeg 库avutil 的avlog 函数->testavlog.c

#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");
}

2.2 运行结果

output 结果

3.FFmpeg 文件的删除与重命名

  • 删除文件Api-> avpriv_io_delete(fileName)
  • 重命名文件Api-> avpriv_io_move(src,dst)
    // src 指被命名文件、dst指命名后文件

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 不存在时

testdel.txt 存在时

运行结果

3.2 FFmpeg 重命名文件

//引入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 文件不存在

output 结果

testmove000.txt 文件存在

output 结果

testmove000.txt 更名为 testmove111.txt

4.FFmpeg处理流数据

4.1 多媒体文件的基本概念

  • 多媒体文件是个容器、音视、字母数据、
  • 在容器里有很多流(stream/track) 比如 音频流、视频流、多路音频流、
  • 每种流是由不同的编码器编码的、
  • 如视频编码 通常采用HR64 HR65编码、
  • 从流中 Stream 读取的数据称为包 Packet 多帧压缩成的包、
  • 一个包中包含着一个或多个帧 Frame 没有被压缩的数据、

4.2 几个重要的结构体

AVFormatContext

格式上下文,连接多个Api的桥梁、读多媒体流的时候需要将 AVFormatContext 指针变量作为参数传递,获取相关数据。

AVStream

从 AVFormatContext 可以获取到AVStream->音频流、视频流

AVPacket

从 AVStream 可以获取到对应的 AVPacket,AVPacket 的组成内容是被压缩的帧。

AVFrame

从AVPacket 可以拿到具体的 1-n 个AVFrame

5. FFmpeg操作流数据的基本步骤

如下图所示:


FFmpeg操作流数据基本步骤

6.FFmpeg 打印音/视频信息(Meta)

7.1 获取视频文件 音视频流信息代码->mediainfo.c

#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;
}

7.2 展示音/视频信息

FFmpeg 打印音/视频信息(Meta)

当前目录下音视频文件如下所示:


音视频文件

8.抽取音频数据

8.1 抽取音频数据涉及到的Api

抽取音频数据涉及到的Api
  • av_init_packet() 初始化数据包
  • av_find_best_stream() 找寻上下文中最优的流
  • av_read_frame() / av_packet_unref() 读取包数据、

注意:在这几天阅读源码的过程中,我注意到av_read_frame()读出来的数据是 AVPacket 类型的变量,这使我很疑惑,最终我在讲师的课程中得到了答案,是因为FFmpeg的历史问题。

8.2 实战从音视频文件中抽取音频数据

#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;
}

运行结果如下所示

output 结果

播放音频效果如下所示

播放效果

采用如下命令 ffplay test.aac 即可播放从 test.mp4 中抽取的音频文件。

完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容

  • 教程一:视频截图(Tutorial 01: Making Screencaps) 首先我们需要了解视频文件的一些基...
    90后的思维阅读 4,695评论 0 3
  • FFmpeg 介绍 FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LG...
    Y了个J阅读 11,280评论 0 28
  • 版本记录 前言 FFMPEG是一个多媒体转码、音视频流化传输的一个第三方开源框架,并且这个第三方库是基于C语言的。...
    刀客传奇阅读 9,735评论 3 54
  • ffmpeg是一个非常有用的命令行程序,它可以用来转码媒体文件。它是领先的多媒体框架FFmpeg的一部分,其有很多...
    城市之光阅读 6,774评论 3 6
  • 我想有一天我也会轻声地对你唱我最亲爱的,你过得怎么样,没我的日子,你别来无恙。 看到你跟我说,以后不再联系的时候,...
    勒音阅读 308评论 0 0