记录一下做视频解码的demo:
github地址:https://github.com/whcx/AudioVideoEncoderDecoder
存储权限申请: 当前项目targetSdkVersion 29,按之前方式动态申请权限 Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,无法在sdcard的共有目录创建文件。 可以使用context.getExternalFilesDir(type);获取应用私有的文件目录,即:/storage/emulated/0/Android/data/com.cxd.av/files; 或者,manifest中添加android:requestLegacyExternalStorage="true",依然使用共有目录。
1,使用MediaCodec解码视频文件,
主要代码在VideoPlayer.java,AVUtils.java中。
这里主要是获取到视频的yuv数据,并没有给出控件view显示视频到屏幕,用控件播放的demo后面整理下。
获取视频YUV数据,使用了两种方式,
1)使用 MediaCodec.Callback,这种异步的方式,填充待解码数据,然后获取解码后的数据,具体就是代码中的
updateVideoFrame() 方法。
2)同样是使用MediaCodec.Callback这种异步的方式,只是YUV数据的获取是通过Surface,使用这种方式的依据,请参考:https://developer.android.com/reference/android/media/MediaCodec
Codecs operate on three kinds of data: compressed data, raw audio data and raw video data. All three kinds of data can be processed using
ByteBuffer
, but you should use a
Surface
for raw video data to improve codec performance. Surface uses native video buffers without mapping or copying them to ByteBuffers; thus, it is much more efficient. You normally cannot access the raw video data when using a Surface, but you can use the
ImageReader
class to access unsecured decoded (raw) video frames. This may still be more efficient than using ByteBuffers, as some native buffers may be mapped into ByteBuffer#isDirect ByteBuffers. When using ByteBuffer mode, you can access raw video frames using the
Image
class and
getInput
/
OutputImage(int)
.
大意是使用surface使用了本地的video buffer可以提高解码的性能,这种情况可以使用ImageReader来获取视频帧。
具体代码是updateVideoFrameReaderAsync方法,获取视频帧的代码是
image = mImageListener.getImage((long)delay);
这里为什么要去强调使用异步的方式呢?因为我最初使用同步的方式即:
updateVideoFrameReaderSync(),在获取解码后的数据时,一直没能读取到流结束的标记,当然,在填充带解码数据的最后,我肯定是queueInputBuffer中加入了
MediaCodec.BUFFER_FLAG_END_OF_STREAM的。
3)就是视频帧YUV数据的提取:具体就是AVUtils中的
private byte[] getDataFromImage(Image image, int colorFormat),返回的字节数组就是YUV 数据,可以使用
dumpI420File,
dumpJpegFile保存到本地查看。
具体提取YUV的做法,可以去参考android cts测试的源码:android/cts/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
2,音频的解码,
音频的解码相对比较简单,实现音频解码除了播放声音外,还有一个重要的意思就是获取音频的时钟信息,方便做音视频同步。
具体代码:AudioPlayer.java,播放视频使用AudioTack.java