首页 > 其他分享 >使用 MediaCodec 在 Android 上进行硬解码

使用 MediaCodec 在 Android 上进行硬解码

时间:2024-04-23 17:37:45浏览次数:24  
标签:extractor format int 解码 codec MediaFormat MediaCodec Android

要使用 MediaCodec 在 Android 上进行硬解码,并获取 RGBA 数据,你可以按照以下步骤进行操作:

创建 MediaExtractor 对象并设置要解码的 MP4 文件路径:

MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(filePath);

根据需要选择音频或视频轨道:

int trackCount = extractor.getTrackCount();
int trackIndex = -1;
for (int i = 0; i < trackCount; i++) {
    MediaFormat format = extractor.getTrackFormat(i);
    String mime = format.getString(MediaFormat.KEY_MIME);
    if (mime.startsWith("video/")) {
        trackIndex = i;
        break;
    }
}
if (trackIndex >= 0) {
    extractor.selectTrack(trackIndex);
}

创建 MediaCodec 对象并配置解码器:

MediaFormat format = extractor.getTrackFormat(trackIndex);
String mime = format.getString(MediaFormat.KEY_MIME);
MediaCodec codec = MediaCodec.createDecoderByType(mime);
codec.configure(format, null, null, 0);
codec.start();

循环解码并获取 RGBA 数据:

MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
boolean isEOS = false;
while (!isEOS) {
    int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs);
    if (inputBufferIndex >= 0) {
        ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferIndex);
        int sampleSize = extractor.readSampleData(inputBuffer, 0);
        if (sampleSize < 0) {
            codec.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            isEOS = true;
        } else {
            long presentationTimeUs = extractor.getSampleTime();
            codec.queueInputBuffer(inputBufferIndex, 0, sampleSize, presentationTimeUs, 0);
            extractor.advance();
        }
    }

    int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, timeoutUs);
    if (outputBufferIndex >= 0) {
        ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferIndex);
        // 解码后的数据位于 outputBuffer 中,根据需要进行 RGBA 数据的提取和处理
        // outputBuffer 中的数据格式可能是 YUV 或其他格式,需要根据解码器设置的输出格式进行相应的转换
        codec.releaseOutputBuffer(outputBufferIndex, false);
    } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
        // 解码器输出格式已更改,可以通过 codec.getOutputFormat() 获取新的格式
    }
}

在上述代码中,你需要根据解码器输出的数据格式进行相应的转换,以获取 RGBA 数据。具体的转换流程和代码取决于解码器输出的数据格式和你的需求。

需要注意的是,硬解码的支持和性能可能因设备、Android 版本和视频编码格式的不同而有所差异。确保你的设备支持硬解码,并且适当处理解码器的输入和输出缓冲区。

以下是完整代码:

import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.util.Log;

import java.nio.ByteBuffer;

public class MP4Decoder {
    private static final int TIMEOUT_US = 10000;
    private static final String TAG = "MP4Decoder";

    public interface FrameCallback {
        void onFrameDecoded(byte[] rgbaData, int width, int height);
    }

    public static void decodeMP4(String filePath, FrameCallback callback) {
        new Thread(() -> {
            MediaExtractor extractor = new MediaExtractor();
            try {
                extractor.setDataSource(filePath);

                int trackIndex = selectVideoTrack(extractor);
                if (trackIndex < 0) {
                    return;
                }

                MediaFormat format = extractor.getTrackFormat(trackIndex);
                String mime = format.getString(MediaFormat.KEY_MIME);
                MediaCodec codec = MediaCodec.createDecoderByType(mime);
                codec.configure(format, null, null, 0);
                codec.start();

                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                boolean isEOS = false;
                while (!isEOS) {
                    int inputBufferIndex = codec.dequeueInputBuffer(TIMEOUT_US);
                    if (inputBufferIndex >= 0) {
                        ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferIndex);
                        int sampleSize = extractor.readSampleData(inputBuffer, 0);
                        if (sampleSize < 0) {
                            codec.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                            isEOS = true;
                        } else {
                            long presentationTimeUs = extractor.getSampleTime();
                            codec.queueInputBuffer(inputBufferIndex, 0, sampleSize, presentationTimeUs, 0);
                            extractor.advance();
                        }
                    }

                    int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);
                    if (outputBufferIndex >= 0) {
                        ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferIndex);
                        // 解码后的数据位于 outputBuffer 中,根据需要进行 RGBA 数据的提取和处理
                        // outputBuffer 中的数据格式可能是 YUV 或其他格式,需要根据解码器设置的输出格式进行相应的转换
                        byte[] rgbaData = convertToRGBA(outputBuffer, format);
                        int width = format.getInteger(MediaFormat.KEY_WIDTH);
                        int height = format.getInteger(MediaFormat.KEY_HEIGHT);
                        callback.onFrameDecoded(rgbaData, width, height);
                        codec.releaseOutputBuffer(outputBufferIndex, false);
                    } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                        // 解码器输出格式已更改,可以通过 codec.getOutputFormat() 获取新的格式
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                extractor.release();
            }
        }).start();
    }

    private static int selectVideoTrack(MediaExtractor extractor) {
        int trackCount = extractor.getTrackCount();
        int trackIndex = -1;
        for (int i = 0; i < trackCount; i++) {
            MediaFormat format = extractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("video/")) {
                trackIndex = i;
                break;
            }
        }
        if (trackIndex >= 0) {
            extractor.selectTrack(trackIndex);
        }
        return trackIndex;
    }

    private static byte[] convertToRGBA(ByteBuffer buffer, MediaFormat format) {
        // 根据解码器设置的输出格式进行 RGBA 数据的提取和处理
        // 这里只是一个示例,实际的转换过程可能会更复杂,取决于输出格式和需求
        int width = format.getInteger(MediaFormat.KEY_WIDTH);
        int height = format.getInteger(MediaFormat.KEY_HEIGHT);
        int remaining = buffer.remaining();
        byte[] rgbaData = new byte[remaining];
        buffer.rewind();
        buffer.get(rgbaData);
        Log.i(TAG, "convertToRGBA: count:" + width + "; " + height + "; " + remaining);
        // todo: 进行 YUV 到 RGBA 的转换
        return rgbaData;
    }
}

