FFmpeg手记

音频的一些基本概念

  • AVSampleFormat 音频sample的存储格式
  • channel layout 各个通道存储顺序
  • avcodec_send_packet和avcodec_receive_frame 音频解码

采样格式是AVSampleFormat:

enum AVSampleFormat {
    AV_SAMPLE_FMT_NONE = -1, 
    AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
    AV_SAMPLE_FMT_S16,         ///< signed 16 bits
    AV_SAMPLE_FMT_S32,         ///< signed 32 bits
    AV_SAMPLE_FMT_FLT,         ///< float
    AV_SAMPLE_FMT_DBL,         ///< double

    AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
    AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
    AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
    AV_SAMPLE_FMT_FLTP,        ///< float, planar
    AV_SAMPLE_FMT_DBLP,        ///< double, planar

    AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
};

位数越大,声音表达越准确,除此之外还有采用频率,人耳可识别的范围在20Hz~20KHz,采样频率是指每秒对连续音频信号采集
多少次数据,每次数据的位数就是AVSampleFormat,常用的采样频率有22050、44100和44800,再高的话人耳也听不出差别了

多一个通道存储多占一倍数据,多声道的存储方式有两种:planner和packed,第一种是平面存储,每个通道占用一个平面存
储,packed是交叉存储,每个通道数据依次交叉存储,依次进行;但是如何存储呢?如planner哪个平面和哪个通道对应呢,
packed的各个通道是按何种排序方式存储的呢?这就需要channel_layout布局说明?如何存储可参考 FFmpeg中音频的通道channels和channel_layout等概念

音视频时钟概念
各种时钟
声音中的采样率、通道等概念


音视频同步概念和原理

每帧音频可能有多个采样点,AAC格式数据一帧有1024个采样点,每帧耗时1024*1000/sample_rate,如果sample_rate为44.10Khz,大约耗时22ms

每帧视频帧包含一个画面,耗时等于1/fps,fps为码率,一帧约40ms

上面计算的耗时可以理解为PTS,所以在播放时根据时间轴,依次播放耗时短的在长的视频帧,如在22ms播放第一个音频帧,40ms播放第一个视频帧;但是这会有一个问题: 如声卡在播放一个22ms的音频帧的耗时和正常时间轴走完可能不同,这样可能会出现这样的情况,22ms播放第一个音频帧,到40ms播放视频帧时,音频帧理论应该播完,但是实际情况可能声卡的一些机制可能音频未播完,依次播放累加,会导致音视频不同步, 如何解决?

回调反馈机制,给声卡固定的buffer,这个buffer装载固定耗时的音频数据,当buffer播完,就知道这个固定耗时,依次为基准来播放视频,就会解决该问题;更多请点击下面的原理

音视频时钟同步原理


HEVC比H264高级的地方

-可变量的尺寸转换(从4×4 到32×32)

-四叉树结构的预测区域(从64×64到4×4)

-基于候选清单的运动向量预测。

-多种帧内预测模式。

-更精准的运动补偿滤波器。

-优化的去块、采样点自适应偏移滤波器等。
  
视频编码历史
HEVC和H264对比


Android音频播放器SLES

SLES播放简介
SLES代码简介


Android视频播放设备相关

openGL EGL
腾讯资料openGL EGL


struct AVFormatContext

struct AVInputFormat *iformat:输入数据的封装格式

AVIOContext *pb:输入数据的缓存

unsigned int nb_streams:视音频流的个数

AVStream **streams:视音频流

char filename[1024]:文件名

