如何从 MediaCodec 解码器的输出中提取 PCM 样本

2024-03-07

我正在尝试从解码的 mp4 缓冲区获取 PCM 样本以进行进一步处理。我首先从使用手机相机应用程序录制的视频文件中提取音轨,并且确保当我获得“audio/mp4”mime 键时已选择音轨:

MediaExtractor extractor = new MediaExtractor();
try {
    extractor.setDataSource(fileUri.getPath());
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
int numTracks = extractor.getTrackCount();
for(int i =0; i<numTracks; ++i) {
    MediaFormat format = extractor.getTrackFormat(i);
    String mime = format.getString(MediaFormat.KEY_MIME);
    //Log.d("mime =",mime);
    if(mime.startsWith("audio/")) {
        extractor.selectTrack(i);
        decoder = MediaCodec.createDecoderByType(mime);
        decoder.configure(format, null, null, 0);

        //getSampleCryptoInfo(MediaCodec.CryptoInfo info)
        break;
    }
}
if (decoder == null) {
    Log.e("DecodeActivity", "Can't find audio info!");
    return;
}
decoder.start();

之后,我迭代轨道,向编解码器提供编码访问单元流,并将解码的访问单元拉入 ByteBuffer(这是我从此处发布的视频渲染示例中回收的代码)https://github.com/vecio/MediaCodecDemo https://github.com/vecio/MediaCodecDemo):

ByteBuffer[] inputBuffers = decoder.getInputBuffers();
ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
BufferInfo info = new BufferInfo();

boolean isEOS = false;

while (true) {
    if (!isEOS) {
        int inIndex = decoder.dequeueInputBuffer(10000);
        if (inIndex >= 0) {
            ByteBuffer buffer = inputBuffers[inIndex];
            int sampleSize = extractor.readSampleData(buffer, 0);
            if (sampleSize < 0) {
                // We shouldn't stop the playback at this point, just pass the EOS
                // flag to decoder, we will get it again from the
                // dequeueOutputBuffer
                Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
                decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                isEOS = true;
            } else {
                decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
                extractor.advance();
            }
        }
    }

    int outIndex = decoder.dequeueOutputBuffer(info, 10000);
    switch (outIndex) {
    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
        Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
        outputBuffers = decoder.getOutputBuffers();
        break;
    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
        Log.d("DecodeActivity", "New format " + decoder.getOutputFormat());
        break;
    case MediaCodec.INFO_TRY_AGAIN_LATER:
        Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
        break;
    default:
        ByteBuffer buffer = outputBuffers[outIndex];
        // How to obtain PCM samples from this buffer variable??

        decoder.releaseOutputBuffer(outIndex, true);
        break;
    }

    // All decoded frames have been rendered, we can stop playing now
    if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
        Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
        break;
    }
}

到目前为止,代码似乎没有错误,但我目前一直在试图弄清楚如何从获取输出缓冲区值的 ByteBuffer 中获取 PCM 样本。我想我可以假设,因为我正在使用 16 位立体声音频文件,所以交错方案中应该至少有两个字节......但是我不太确定这一点,所以要明确地检索 PCM 样本从这个字节流。有人知道如何从 MediaCodec API 获取这些吗?

我已经阅读了一些使用 ffmpeg 或 openSL 的替代方案,但由于我是 Android 编程新手,我希望避免使用基于 C 的 API 的复杂性,并仅使用 Android 框架提供的工具构建我的第一个应用程序(我我正在使用奇巧)。任何帮助将不胜感激。

UPDATE:我能够提取 PCM 样本,按照我假设的方式以及 @marcone 指出的方式。为此,我在缓冲区分配下方添加了这些行:

byte[] b = new byte[info.size-info.offset];                         
int a = buffer.position();
buffer.get(b);
buffer.position(a);

最后通过以下方式将字节数组写入文件:

f.write(b,0,info.size-info.offset);

我现在处理的问题是:

解码的音频样本与 iZotope 完成的 mp4 音轨解码不完全匹配。波形文件大小中有 48 个样本不匹配,解码信号中有 2112 个样本延迟。我现在的问题是:所有 mp4 解码器都会产生相同的输出 PCM 流,还是取决于解码器的实现?


我发现延迟是由 AAC 编码启动和剩余时间引起的,如下所述:

https://developer.apple.com/library/mac/documentation/quicktime/qtff/QTFFAppenG/QTFFAppenG.html https://developer.apple.com/library/mac/documentation/quicktime/qtff/QTFFAppenG/QTFFAppenG.html

就我而言,启动时间始终为 2112 个样本,其余部分自然会根据音频大小而变化。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从 MediaCodec 解码器的输出中提取 PCM 样本 的相关文章

