用MediaCodec实现多段视音频的截取与拼接

视音频编辑中,对多段媒体素材进行截取和拼接是非常常见的操作。截取和拼接实际上是对媒体文件数据重新进行组合的过程。

    要实现这些功能,就需要对媒体文件进行编解码操作,即先解码要处理的媒体文件数据,然后再按照某种规则对这些数据进行编码,以生成我们所需的目标。


用MediaCodec实现多段视音频的截取与拼接_第1张图片

    Android提供的MediaCodec及其相关类为我们提供了所需的方法,这些类主要包括:MediaCodec、MediaExtractor、MediaMuxer、MediaFormat。

    MediaCodec用于创建视音频编解码器,通过它可以对视音频数据进行编解码操作,它是编解码功能的核心类。

    MediaExtractor相当于一个reader,它用于读取媒体文件,并提取出其中的视音频数据。

    MediaMuxer相当于一个writer,它用于将内存中的视音频数据写到文件中。

    MediaFormat即媒体格式类,它用于描述媒体的格式参数,如视频帧率、音频采样率等。

    对视音频文件进行截取与拼接的主要过程是:先创建视音频编解码器,再分别启动视频线程和音频线程,以分别对视音频数据进行解码、编码,最后将编码好的数据写入文件。

    主要逻辑如下,关键是三处线程同步的地方,具体原因稍后解释。

用MediaCodec实现多段视音频的截取与拼接_第2张图片

    再来了解一下这几个类的“脾气”。

    一般用MediaCodec创建编解码器时,使用的都是createDecoderByType方法,但在用三星PAD(API LEVEL 18, Android version 4.3)调试时发现,调用该方法创建音频编码器时会出错。故改为使用createByCodecName("OMX.SEC.aac.enc")创建音频编码器。估计这是一个API bug。

    configure decoder时,第一个格式参数要与源的格式相同,这里可以先通过extractor将源的格式读出来,再直接传给configure方法。configure encoder时,需设定几个必要的参数,具体请参考官方说明。

    当把全部要处理的数据灌给编解码器后,使用queueInputBuffer(inputBufIndex,0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM)方法来通知编解码器结束工作。之后,在OutputBuffer的bufferInfo中,将携带这个标志,通过判断是否有这个标志,我们就可以断定数据是否都已处理完成,以进行后续操作。再之后,dequeueOutputBuffer的返回都将是INFO_TRY_AGAIN_LATER。当然,如果你自己有办法在OutputBuffer中判断数据是否灌完,也可以不使用此标志。

    当数据灌完后,要使用releaseOutputBuffer,把缓冲区释放掉。否则,你会发现queueInputBuffer总是返回-1,因为没有空闲的缓冲区了。

    MediaExtractor用来读取源文件,给定文件路径后,便可将其中的视音频数据拿出来。关键是它的seekTo方法,它用于对读取数据的游标进行定位,可以定位到指定点前的最后一个sync点、指定点后的第一个sync点,或者与指定点最近的sync点。对于H.264数据,sync点可以认为是视频关键帧的时码位置。

    MediaMuxer的使用非常简单,先创建,再添加视音频轨,然后start,再writeSampleData,最后stop。需要注意的是,必需先添加完所有的视音频轨后,再去start,而不能先start,再试图去addTrack,否则会出错。这也是为什么在本文的逻辑中,视频线程需要去wait音频线程添加完音频轨后再继续的原因。

    需要注意的是,不能在启动完编解码器后,立即调用getOutputFormat企图addTrack,而应该在dequeueOutputBuffer后的INFO_OUTPUT_FORMAT_CHANGED中调用,否则会出错。

    当要向文件中同时写入视频和音频数据时,必需先writeSampleData所有视频数据,再写音频数据,或者反之,即二者必需连续调用writeSampleData,不能交叉调用,否则写出的文件会有问题。这也是本文中为何muxer启动后,音频线程需等待视频线程先写完数据,自己才能继续干活的原因。

    当所有的writeSampleData完成后,不要忘记调用stop和release,否则写出的文件也会有问题。

    最后还要注意,这几个类中,时间单位都是微秒,不要搞错了。

你可能感兴趣的:(用MediaCodec实现多段视音频的截取与拼接)