int64_t duration:时长(单位:微秒us,转换为秒需要除以1000000int bit_rate:比特率(单位bps,转换为kbps需要除以1000)

AVDictionary *metadata:元数据

AVCodecParameters *codecpar; 解码器参数

int64_t skip_initial_bytes;  解码时跳过开头的字节数,偏移;编码时设置无效

struct AVOutputFormat *oformat;  编码时有效

struct AVCodecContext

gop_size : the number of pictures in a group of pictures, or 0 for intra_only,即帧率 

struct AVPacket

解码之前的数据和关于这些数据的一些附加信息,如显示时间戳(pts)、解码时间戳(dts)、数据时长,所在媒体流的索引等。对于视频来说,AVPacket通常包含一个压缩的Frame,而音频(Audio)则有可能包含多个压缩的Frame。并且,一个Packet有可能是空的,不包含任何压缩数据,只含有side data(side data>,容器提供的关于Packet的一些附加信息。例如,在编码结束的时候更新一些流的参数)。AVPacket的大小是公共的ABI(public ABI)一部分,这样的结构体在FFmpeg很少,由此也可见AVPacket的重要性。它可以被分配在栈空间上(可以使用语句AVPacket packet; 在栈空间定义一个Packet ),并且除非libavcodec >和 libavformat有很大的改动,不然不会在AVPacket中添加新的字段。
av_packet_unref使AVPacket变得无效

// 用来管理data指针引用的数据缓存的
        // 为NULL时,那么数据包是不计数的
    AVBufferRef *buf;

        // 显示时间戳, 对应时间戳AVStream->time_base单元; 这个时间点, 解压缩的数据包将被提交给用户
    // 如果时间不被存储在文件里, 则可以写成AV_NOPTS_VALUE
    // pts必须大于或等于dts, 因为显示不能在解压缩之前被发生, 除非有人想查看十六进制存储。
        // 某些格式误用了这个名词dts或者是pts/cts那是意味着别的意思, 所以时间戳必须在被存储到AVPacket之前转换成真正的PTS/DTS。
    int64_t pts;

        // 解码时间戳, 对应时间戳AVStream->time_base单元; 这个时间点, 数据包被解码
        // 如果时间不被存储在文件里, 则可以写成AV_NOPTS_VALUE
    int64_t dts;

        // 存储的数据,指向一个缓存,这是AVPacket实际的数据
    uint8_t *data;

        // 数据的大小
    int size;

        // 标识该AVPacket所属的音频/视频流的索引
    int stream_index;

        // 一个AV_PKT_FLAG标识值, 最低为置1表示关键帧
    int flags;

        // 容器可以提供的附加数据
        // 包可以包含几种AVPacketSideDataType类型的侧信息
    AVPacketSideData *side_data;

        // 附加信息元素
    int side_data_elems;

    // 数据的时长,以所属媒体流的时间基准为单位
    int64_t duration;

        // 该数据在媒体流中的字节偏移量
    int64_t pos;

        // 该字段不再使用
#if FF_API_CONVERGENCE_DURATION
    attribute_deprecated
    int64_t convergence_duration;
#endif

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 *codecpar; 编码器的名字和id,视频宽高等

int disposition; /**< AV_DISPOSITION_* bit field 以字节码来显示Packet的标志量*/

enum AVDiscard discard;  //丢弃策略,哪些stream可以直接丢弃,无须demux,AVDiscard有许多丢弃策略,直接丢弃,0大小packet丢弃,根据需要去选择

判断流是否是一个只包含封面的图片流
pStream->disposition & AV_DISPOSITION_ATTACHED_PIC,了解更多点击这里


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差不太多)

AVFrame

解码后的一帧数据

uint8_t *data[AV_NUM_DATA_POINTERS]:解码后原始数据(对视频来说是YUV,RGB,对音频来说是PCM)

int linesize[AV_NUM_DATA_POINTERS]:data中“一行”数据的大小。注意:未必等于图像的宽,一般大于图像的宽。

