MediaExtractor、MediaMuxer 分离和合成 mp4

本文介绍了Android中利用MediaExtractor进行视频音频分离,以及MediaMuxer进行合成功能。在处理过程中,提到了MediaExtractor的setDataSource、getTrackCount等API,以及MediaMuxer的初始化、添加轨道和写入数据的方法。在实际操作中,遇到了presentationTimeUs计算的坑点,包括不能直接使用MediaExtractor.getSampleTime()以及在部分机型上MediaFormat.KEY_FRAME_RATE缺失的问题。为了解决这些问题,提出了根据帧率计算presentationTimeUs的方案。
摘要由CSDN通过智能技术生成
MediaExtractor

视音频分离器,将一些格式的视频分离出视频轨道和音频轨道。
主要流程,也是主要的 API:

  • setDataSource(String path):设置数据源
  • getTrackCount():获取通道数
  • getTrackFormat(int index):获取通道格式
  • readSampleData(ByteBuffer byteBuf, int offset):读取指定通道中的数据
  • getSampleTime():获取当前时间戳
  • advance():下一帧
  • release():释放资源
MediaMuxer

视音频合成器,将视频和音频合成相应的格式

  • MediaMuxer(String path, int format):初始化输出文件名称和输出文件的格式:mpeg4
  • addTrack(MediaFormat format):添加轨道,返回 track Index,会在 writeSampleData 中使用
  • start():开始合成文件
  • writeSampleData(int, ByteBuffer, MediaCodec.BufferInfo):写数据
  • stop():停止合成文件
  • release():释放资源
大体流程:

以下是分离 mp4 音频和视频的源码及注释:

    private String seperateMedia(String fileName, boolean isAudio) {
        String type = isAudio ? "audio/" : "video/";
        MediaExtractor mMediaExtractor = new MediaExtractor();;
        MediaMuxer mMediaMuxer = null;
        try {
            mMediaMuxer = new MediaMuxer(getSDPath() + "/" + fileName, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
            // 获取assert中的资源文件
            AssetFileDescriptor fileDescriptor = getAssetFileSource();
            // 设置资源文件
            mMediaExtractor.setDataSource(fileDescriptor.getFileDescriptor(), fileDescriptor.getStartOffset(), fileDescriptor.getLength());
            int mMediaIndex = 0;
            for (int i = 0; i < mMediaExtractor.getTrackCount(); i++) {
                //获取码流的详细格式/配置信息
                MediaFormat format = mMediaExtractor.getTrackFormat(i);
                String mine = format.getString(MediaFormat.KEY_MIME);
                // 查找音频:"audio/" 或者视频:"video/"的轨道
                if (mine.startsWith(type)) {
                    mMediaIndex = i;
                    break;
                }
            }
            // 选择感兴趣的轨道
            mMediaExtractor.selectTrack(mMediaIndex);
            // 获取通道格式,可以自己新建,但是有坑
            MediaFormat mediaFormat = mMediaExtractor.getTrackFormat(mMediaIndex);
            int muxerTrackIndex = mMediaMuxer.addTrack(mediaFormat);
            // 当采集视频的使用,需要获取帧率,音频轨道没有这个参数
            int framerate = isAudio ? 0 : mediaFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
            mMediaMuxer.start();

            ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);
            int readSize = 0;
            // writeSampleData需要BufferInfo参数
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            while((readSize = mMediaExtractor.readSampleData(byteBuffer, 0)) > 0) {
                bufferInfo.size = readSize;
                bufferInfo.flags = mMediaExtractor.getSampleFlags(); //设置为关键帧等
                bufferInfo.offset = 0;
                if (!isAudio) { // 时间戳,音频和视频的处理方式不一样
                    bufferInfo.presentationTimeUs += 1000 * 1000 / framerate;
                } else {
                    bufferInfo.presentationTimeUs = mMediaExtractor.getSampleTime();
                }
                mMediaMuxer.writeSampleData(muxerTrackIndex, byteBuffer, bufferInfo);
                Log.d("getSampleTime", "seperateMedia: " + mMediaExtractor.getSampleTime() );
                mMediaExtractor.advance(); 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
可以使用以下步骤从mp4视频文件中提取帧缩略图: 1. 使用MediaExtractor从mp4文件中提取视频轨道数据。 ```java MediaExtractor extractor = new MediaExtractor(); extractor.setDataSource(filePath); int trackCount = extractor.getTrackCount(); for (int i = 0; i < trackCount; i++) { MediaFormat format = extractor.getTrackFormat(i); String mime = format.getString(MediaFormat.KEY_MIME); if (mime.startsWith("video/")) { extractor.selectTrack(i); ByteBuffer inputBuffer = ByteBuffer.allocate(1024 * 1024); MediaCodec codec = MediaCodec.createDecoderByType(mime); codec.configure(format, null, null, 0); codec.start(); boolean isEOS = false; while (!isEOS) { int sampleSize = extractor.readSampleData(inputBuffer, 0); if (sampleSize < 0) { isEOS = true; } else { long presentationTimeUs = extractor.getSampleTime(); codec.queueInputBuffer(inputBuffer.position(), sampleSize, presentationTimeUs, 0, isEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); extractor.advance(); } MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US); while (outputBufferIndex >= 0) { Image image = codec.getOutputImage(outputBufferIndex); // 处理image,生成缩略图 codec.releaseOutputBuffer(outputBufferIndex, true); outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US); } } codec.stop(); codec.release(); extractor.unselectTrack(i); } } extractor.release(); ``` 2. 使用MediaCodec进行视频解码,获取每一帧的Image对象。 3. 将Image对象转换为Bitmap对象,生成缩略图。 ```java Image.Plane[] planes = image.getPlanes(); ByteBuffer buffer = planes[0].getBuffer(); int pixelStride = planes[0].getPixelStride(); int rowStride = planes[0].getRowStride(); int rowPadding = rowStride - pixelStride * width; Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888); bitmap.copyPixelsFromBuffer(buffer); bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height); ``` 4. 可以使用OpenGL对缩略图进行后期处理,如旋转、缩放等操作。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值