标签:extractor,format,int,解码,codec,MediaFormat,MediaCodec,Android
From: https://www.cnblogs.com/futureli/p/18153340

相关文章

  • Android Studio 蓝牙 示例代码(转)
    原文:https://blog.csdn.net/qq_40511184/article/details/122698077因为androidstudio升级,下面代码中的startactivityresult函数有变化,不能使用,需要更换为publicActivityResultLauncher<Intent>register;ActivityResultLauncher<Intent>startBlueTooth=registerForActi......
  • 关于Android Studio双击exe没有反应解决办法
    首先说原因,我的因为之前卸载了,然后重新下载后一直打不开,查看了很多博客找不到解决办法,最终在AndroidStudio双击exe没有反应解决办法_androidstudio双击没反应-CSDN博客这个博客中得到了启发 先说解决办法第一步,到AndroidStudio的bin目录下,查看studio.bat文件查看的话......
  • 解码返回Unicode编码的文本
    publicstaticvoidMain(string[]args){stringunicodeText="\\u6b22\\u8fce\\u56de\\u6765";stringdecodedText=Regex.Unescape(unicodeText);Console.WriteLine(decodedText);}Unicode是一个字符集,它为世界上几乎所有的......
  • Android进程创建流程-2
    一、java线程创建流程1.部分调用逻辑Runtime::AttachCurrentThread(constchar*thread_name,boolas_daemon,jobjectthread_group,boolcreate_peer)Runtime::Init(RuntimeArgumentMap&&runtime_options_in)//runtime.cc备注说"ClassLinkerneedsan......
  • android studio Edit Custom VM Options后无法启动
    异常描述:想要修改虚拟器的内存,就百度了方法,设置了Help——EditCustomVMOptions,然后AndroidStudio就无法启动了,直接弹这个弹窗:所以,建议大家写文,还是要有头有尾,该上图上图,不能啪啪几个字让人猜啊,容易误导人的啊啊啊!!!解决问题:按这个路径查找到更改的文件,C:\Users\XXXX\AppDat......
  • MAUI Android 透明状态栏/导航栏(也有叫沉浸式的)
    不说任何废话,上代码,不好用来打我Platforms/Android/MainActivity.csprotectedoverridevoidOnCreate(BundlesavedInstanceState){Google.Android.Material.Internal.EdgeToEdgeUtils.ApplyEdgeToEdge(Window,true);if(Operatin......
  • FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
    ​《FFmpeg开发实战:从零基础到短视频上线》一书的例程主要测试本地的音视频文件,当然为了安全起见,很多网络视频都采用了https地址。FFmpeg若要访问https视频,就必须集成第三方的openssl库,但编译FFmpeg时却默认关闭了openssl。为了让App能够播放采用https的在线视频,需要编译安装并启......
  • url编码和解码分析URLEncoder.encode和URLDecoder.decode
    url编码和解码分析1.Get请求会将参数做默认的url解码操作,接口接收到的值是Get解码后的值。2.可以将Get操作修改成Post操作,这样不会url解码。可以在接口中做url解码。3.在多次传递参数的过程中,无需反复的编码(或者加了空格,加了换行),否则会将整个字符串错乱了。(/%2F%252F)......
  • Appium 实现APP的UI自动化测试(Android)
    Appium是一款开源工具,用于自动化iOS、Android和Windows桌面平台上的本地、移动web和混合应用程序。原生应用是指那些使用iOS、Android或Windowssdk编写的应用。移动网页应用是通过移动浏览器访问的网页应用(appum支持iOS和Chrome上的Safari或Android上的内置“浏览器”应用)。混......
  • Android系统build阶段签名机制
    https://maoao530.github.io/2017/01/31/android-build-sign/APK签名机制https://maoao530.github.io/2017/01/31/apk-sign/ 本文介绍Android系统build阶段的签名机制。一、系统build阶段签名机制1、系统中有4组key用于build阶段对apk进行签名:MediaPlatformSharedTestk......