int width, height:视频帧宽和高(1920x1080,1280x720...int nb_samples:音频的一个AVFrame中可能包含多个音频帧,在此标记包含了几个

int format:解码后原始数据类型(YUV420,YUV422,RGB24...int key_frame:是否是关键帧

enum AVPictureType pict_type:帧类型(I,B,P...)

AVRational sample_aspect_ratio:宽高比(16:94:3...)

int64_t pts:显示时间戳

int coded_picture_number:编码帧序号

int display_picture_number:显示帧序号

int8_t *qscale_table:QP表

uint8_t *mbskip_table:跳过宏块表

int16_t (*motion_val[2])[2]:运动矢量表

uint32_t *mb_type:宏块类型表

short *dct_coeff:DCT系数,这个没有提取过

int8_t *ref_index[2]:运动估计参考帧列表(貌似H.264这种比较新的标准才会涉及到多参考帧)

int interlaced_frame:是否是隔行扫描

uint8_t motion_subsample_log2:一个宏块中的运动矢量采样个数,取log的

AVRational


typedef struct AVRational{
        int num; ///< Numerator 分子
        int den; ///< Denominator 分明
} AVRational;    ///* 就是一个分数


AVClass和AVOption

AVClass与AVOption常用语FFmpeg内部结构体的赋值,可以使用统一的变量名查找并赋值,而不必了解结构体;一般AVClass是ffmpeg结构体的第一个成员,AVClass描述结构体的基本信息,AVClass的成员AVoption指向一个全局AVoption数组,这个数组保存当前结构体的所有成员以及类型帮助等,用于设置结构体成员


播放业务逻辑


ffmpeg初始化

  1. avformat_alloc_context分配流媒体全局上下文
  2. 设置打开输入中断回调
  3. avformat_open_input打开流媒体
  4. avformat_find_stream_info查询流媒体
  5. 遍历或者av_find_best_stream查找音视频流

读取packet逻辑

  1. 判断是否暂停/继续播放
  2. 判断是否停止abort,退出读包流程
  3. 判断是否有拖拽视频进度、快进快退等
  4. 判断是否有读取封面包任务attachment
  5. 判断缓冲区存放的未解码数据是否到达上限阈值,决定是否继续读取AVPacket
  6. 进行AVPacket包读取,并判断当前读取的packet时间是否小于播放时间,在播放范围内
  7. 将packet送入解码器解码

视频解码流程

  1. 提前分配好缓存AVPacket、AVFrame以及stream的时基timebase和帧率
  2. 判断用户是否abort停止
  3. 判断AVPacket队列是否缓存了Packet数据
  4. 读取packed,并send进入解码器解码
  5. 判断解码是否成功,失败直接重新下一轮,成功则receive接收Frame
  6. 更新Frame的pts,计算此pts是否慢于主时钟,并且在一个阈值内
  7. 是就丢弃,否就不丢弃
  8. 将frame数据更新到opengl的纹理上去

音频解码流程

  1. 判断是否有缓存音频数据AVPacket,有就直接送去解码,没有就从Packet队列中取出AVPacket
  2. avcodec_send_packet送入解码器
  3. 判断返回值,小于0并且是AGAIN状态,缓存此Packet,小于0释放引用并continue;
  4. 大于等于0 avcodec_receive_frame读取解码数据
  5. 判断返回值,小于0释放引用并continue;大于0更新frame的pts为秒

音频重采样流程

为何要进行重采样?因为解码器解码后的音频流在格式上,比如采样率、采样格式、通道、通道layout等存在不一致,音频播放器无法进行播放,这个时候就需要用到重采样进行音频格式转换,转换流程如下:

  1. 判断解码后的音频AVFrame与我们设定播放音频在采样率、通道、采样格式等方面一致与否,不一致则需要转换
  2. 设置转换需要的参数,主要是源和目的的layout、采样率以及采样格式,并获得重采样上下文
  3. 初始化重采样上下文
  4. 通过源AVFrame的采样点,计算出转换后需要的内存大小空间,空间可以稍稍大点,不影响转码,计算过程如下:
int out_count = av_rescale_rnd(swr_get_delay(swrCtx, in->sample_rate) + frame->nb_samples, out->freq, in->sample_rate,AV_ROUND_UP);

int out_size = av_samples_get_buffer_size(NULL, out->channels, out_count, out->fmt, 0);
//快速分配内存
av_fast_malloc(&resampleBuffer, &resampleSize, out_size);
  1. 开始转码swr_convert
  2. 转码完成后需要更新时间戳audioClock=pts+这么多的采样点,送入播放器之前还要在减去这些未播放完成的数据;看着两个地方:
//更新时钟
//解码时间戳加这么 nb_samples个采样点的时间
audioClock = frame->pts * av_q2d((AVRational){1, frame->sample_rate}) +
                (double)frame->nb_samples / frame->sample_rate;

//再次更新
//乘2是因为有两个通道 audioClock在audioFrameResample更新了播放解码后数据的时间,这里要减去这些数据,还没有播放的数据
//audio_hw_buf_size硬件缓冲区缓存数据,bytes_per_sec是一秒播放的字节数
sync->updateAudioColock(audioClock - (double)(2 * audioState->audio_hw_buf_size + audioState->writeBufferSize) / audioState->audioParamsTarget.bytes_per_sec,
audioState->audio_callback_time / 1000000.0);

音频播放流程

前提,音频播放器注册Mediplayer的回调

  1. 不停循环音频缓冲区队列,如果队列长度小于我们配置的长度,就回调MediaPlayer
  2. MediaPlayer中调用音频重采样器的方法
  3. 重采样器中就从音频解码器获取一帧解码后的数据
  4. 在查看数据是否需要重采样,逻辑如上的重采样逻辑
  5. 把数据返回给播放器
  6. 播放器将数据入队列到播放器中

编码流程

avformat_alloc_output_context2()
avfomat_write_header() //写入文件头
avcodec_send_frame()/avcodec_receive_packet() //编码
av_write_frame()/av_interleaved_write_frame() //写入帧
av_write_trailer() //写入文件尾


编解码过程中用到的时间参数

pts:显示时间戳,编解码时需要自行处理以及转换
dts:解码时间戳,无须自己处理,有解码器自行处理
time_base:ffmpeg中时间的最小单位,任何时间戳乘上time_base才转换位具体的时间刻度;ffmpeg有三种时间基;
AVStream的时间基,目前不知道用什么用,应该是在该协议层会使用到;
AVCodec编码层的时间基,一般为帧率的倒数,编码是一帧帧的去编码,这个是最小的度量单位,应该是这么决定的
FFmpeg的内部时间基,供FFmpeg的内部api使用;
上面说到pts在编解码中需要我们自行转换,转换也是通过FFmpeg的内部api来转换:

为了保证时间的精度,时基一般用分数表示:AVRational结构体内部由分子,分母构成,以下av_q2d就是把分数形式转换为double类型

  • static inline double av_q2d(AVRational a);

把a从bg时间基转换为cq时间基的值

  • int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)

将AVPacket中各种时间值从一种时间基转换为另一种时间基

  • void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst);

