热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

NDK(4)初识FFMPEG

对于FFMPEG是只闻其名,不见其人,各个大厂比如QQ影音,Bilibili等等都是以它为基础的。(好多都是从雷神那里直接C

对于FFMPEG是只闻其名,不见其人,各个大厂比如QQ影音,Bilibili等等都是以它为基础的。(好多都是从雷神那里直接Copy的在此,致敬雷神)
对于如何编译FFMPEG,网上有很多方法,百度即可,我用linux编译的,mac编译遇到了一个问题,应该是环境的问题。所以mac没有编译出来,不过大家按照网上的一步一步来,基本没有问题的,除了个别莫名问题。
编译完成之后有这么几个so,这就是我们需要用的东西了。

1870221-1e53a0d65a88436e.png
image.png

-libavcodec :用于各种类型声音/图像编解码 (解码器)
-libavfilter :包含多媒体处理常用的滤镜功能
-libavformat :包含多种多媒体容器格式的封装、解封装工具(容器格式处理)
-libavutil :包含一些公共的工具函数;(工具函数)
-libswresample: 用于音频重采样和格式转换等功能
-libswscale:用于视频场景比例缩放、色彩映射转换
1870221-c9ec81ad42ca8ee7.png
image.png

这是FFMPEG的几个重要的结构体。
FFMPEG中结构体很多。最关键的结构体可以分成以下几类:

1、解协议(http,rtsp,rtmp,mms)
AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)

2、 解封装(flv,avi,rmvb,mp4)
AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。

3、 解码(h264,mpeg2,aac,mp3)
每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。

4、存数据
视频的话,每个结构一般是存一帧;音频可能有好几帧

解码前数据:AVPacket
AVPacket是存储压缩编码数据相关信息的结构体

解码后数据:AVFrame
一般用于存储原始数据

AVFrame:

一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM)

1.data[]

对于packed格式的数据(例如RGB24),会存到data[0]里面。

对于planar格式的数据(例如YUV420P),则会分开成data[0],data[1],data[2]...(YUV420P中data[0]存Y,data[1]存U,data[2]存V)

2.pict_type

包含以下类型:

