Android MediaCodec 向后查找

2024-02-29

我正在尝试使用以下方法实现视频的精确搜索MediaCodec and MediaExtractor。通过关注 Grafika 的电影播放器 https://github.com/google/grafika/blob/master/app/src/main/java/com/android/grafika/MoviePlayer.java,我已经成功地实现了前瞻性。但是我仍然在向后查找方面遇到问题。相关代码在这里:

public void seekBackward(long position){
    final int TIMEOUT_USEC = 10000;
    int inputChunk = 0;
    long firstInputTimeNsec = -1;

    boolean outputDone = false;
    boolean inputDone = false;

    mExtractor.seekTo(position, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
    Log.d("TEST_MEDIA", "sampleTime: " + mExtractor.getSampleTime()/1000 + " -- position: " + position/1000 + " ----- BACKWARD");

    while (mExtractor.getSampleTime() < position && position >= 0) {

        if (VERBOSE) Log.d(TAG, "loop");
        if (mIsStopRequested) {
            Log.d(TAG, "Stop requested");
            return;
        }

        // Feed more data to the decoder.
        if (!inputDone) {
            int inputBufIndex = mDecoder.dequeueInputBuffer(TIMEOUT_USEC);
            if (inputBufIndex >= 0) {
                if (firstInputTimeNsec == -1) {
                    firstInputTimeNsec = System.nanoTime();
                }
                ByteBuffer inputBuf = mDecoderInputBuffers[inputBufIndex];
                // Read the sample data into the ByteBuffer.  This neither respects nor
                // updates inputBuf's position, limit, etc.
                int chunkSize = mExtractor.readSampleData(inputBuf, 0);
                if (chunkSize < 0) {
                    // End of stream -- send empty frame with EOS flag set.
                    mDecoder.queueInputBuffer(inputBufIndex, 0, 0, 0L,
                            MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    inputDone = true;
                    if (VERBOSE) Log.d(TAG, "sent input EOS");
                } else {
                    if (mExtractor.getSampleTrackIndex() != mTrackIndex) {
                        Log.w(TAG, "WEIRD: got sample from track " +
                                mExtractor.getSampleTrackIndex() + ", expected " + mTrackIndex);
                    }
                    long presentationTimeUs = mExtractor.getSampleTime();
                    mDecoder.queueInputBuffer(inputBufIndex, 0, chunkSize,
                            presentationTimeUs, 0 /*flags*/);
                    if (VERBOSE) {
                        Log.d(TAG, "submitted frame " + inputChunk + " to dec, size=" + chunkSize);
                    }
                    inputChunk++;
                    mExtractor.advance();
                }
            } else {
                if (VERBOSE) Log.d(TAG, "input buffer not available");
            }
        }

        if (!outputDone) {
            int decoderStatus = mDecoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
            if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
                // no output available yet
                if (VERBOSE) Log.d(TAG, "no output from decoder available");
            } else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                // not important for us, since we're using Surface
                if (VERBOSE) Log.d(TAG, "decoder output buffers changed");
            } else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                MediaFormat newFormat = mDecoder.getOutputFormat();
                if (VERBOSE) Log.d(TAG, "decoder output format changed: " + newFormat);
            } else if (decoderStatus < 0) {
                throw new RuntimeException(
                        "unexpected result from decoder.dequeueOutputBuffer: " +
                                decoderStatus);
            } else { // decoderStatus >= 0
                if (firstInputTimeNsec != 0) {
                    // Log the delay from the first buffer of input to the first buffer
                    // of output.
                    long nowNsec = System.nanoTime();
                    Log.d(TAG, "startup lag " + ((nowNsec-firstInputTimeNsec) / 1000000.0) + " ms");
                    firstInputTimeNsec = 0;
                }
                boolean doLoop = false;
                if (VERBOSE) Log.d(TAG, "surface decoder given buffer " + decoderStatus +
                        " (size=" + mBufferInfo.size + ")");
                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    if (VERBOSE) Log.d(TAG, "output EOS");
                    if (mLoop) {
                        doLoop = true;
                    } else {
                        outputDone = true;
                    }
                }

                boolean doRender = (mBufferInfo.size != 0);

                // As soon as we call releaseOutputBuffer, the buffer will be forwarded
                // to SurfaceTexture to convert to a texture.  We can't control when it
                // appears on-screen, but we can manage the pace at which we release
                // the buffers.
                if (doRender && mFrameCallback != null) {
                    mFrameCallback.preRender(mBufferInfo.presentationTimeUs);
                }
                mDecoder.releaseOutputBuffer(decoderStatus, doRender);
                doRender = false;
                if (doRender && mFrameCallback != null) {
                    mFrameCallback.postRender();
                }

                if (doLoop) {
                    Log.d(TAG, "Reached EOS, looping");
                    mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
                    inputDone = false;
                    mDecoder.flush();    // reset decoder state
                    mFrameCallback.loopReset();
                }
            }
        }
    }
}