AVrational赋值操作

  • static av_always_inline AVRational av_inv_q(AVRational q);

将具体的时间值从一种时间基转换为另一种时间基

  • int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
    对于编码来说:
// 编码
avcodec_send_frame(enc_ctx, frame);
avcodec_receive_packet(enc_ctx, packet);

// 时间基转换
packet.stream_index = out_stream_idx;
enc_ctx->time_base = av_inv_q(dec_ctx->framerate);
av_packet_rescale_ts(&opacket, enc_ctx->time_base, out_stream->time_base);

// 将编码帧写入输出媒体文件
av_interleaved_write_frame(o_fmt_ctx, &packet);

对于解码来说:

// 从输入文件中读取编码帧
av_read_frame(ifmt_ctx, &packet);

// 时间基转换
int raw_video_time_base = av_inv_q(dec_ctx->framerate);
av_packet_rescale_ts(packet, in_stream->time_base, raw_video_time_base);

// 解码
avcodec_send_packet(dec_ctx, packet)
avcodec_receive_frame(dec_ctx, frame);

码率控制

码率控制就是对编码输出的速率控制,单位时间内输出的字节数kbps(kb per second);码率越大,视频越接近原始大小,图像越清晰;码率越低,压缩程度越大,图像失真越厉害;码率控制的目的在于 尽可能用最低的码率来达到图像最小的可以接受的失真

CQP固定质量控制(Constant QP)

ffmpeg -i input -c:v libx264 -qp 23 output
通过控制视频帧中每个宏块的压缩量,QP越大压缩月大,质量越低,图像越失真,在X264中QP的取值在0~51

CBR固定码率模式(Constant bit rate)

ffmpeg -i input -c:v libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v 1M -minrate 1M -maxrate 1M -bufsize 2M output
固定码率,无论视频运动量多还是低的情况,都会是相同的码率进行输出;可能出现浪费带宽的情况;此种控制方式图像质量得不到保证,图像复杂,可能会失真;图像简单,图像正常

2-PASS ABR

ffmpeg -i input -c:v libx264 -b:v 1M -pass 2 output.mp4
允许编码器进行两次(或两次以上)传递,使其能够及时估计前方的情况。它可以在第一轮计算编码帧的成本,然后在第二轮更有效地利用可用的比特。这确保了在一定的比特率约束下,输出质量是最好的

VBR

动态码率,根据图像内容,当图像包含场景运动时,码率变大,保证图像质量;图像静止时,码率变低,节省带宽

ABR平均码率模式(Average bit rate)

ffmpeg -i input -c:v libx264 -b:v 1M output
ABR也称Safe VBR,举例来讲,当你设置一个码率后,编码器会将大部分图像用你的设置的码率来进行编码输出,少部分图像用于优化更高的码率输出;比VBR更合理

总结