enum AVPictureType { AV_PICTURE_TYPE_NONE = 0, ///};

3.sample_aspect_ratio
宽高比是一个分数,FFMPEG中用AVRational表达分数:

/** * rational number numerator/denominator */
typedef struct AVRational{ int num; ///} AVRational;

AVPacket

AVPacket是存储压缩编码数据相关信息的结构体
uint8_t *data:压缩编码的数据。

例如对于H.264来说。1个AVPacket的data通常对应一个NAL。

因此在使用FFMPEG进行视音频处理的时候,常常可以将得到的AVPacket的data数据直接写成文件,从而得到视音频的码流文件。

int size:data的大小

int64_t pts:显示时间戳

int64_t dts:解码时间戳

int stream_index:标识该AVPacket所属的视频/音频流。

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音频文件附带的专辑封面。

AVFormatContext

AVFormatContext是包含码流参数较多的结构体,AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。
struct AVInputFormat *iformat:输入数据的封装格式

AVIOContext *pb:输入数据的缓存

unsigned int nb_streams:视音频流的个数

AVStream **streams:视音频流

char filename[1024]:文件名

int64_t duration:时长(单位:微秒us,转换为秒需要除以1000000)

int bit_rate:比特率(单位bps,转换为kbps需要除以1000)

AVDictionary *metadata:元数据

AVCodecContext

enum AVMediaType codec_type:编解码器的类型(视频,音频...)

struct AVCodec *codec:采用的解码器AVCodec(H.264,MPEG2...)

int bit_rate:平均比特率

uint8_t *extradata; int extradata_size:针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)

AVRational time_base:根据该参数,可以把PTS转化为实际的时间(单位为秒s)

int width, height:如果是视频的话,代表宽和高

int refs:运动估计参考帧的个数(H.264的话会有多帧,MPEG2这类的一般就没有了)

int sample_rate:采样率(音频)

int channels:声道数(音频)

enum AVSampleFormat sample_fmt:采样格式

int profile:型(H.264里面就有,其他编码标准应该也有)

int level:级(和profile差不太多)

在这里需要注意:AVCodecContext中很多的参数是编码的时候使用的,而不是解码的时候使用的。

codec_type:


enum AVMediaType {AVMEDIA_TYPE_UNKNOWN = -1, ///};

sample_fmt:


enum AVSampleFormat {AV_SAMPLE_FMT_NONE = -1,AV_SAMPLE_FMT_U8, ///};

profile
在FFMPEG中型有以下几种,可以看出AAC,MPEG2,H.264,VC-1,MPEG4都有型的概念


#define FF_PROFILE_UNKNOWN -99
#define FF_PROFILE_RESERVED -100#define FF_PROFILE_AAC_MAIN 0
#define FF_PROFILE_AAC_LOW 1
#define FF_PROFILE_AAC_SSR 2
#define FF_PROFILE_AAC_LTP 3
#define FF_PROFILE_AAC_HE 4
#define FF_PROFILE_AAC_HE_V2 28
#define FF_PROFILE_AAC_LD 22
#define FF_PROFILE_AAC_ELD 38#define FF_PROFILE_DTS 20
#define FF_PROFILE_DTS_ES 30
#define FF_PROFILE_DTS_96_24 40
#define FF_PROFILE_DTS_HD_HRA 50
#define FF_PROFILE_DTS_HD_MA 60#define FF_PROFILE_MPEG2_422 0
#define FF_PROFILE_MPEG2_HIGH 1
#define FF_PROFILE_MPEG2_SS 2
#define FF_PROFILE_MPEG2_SNR_SCALABLE 3
#define FF_PROFILE_MPEG2_MAIN 4
#define FF_PROFILE_MPEG2_SIMPLE 5#define FF_PROFILE_H264_CONSTRAINED (1<<9) // 8&#43;1; constraint_set1_flag
#define FF_PROFILE_H264_INTRA (1<<11) // 8&#43;3; constraint_set3_flag#define FF_PROFILE_H264_BASELINE 66
#define FF_PROFILE_H264_CONSTRAINED_BASELINE (66|FF_PROFILE_H264_CONSTRAINED)
#define FF_PROFILE_H264_MAIN 77
#define FF_PROFILE_H264_EXTENDED 88
#define FF_PROFILE_H264_HIGH 100
#define FF_PROFILE_H264_HIGH_10 110
#define FF_PROFILE_H264_HIGH_10_INTRA (110|FF_PROFILE_H264_INTRA)
#define FF_PROFILE_H264_HIGH_422 122
#define FF_PROFILE_H264_HIGH_422_INTRA (122|FF_PROFILE_H264_INTRA)
#define FF_PROFILE_H264_HIGH_444 144
#define FF_PROFILE_H264_HIGH_444_PREDICTIVE 244
#define FF_PROFILE_H264_HIGH_444_INTRA (244|FF_PROFILE_H264_INTRA)
#define FF_PROFILE_H264_CAVLC_444 44#define FF_PROFILE_VC1_SIMPLE 0
#define FF_PROFILE_VC1_MAIN 1
#define FF_PROFILE_VC1_COMPLEX 2
#define FF_PROFILE_VC1_ADVANCED 3#define FF_PROFILE_MPEG4_SIMPLE 0
#define FF_PROFILE_MPEG4_SIMPLE_SCALABLE 1
#define FF_PROFILE_MPEG4_CORE 2
#define FF_PROFILE_MPEG4_MAIN 3
#define FF_PROFILE_MPEG4_N_BIT 4
#define FF_PROFILE_MPEG4_SCALABLE_TEXTURE 5
#define FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION 6
#define FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE 7
#define FF_PROFILE_MPEG4_HYBRID 8
#define FF_PROFILE_MPEG4_ADVANCED_REAL_TIME 9
#define FF_PROFILE_MPEG4_CORE_SCALABLE 10
#define FF_PROFILE_MPEG4_ADVANCED_CODING 11
#define FF_PROFILE_MPEG4_ADVANCED_CORE 12
#define FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE 13
#define FF_PROFILE_MPEG4_SIMPLE_STUDIO 14
#define FF_PROFILE_MPEG4_ADVANCED_SIMPLE 15

AVCodec

AVCodec是存储编解码器信息的结构体

const char *name&#xff1a;编解码器的名字&#xff0c;比较短

const char *long_name&#xff1a;编解码器的名字&#xff0c;全称&#xff0c;比较长

enum AVMediaType type&#xff1a;指明了类型&#xff0c;是视频&#xff0c;音频&#xff0c;还是字幕

enum AVCodecID id&#xff1a;
ID&#xff0c;为了识别二进制数据流
具有相同ID的两个解码器可以解码相同的流。
具有相同ID的两个编码器可以编码兼容的流。

*supported_framerates&#xff1a;支持的帧率&#xff08;仅视频&#xff09;

const enum AVPixelFormat *pix_fmts&#xff1a;支持的像素格式&#xff08;仅视频&#xff09;

const int *supported_samplerates&#xff1a;支持的采样率&#xff08;仅音频&#xff09;

const enum AVSampleFormat *sample_fmts&#xff1a;支持的采样格式&#xff08;仅音频&#xff09;

const uint64_t *channel_layouts&#xff1a;支持的声道数&#xff08;仅音频&#xff09;

int priv_data_size&#xff1a;私有数据的大小

