[关闭]
@ltlovezh 2020-09-18T09:01:55.000000Z 字数 1791 阅读 59

Android多媒体框架之MediaCodec

Android多媒体框架 MediaCodec


关键技术

处理EOS

当没有输入数据时,必须通过queueInputBuffer携带BUFFER_FLAG_END_OF_STREAM标志位通知Codec,BUFFER_FLAG_END_OF_STREAM可以与最后一个合法输入帧一起输入,也可以单独输入一个带BUFFER_FLAG_END_OF_STREAM的空帧,此时带BUFFER_FLAG_END_OF_STREAM的空帧的pts值会被忽略。

然后,Codec会继续输出数据,直到没有更多输出数据了。

当没有输出数据时,Codec通过dequeueOutputBuffer为BufferInfo设置BUFFER_FLAG_END_OF_STREAM,BUFFER_FLAG_END_OF_STREAM可以与最后一个合法输出帧一起输出,也可以与一个空帧一起输出,此时带BUFFER_FLAG_END_OF_STREAM的空帧的pts值应该被忽略。

但是实际用下来发现:非空EOS输出帧的pts有时也为0,此时这个输出帧也应该被丢弃。

控制关键帧数量

MediaCodec提供了KEY_I_FRAME_INTERVAL控制关键帧频率,表示多少秒一个I帧。实际测试下来发现,必须通过OpenGLMediaCodec.createInputSurface创建的Surface输入帧,才能达到控制关键帧数量的目的。

此外,MediaCodec还提供了强制I帧的参数MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME,甚至可以编码出一个全部是关键帧的视频。

  1. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  2. val params = Bundle()
  3. params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0)
  4. videoEncoder.setParameters(params)
  5. }

输入输出的时间戳单位是毫秒

通过MediaCodec解码音频和视频时,输入的PTS单位必须是微秒,FFmpeg av_read_frame返回的AVPacket,时间戳是基于AVStream的time_base,所以必须将AVPacket的pts和dts从AVStream的time_base转换到time_base = 1000000,再送给MediaCodec;否则会出现异常情况,比如:多个AVPacket的pts间隔非常短,系统会认为视频帧率太高,解码器已经超负荷,导致硬解码器(OMX.qcom.video.decoder.avc)创建失败,转成系统内部的google软解码器(OMX.google.h264.decoder),从而导致解码速度大幅下降。

硬解码丢弃视频帧

用Surface作为硬解码的输出时,可以选择丢弃解码后的视频帧,即可以根据具体场景,决定是否把解码后的视频帧更新到Surface上。比如:Seek的场景,因为要从I帧解码到目标时间戳,但是中间的解码帧并不需要渲染,那么就可以在解码时直接丢弃这些中间帧,减少GPU负担。

  1. // render为true则表示把视频帧更新到Surface,为false则表示丢弃这个视频帧,即Surface的画面不会更新
  2. public final void releaseOutputBuffer(int index, boolean render);

编码参数

Android7.0及以上才支持设置Profile。

多实例问题

硬解码首帧延迟

硬解码需要送入大概5-7帧左右,才能吐出raw数据,会有200-300ms延时?这个可以优化吗

特殊Case

某些手机上MediaCodec Buffer编码比Surface编码更快

ViVo x21(高通665)上,720P 30S的视频,MediaCodec Surface编码耗时约为23S,MediaCodec Buffer编码耗时约为12S左右。参考:ViVo x21(高通665)合成导出速度分析和优化Buffer硬编码技术方案

相关文章

  1. Android MediaCodec原理剖析及填坑
  2. MediaCodec 解码视频快速取帧
  3. Android MediaCodec硬编码进阶指南
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注