VBR适合于媒体存储,而不是网络传输
媒体传输时,建议选择ABR,压缩比VBR快,更适合传输

ffmpeg中如何设置码率

通过设置AVCodecContext中的变量可以达到控制码率的目的:

  1. 通过设置bit_rate,则编码器默认采用ABR的方式来控制码率输出
    AVCodecContext->bit_rate = bpsValue;
  2. 如果什么都没做,则默认采用CRF(固定质量因子,cr=23),控制方法类似于CQP
  3. 也可以通过设置AVCodecContext的priv_data来设置不同的模式
    //crf控制模式
    av_opt_set(pCodecCtx->priv_data,“crf”,crf,AV_OPT_SEARCH_CHILDREN);

//qp控制模式
av_opt_set(pCodecCtx->priv_data,"",crf,AV_OPT_SEARCH_CHILDREN);

同时,如果编码采用H264、H265编码标准,还可以设置如下:

AVDictionary *dictParam = 0;
av_dict_set(&dictParam,"preset","medium",0);
av_dict_set(&dictParam,"tune","zerolatency",0);
av_dict_set(&dictParam,"profile","main",0);
avcodec_open2(pCodecCtx, pCodec,&dictParam);

preset的参数主要调节编码速度和质量的平衡,有ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow、placebo这10个选项,从快到慢。

tune的参数主要配合视频类型和视觉优化的参数,或特别的情况。如果视频的内容符合其中一个可用的调整值又或者有其中需要,则可以使用此选项,否则建议不使用
tune的值有: film: 电影、真人类型;

animation: 动画;

grain: 需要保留大量的grain时用;

stillimage: 静态图像编码时使用;

psnr: 为提高psnr做了优化的参数;

ssim: 为提高ssim做了优化的参数;

fastdecode: 可以快速解码的参数;

zerolatency:零延迟,用在需要非常低的延迟的情况下,比如电视电话会议的编码。

profile,对应四种H264画质级别。每个profile支持一组特定的编码功能

1. BP-Baseline Profile

基本画质,支持I/P帧,无B帧。只支持无交错(Progressive)和CAVLC

主要用于可视电话、会议电视、无线通信等实时视频通信。

2. EP-Extended Profile

进阶画质,支持I/B/P/SP/SI帧。只支持无交错(Progressive)和CAVLC

主要用于流媒体服务。

3. MP-Main Profile

主流画质,支持I/B/P。只是无交错(Progressive)和交错(Interlaced),支持CAVLC和CABAC。

主要用于电视广播和视频存储。

4. HP-High profile

高级画质, 在mainProfile的基础上增加了8x8内部预测、自定义量化、 无损视频编码和更多的YUV格式。

H.264 FRExt(即:FidelityRange Extensions)扩展部分(Amendment),包括High profile(HP)、High10 profile(Hi10P)、High 4:2:2 profile(Hi422P)、High4:4:4 profile(Hi444P)4个profile。H.264 Baseline profile、Extended profile和Main profile都是针对8位样本数据、4:2:0格式的视频序列,FRExt将其扩展到8~12位样本数据,视频格式可以为4:2:0、4:2:2、4:4:4,设立了High profile(HP)、High 10profile(Hi10P)、High 4:2:2 profile(Hi422P)、High 4:4:4 profile(Hi444P) 4个profile,这4个profile都以Main profile为基础。在相同配置情况下,Highprofile(HP)可以比Mainprofile(MP)节省10%的码流量,比MPEG-2 MP节省60%的码流量,具有更好的编码性能。


libyuv库

在视频编解码的时候你可能还会用到libyuv库,一款可以高效的图像格式转换、裁剪、旋转镜像的库,Google开发的,用法自己去看,这里讲我们需要用到的;后置摄像头摄像时一般不需要自己对图像进行处理,但是前置摄像头拍摄时,图像处于上下颠倒,左右镜像的情况,我们可以使用mirror对图像数据镜像处理,也可以给图像设置一个旋转角度,播放时对读取旋转角度处理即可,以下是我处理前置摄像头的操作:

  1. 编码之前,视频编码的stream,设置角度
av_dict_set(&stream->metadata, "rotate", "180", 0);
  1. 编码时,将视频数据左右镜像处理