随机推荐

  • 我的 Redux 状态发生了变化,为什么 React 没有触发重新渲染?

    我正在尝试设计一个通知组件 其中通知会在某些情况下出现 例如连接问题 成功修改等 我需要通知在几秒钟后消失 因此我触发状态更改以从 Redux 状态中删除通知setTimeout在通知内componentDidMount 我可以看到状态确实
  • Firebase 最大项目和应用程序

    我可以从一个帐户在一个项目中创建多少个应用程序 以及可以在 Firebase for Cloud Messaging 中创建多少个项目 截至目前 2017 年 12 月 8 日 我最多获得 5 个项目 我在创建新项目时收到此消息 并且只有
  • 商务/节假日日期处理[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个与 Perl 模块类似的 C 类 库Date Manip http search cpa
  • RDLC过滤嵌套表

    我正在创建一个 RDLC 报告 其中数据集由多个数据表组成 有一张父表和几张子表 我想做的是显示父表中每一行的每个子表的相关数据 这是一个简化的示例 table1 Purchase 具有 PurhcaseID PurchaseNumber
  • 为什么 c++ std::min 在 O0 上编译时不能使用静态字段作为其参数?

    同样的代码 用O0编译 会报错 Name test cpp Author Version Copyright Your copyright notice Description Hello World in C Ansi style inc
  • 制作应用程序时使用的易失性变量

    我是这个领域的新手 以前 我在进行微控制器编程时 使用易失性变量来避免编译器优化 但我从来没有在变量声明之前看到过这样的 volatile 声明 这是否意味着编译是在 Arago 构建中没有进行任何优化的情况下完成的 这里我有两个问题 如何
  • (图书馆问题)

    我正在使用 Sencha Architect 和 Extjs 4 并按照此处的基本示例进行操作 http geoext org tutorials quickstart html basic example http geoext org
  • 如何将时间跨度转换为小数?

    我的值正好是1 08 43 23在我的文本框中 它等于 1d 08 43 23 我想将该值转换为十进制 以便将其乘以另一个十进制值 但是当我使用Convert ToDecimal它返回错误 输入字符串的格式无效 Is Convert ToD
  • 为 Asp.net core web api 启用 HTTPS 安全连接

    我有使用 ASP NET Core 2 1 创建的 REST API 应用程序 REST API 由 WebHostBuilder 创建并由 Kestrel 托管 Startup Kernel kernel restApiServer ne
  • Ajax 自动完成(或自动建议)与 TAB 完成/自动填充类似于 shell 命令行完成?

    我正在实现 AJAX 自动完成 自动建议功能 我不仅想要执行与用户键入的内容类似的常规显示建议 而且还想让用户完成部分完成以节省键入 所以 想象我的字典中有这些值 青苹果 青梨 绿色水果 蓝天 蓝色水 蓝色唤醒 如果用户输入 g 建议应该是
  • GetElementById() 找不到标签?

    我有一个有效的 XML 文件正在由以下 NET C windows 服务读取 有问题的标签 u1 000 绝对在元素中
  • 使用 CUDA 占用计算器

    我正在使用占用率计算器 但我无法理解如何获取每个线程的寄存器 每个块的共享内存 我阅读了文档 我使用 Visual Studio 所以在 CUDA 构建规则下的项目属性中 gt 命令行 gt 附加选项 I添加 ptxas options v
  • Selenium WebDriver 如何解决陈旧元素引用异常?

    我在 Selenium 2 Web 驱动程序测试中有以下代码 该代码在调试时有效 但在构建中运行时大多数情况下都会失败 我知道这一定与页面未刷新的方式有关 但不知道如何解决它 因此任何有关我做错了什么的指示都会受到赞赏 我使用 JSF pr
  • ToArrayAsync() 抛出“源 IQueryable 未实现 IAsyncEnumerable”

    我在 ASP NET Core 上有一个 MVC 项目 我的问题与 IQueryable 和异步有关 我编写了以下搜索方法IQueryable
  • 如何在客户端获取Tomcat JSESSIONID?

    我想在客户端获取Tomcat的JSESSIONID 那可能吗 如果是 怎么办 Thanks 这取决于 JSESSIONID 的传输方式 如果它是会话 cookie 请参阅如何使用javascript获取cookie http www w3s
  • 如果从数据库加载两次,hibernate 是否会加载同一实例的两个单独副本?

    我知道关于延迟加载有很多不同的问题 但我的问题有点不同 假设我有一个实体 A 其中有实体 B 的集合 类似地 在实体 B 中 我有 A 的集合 在这两种情况下 都使用lazy true 选项 Entity A的实例 aA 有 gt Set
  • 如何安装GWT浏览器插件?

    我如何安装适用于 Chrome 的 GWT 浏览器插件 https chrome google com webstore detail gwt developer plugin jpjpnpmbddbjkfaccnmhnkdgjideiei
  • 如何清除应用程序中的远程通知?

    有没有办法从 iPhone 屏幕顶部向下滑动时清除通知横幅中的远程通知 我尝试将徽章编号设置为零 application applicationIconBadgeNumber 0 代表中didFinishLaunchingWithOptio
  • 在代码中访问编译器常量

    是否可以在如下代码中使用编译器定义的常量 If DEALER DEBUG ID12345 Then If Dealer ID DEALER DEBUG Then Do something End If End If 我正在运行批处理 但我的
  • 如何从 MediaCodec 解码器的输出中提取 PCM 样本

    我正在尝试从解码的 mp4 缓冲区获取 PCM 样本以进行进一步处理 我首先从使用手机相机应用程序录制的视频文件中提取音轨 并且确保当我获得 audio mp4 mime 键时已选择音轨 MediaExtractor extractor n