  • enum AVMediaType type


enum AVMediaType {AVMEDIA_TYPE_UNKNOWN &#61; -1, ///};

  • enum AVCodecID id


enum AVCodecID {AV_CODEC_ID_NONE,/* video codecs */AV_CODEC_ID_MPEG1VIDEO,AV_CODEC_ID_MPEG2VIDEO, ///}

  • const enum AVPixelFormat *pix_fmts


enum AVPixelFormat {AV_PIX_FMT_NONE &#61; -1,AV_PIX_FMT_YUV420P, ///}

  • const enum AVSampleFormat *sample_fmts


enum AVSampleFormat {AV_SAMPLE_FMT_NONE &#61; -1,AV_SAMPLE_FMT_U8, ///};

每一个编解码器对应一个该结构体&#xff0c;查看一下ffmpeg的源代码&#xff0c;我们可以看一下H.264解码器的结构体如下所示&#xff08;h264.c&#xff09;&#xff1a;


AVCodec ff_h264_decoder &#61; {.name &#61; "h264",.type &#61; AVMEDIA_TYPE_VIDEO,.id &#61; CODEC_ID_H264,.priv_data_size &#61; sizeof(H264Context),.init &#61; ff_h264_decode_init,.close &#61; ff_h264_decode_end,.decode &#61; decode_frame,.capabilities &#61; /*CODEC_CAP_DRAW_HORIZ_BAND |*/ CODEC_CAP_DR1 | CODEC_CAP_DELAY |CODEC_CAP_SLICE_THREADS | CODEC_CAP_FRAME_THREADS,.flush&#61; flush_dpb,.long_name &#61; NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),.init_thread_copy &#61; ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),.update_thread_context &#61; ONLY_IF_THREADS_ENABLED(decode_update_thread_context),.profiles &#61; NULL_IF_CONFIG_SMALL(profiles),.priv_class &#61; &h264_class,
};

JPEG2000解码器结构体&#xff08;j2kdec.c&#xff09;


AVCodec ff_jpeg2000_decoder &#61; {.name &#61; "j2k",.type &#61; AVMEDIA_TYPE_VIDEO,.id &#61; CODEC_ID_JPEG2000,.priv_data_size &#61; sizeof(J2kDecoderContext),.init &#61; j2kdec_init,.close &#61; decode_end,.decode &#61; decode_frame,.capabilities &#61; CODEC_CAP_EXPERIMENTAL,.long_name &#61; NULL_IF_CONFIG_SMALL("JPEG 2000"),.pix_fmts &#61;(const enum PixelFormat[]) {PIX_FMT_GRAY8, PIX_FMT_RGB24, PIX_FMT_NONE}
};

下面简单介绍一下遍历ffmpeg中的解码器信息的方法&#xff08;这些解码器以一个链表的形式存储&#xff09;&#xff1a;
1.注册所有编解码器&#xff1a;av_register_all();

2.声明一个AVCodec类型的指针&#xff0c;比如说AVCodec* first_c;

3.调用av_codec_next()函数&#xff0c;即可获得指向链表下一个解码器的指针&#xff0c;循环往复可以获得所有解码器的信息。注意&#xff0c;如果想要获得指向第一个解码器的指针&#xff0c;则需要将该函数的参数设置为NULL。

AVIOContext

是FFMPEG管理输入输出数据的结构体

unsigned char *buffer&#xff1a;缓存开始位置

int buffer_size&#xff1a;缓存大小&#xff08;默认32768&#xff09;

unsigned char *buf_ptr&#xff1a;当前指针读取到的位置

unsigned char *buf_end&#xff1a;缓存结束的位置

void *opaque&#xff1a;URLContext结构体

在解码的情况下&#xff0c;buffer用于存储ffmpeg读入的数据。例如打开一个视频文件的时候&#xff0c;先把数据从硬盘读入buffer&#xff0c;然后在送给解码器用于解码。

分界线

从雷神那里把好多资料Copy了过来&#xff0c;为什么要列这么多呢?因为你不了解这些&#xff0c;基本是懵逼的。
我是先懵逼的敲几遍代码&#xff0c;熟悉一下流程&#xff0c;然后再来理解这些东西的含义&#xff0c;真心感觉NDK的东西&#xff0c;需要是啃出来的&#xff0c;真心是懵逼。



推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • Vagrant虚拟化工具的安装和使用教程
    本文介绍了Vagrant虚拟化工具的安装和使用教程。首先介绍了安装virtualBox和Vagrant的步骤。然后详细说明了Vagrant的安装和使用方法,包括如何检查安装是否成功。最后介绍了下载虚拟机镜像的步骤,以及Vagrant镜像网站的相关信息。 ... [详细]
  • 本文介绍了Android中的assets目录和raw目录的共同点和区别,包括获取资源的方法、目录结构的限制以及列出资源的能力。同时,还解释了raw目录中资源文件生成的ID,并说明了这些目录的使用方法。 ... [详细]
  • 手把手教你使用GraphPad Prism和Excel绘制回归分析结果的森林图
    本文介绍了使用GraphPad Prism和Excel绘制回归分析结果的森林图的方法。通过展示森林图,可以更加直观地将回归分析结果可视化。GraphPad Prism是一款专门为医学专业人士设计的绘图软件,同时也兼顾统计分析的功能,操作便捷,可以帮助科研人员轻松绘制出高质量的专业图形。文章以一篇发表在JACC杂志上的研究为例,利用其中的多因素回归分析结果来绘制森林图。通过本文的指导,读者可以学会如何使用GraphPad Prism和Excel绘制回归分析结果的森林图。 ... [详细]
  • 本文讨论了如何使用GStreamer来删除H264格式视频文件中的中间部分,而不需要进行重编码。作者提出了使用gst_element_seek(...)函数来实现这个目标的思路,并提到遇到了一个解决不了的BUG。文章还列举了8个解决方案,希望能够得到更好的思路。 ... [详细]
author-avatar
庄乐星
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有