// I420 mirror.
LIBYUV_API
int I420Mirror(const uint8* src_y, int src_stride_y,
               const uint8* src_u, int src_stride_u,
               const uint8* src_v, int src_stride_v,
               uint8* dst_y, int dst_stride_y,
               uint8* dst_u, int dst_stride_u,
               uint8* dst_v, int dst_stride_v,
               int width, int height);

I420Mirror就是libyuv库的方法,该方法支持水平竖直镜像,width和height图像原始宽高时,水平镜像;height传递负height,竖直镜像,并且水平也给你弄镜像 了,这是个败笔;不能同时水平竖直镜像,你不得不掉两遍该方法
除此之外,libyuv的scale处理裁剪之外,width和height传递负值,也能实现镜像功能

以上是我的编码操作;当然你也可以同时设置旋转和镜像操作,在播放器端,在读取出来,opengl实现镜像即可


FFmpeg API

  • double av_gettime_relative()
    获取当前时间微秒

  • avcodec_flush_buffers(AVCodecContext *avctx)
    刷线编解码上下文中的缓冲区,在重新打开编解码器、定位视频时,必须要重新刷线缓冲区,否则会收到之间的编解码数据,错误的数据帧

  • void av_format_inject_global_side_data(AVFormatContext *s) 将s下的每个AVStream流的side_data置为1

  • int av_get_channel_layout_nb_channels(uint64_t channel_layout) 根据通道的layout返回通道的个数

  • int64_t av_get_default_channel_layout(int nb_channels) 根据通道的个数返回默认的layout

  • int av_get_channel_layout_channel_index(uint64_t channel_layout,uint64_t channel) 返回通道在layout中的index,也就是某一通道在layout的存储位置

  • int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples,
    enum AVSampleFormat sample_fmt, int align)
    用于音频计算,把一系列的音频数据保存起来,需要花费多少空间
    首先,音频数据可以是多个通道的,如左右声道,存储方式和YUV类似,分平面存储planner和打包packed存储
    linesize,即一个平面的长度,packed存储方式看成一个平面,注意参数linesize是指针,执行后他是一个通道的长度;而函数返回值是所有通道的长度和
    nb_channels:通道数
    nb_samples:采样点数
    sample_fmt:采样点存储方式
    align:0,提供冗余数据,各通道数据长度相同;1-不冗余,音频数据真实大小

  • int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd);
    不同时间基转换,计算转换后的值大小,内部大致原理为a*b/c,是由c时基转到b时基下,最后取整依靠rnd来取,rnd取值有

enum AVRounding {
AV_ROUND_ZERO = 0, ///< Round toward zero. 趋近于0
AV_ROUND_INF = 1, ///< Round away from zero. 趋远于0
AV_ROUND_DOWN = 2, ///< Round toward -infinity. 趋于更小的整数
AV_ROUND_UP = 3, ///< Round toward +infinity. 趋于更大的整数
AV_ROUND_NEAR_INF = 5, ///< Round to nearest and halfway cases away from zero. 四舍五入,小于0.5取值趋向0,大于0.5取值趋远于0
AV_ROUND_PASS_MINMAX = 8192, ///< Flag to pass INT64_MIN/MAX through instead of rescaling, this avoids special cases for AV_NOPTS_VALUE
}

  • av_malloc和av_mallocz的区别在于后者分配内存后,将内存区域初始化为0

  • void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size);
    min_size为我们需要分配的内存大小,实际内部分配的大小会比min_size大,执行完成后实际内存大小将赋值给size

  • AVFilter *avfilter_get_by_name(const char *name);
    根据名字查找过滤器

  • int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt,
    const char *name, const char *args, void *opaque,
    AVFilterGraph *graph_ctx);
    创建过滤器上下文AVFilterContext并用args初始化,args一般采用以下格式:

 snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
                                mWidth, mHeight, mInputPixelFormat, timeBase.num, timeBase.den,
                                ratio.num, ratio.den);
  • int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
    打开编码器,参数就不说;这里主要说一下打开失败的情况,返回值为负值小于0;一般来说是AVDictionary参数出错,导致用options中参数去设置codec设置失败导致
    我遇到了-22的返回情况,定位了半天发现是option参数写错了
    设置编码器编码速度:preset选项,可以配置value为ultrafast,superfast, veryfast, faster, fast, medium, slow, slower, veryslow,placebo;
    不知道什么是preset,请参考

# FFmpeg命令行

-i : 输入音视频文件

发布了148 篇原创文章 · 获赞 41 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/jackzhouyu/article/details/93465367