基本上和MoviePlayer的一样doExtract方法。我只是添加了一个轻微的修改来返回到前一个关键帧,而不是向前解码到我想要的位置。我也关注过fadden的这里的建议 https://stackoverflow.com/a/31322473/1602807但收效甚微。

另一个附带问题,据我了解,ExoPlayer 是建立在MediaCodec那么为什么MoviePlayer的纯实现却可以正常播放iOS录制的视频MediaCodec can't?


好吧,这就是我解决问题的方法,基本上我误解了 fadden 对render旗帜。问题不在于解码,而在于仅显示最接近查找位置的最后一个缓冲区。我是这样做的:

if (Math.abs(position - mExtractor.getSampleTime()) < 10000) {
   mDecoder.releaseOutputBuffer(decoderStatus, true);
} else {
   mDecoder.releaseOutputBuffer(decoderStatus, false);
}

这是一种相当黑客的方法。优雅的方法应该是保存最后一个输出缓冲区并将其显示在while循环,但我真的不知道如何访问输出缓冲区,以便我可以将其保存到临时缓冲区。

EDIT:

这是一种不太黑客的方法。基本上,我们只需要计算关键帧和搜索位置之间的总帧数,然后我们只需要显示距离搜索位置最近的 1 或 2 帧。像这样的事情:

    mExtractor.seekTo(position, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
    int stopPosition = getStopPosition(mExtractor.getSampleTime(), position);
    int count = 0;

    while (mExtractor.getSampleTime() < position && mExtractor.getSampleTime() != -1 && position >= 0) {
    ....

        if(stopPosition - count < 2) { //just to make sure we will get something (1 frame sooner), see getStopPosition comment
           mDecoder.releaseOutputBuffer(decoderStatus, true);
        }else{
           mDecoder.releaseOutputBuffer(decoderStatus, false);
        }
        count++;
     ...
    }

/**
 * Calculate how many frame in between the key frame and the seeking position
 * so that we can determine how many while loop will be execute, then we can just
 * need to stop the loop 2 or 3 frames sooner to ensure we can get something.
 * */
private int getStopPosition(long start, long end){
    long delta = end - start;
    float framePerMicroSecond = mFPS / 1000000;

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

Android MediaCodec 向后查找 的相关文章

随机推荐

  • C 中指针比较如何工作?可以比较不指向同一数组的指针吗?

    在 K R C 编程语言第二版 第 5 章中 我读到了以下内容 首先 在某些情况下可以比较指针 如果p and q指向同一个数组的成员 那么关系如下 lt gt 等工作正常 这似乎意味着只能比较指向同一数组的指针 但是当我尝试这段代码时 c
  • 解析 LocalDate 但得到 DateTimeParseException; dd-MMM-uuuu

    我正在尝试转换String to LocalDate using DateTimeFormatter 但我收到一个异常 java time format DateTimeParseException Text 2021 10 31 无法在索
  • 如何在 65536 行后添加 SSRS 分页符,计算组页眉/页脚

    我有一个 SSRS 报告 当行数大于 Excel 2003 限制 65536 时 该报告无法导出到 Excel 该报告已经具有带有组页脚的分组级别 我尝试在表达式上添加一个带有分页符的额外分组级别 ceiling rownumber not
  • 线太长。姜戈 PEP8

    PEP8信息 models py 10 80 E501 line too long 83 gt 79 characters 模型 py field TreeForeignKey self null True blank True relat
  • 展开/折叠 ttk Treeview 分支

    我想知道 ttk Treeview 中折叠和展开分支的命令 这是一个简约的示例代码 coding utf 8 import tkinter as tk from tkinter import ttk root tk Tk tree ttk
  • 沿直线或路径移动 SVG 对象

    我想要为 SVG 对象设置动画 以便它遵循我从 d3 js 中的线条生成器构建的 SVG 路径 有什么简单的方法可以实现这一目标吗 特别是 我想获得与我的路径相对应的插值坐标 从那里 使用 tween js 或 d3 js 本身执行动画将很
  • 防止嵌套对象的 mongodb C# 驱动程序将 id 序列化为 _id

    我正在使用 C mongodb 驱动程序来更新 mongodb 中的记录 下面的代码对我来说工作正常 但它会自动将所有出现的 id 转换为 id var client GetMongoClient var collection1 GetMo
  • 使用 C# 压缩/解压字符串

    我是 net新手 我正在用 C 进行字符串压缩和解压 有一个 XML 我正在转换为字符串 然后进行压缩和解压缩 我的代码中没有编译错误 除非我解压代码并返回字符串时 它只返回 XML 的一半 下面是我的代码 有错误的地方请指正 Code c
  • 如何使用数据库更改更新 edmx 文件?

    我有一个 edmx 文件 并且更改了数据库中的一个表 我知道有一个 从数据库更新模型 向导 但在许多情况下这是无用的 例如 如果我将字段从非空更改为可为空 或者如果我删除字段 则更新模型不会反映更改 我必须删除实体并将其添加回来才能使更改出
  • 如何获取外部页面优惠券/优惠券表格以在 OpenCart 中使用?

    我的 OpenCart 环境中有另一个页面 比如说 关于我们 页面 其中包含下面这些表单 假设用户的购物车中有商品 这些表单应该可以工作 但不能 在此输入您的优惠券代码
  • nginx 将所有 http 重定向到 https,但有一个例外

    我想将所有 http 流量重定向到 https 但有一个例外 url 中带有 preview 的任何内容我都想保留在 http 上 我尝试过以下配置 但它一直告诉我有一个重定向循环 server listen 80 server name
  • C++0x没有信号量?如何同步线程?

    C 0x 真的不会有信号量吗 Stack Overflow 上已经有一些关于信号量使用的问题 我一直使用它们 posix 信号量 来让一个线程等待另一个线程中的某个事件 void thread0 doSomething0 event1 wa
  • 有没有办法以编程方式将 Alias 添加到 Powershell Cmdlet?

    我正在为我的应用程序编写自定义 Powershell cmdlet 并且需要为某些 cmdlet 提供别名 假设我有 cmdlet Get DirectoryListing 并且我想向此 cmdlet 添加别名 例如 gdl 我怎样才能做到
  • React.js 服务器端渲染和事件处理程序

    我正在学习使用react js 并且在使用事件处理程序时遇到一些问题 最后一个问题是 是否可以使用服务器端渲染并将事件处理程序自动发送到客户端 这是我的例子 我有一个 index jsx 我渲染服务器端并将其发送到客户端 var React
  • Vuetify,工具提示:“on”和“attrs”有什么用?

    我在 Vuetify 文档中查找 Tooltip 找到了这个示例
  • 定位并删除 jupyter 笔记本中的隐藏内核

    我试图找出我的 mac 中的 anaconda 内核在哪里 因为应用程序报告了不同的事情 如果我运行 jupyter 内核规范列表 I get 可用内核 python2 用户 用户 anaconda 共享 jupyter kernels p
  • 传递参数 Angular 2 传统方式

    我正在尝试以这种格式将参数传递给一个组件www domain com param value 但是 Angular 不断发送这样的参数www domain com param value 为什么要更换 for 这是我的路线配置 const
  • MATLAB scatter3、plot3 速度差异

    这是关于 MATLAB 如何花费不同的时间来绘制同一件事及其原因 我在 3D 空间中生成 10000 个点 X rand 10000 1 Y rand 10000 1 Z rand 10000 1 然后 我使用四种不同方法之一来绘制此图 创
  • Hibernate异常:缺少列(列存在)

    好的 在数据 库中我们有一个名为 distribution Companies 的表 创建如下 CREATE TABLE distributionCompanies distributionCompanyID INT 11 NOT NULL
  • Android MediaCodec 向后查找

    我正在尝试使用以下方法实现视频的精确搜索MediaCodec and MediaExtractor 通过关注 Grafika 的电影播放器 https github com google grafika blob master app sr