当前位置: 首页 > 知识库问答 >
问题:

AMediaCodec_dequeueOutputBuffer速度较慢,在一个设备上返回-10000

慕容俭
2023-03-14

我目前正在为H264视频流编写解码器。目标平台是Android,所以我使用的是MediaCodec API(Android OS>=6.0)。

我在4个设备上测试了我的代码(4个设备上都是相同的):

  • 它在小米红米5 Plus上运行得很好(实际上速度相当快)。
  • 在Nexus 7和三星Galaxy Tab A上运行速度非常慢
  • 它在Samsung Galaxy Tab S2上失败,在amediaCodeC_DequeueOutputBuffer(configurestart返回正确的值(amedia_ok))中出现神秘错误代码-10000。

所以我的问题是:

  1. 我能以某种方式优化它吗?我测试了每个MediaCodec API调用的时间性能,看起来AmediaCodeC_DequeueOutputBuffer在这里是一个巨大的瓶颈(每个帧有80%-90%的时间)。
  2. 对于银河标签2上的-10000错误,我能做些什么吗?我读过MediaCodecs的文档,但没有描述。我只在VLC的源代码(modules/codec/omxil/mediacodec_ndk.c)中发现了常量AMEDIA_ERROR_UNKNOWN=-10000(问题2.b:他们是从哪里找到这个常量的?)。

设备规格(来自/etc/media_codecs.xml的解码器):

>

  • 小米红米5 Plus:Android 7.1.2“视频/AVC”解码器:omx.qcom.video.decoder.avc、omx.qcom.video.decoder.avc.secure

    Nexus 7(平板电脑)Android 6.0.1“视频/AVC”编解码器:omx.qcom.video.decoder.avc,omx.qcom.video.decoder.avc.secure

    Samsung Tab A
    Android 7.1.1“视频/AVC”解码器:omx.qcom.video.decoder.avc,omx.qcom.video.decoder.avc.secure,omx.sec.avc.sw.dec

    Samsung Tab S2:
    Android 7.0“视频/AVC”解码器:omx.exynos.avc.dec、omx.exynos.avc.dec.secure、omx.sec.avc.sw.dec

    我可以看到所有执行得当的设备(即使很慢)都有高通解码器的共同点。

    我的代码:

    //initialization (I omitted checks for errors, all initialization is executed without any errors.:
    //f contains pointers to functions from libmediandk.so
    
    const char mime[] = "video/avc";
    mDecoder = f.createDecoderByType(mime);
    AMediaFormat* mFormat = f.createMediaFormat();
    
    const int colorFormat = 19; //COLOR_FormatYUV420Planar
    f.setString(mFormat, c.keyMime, mime);
    f.setInt32(mFormat,  c.keyWidth, width);
    f.setInt32(mFormat,  c.keyHeight, height);
    f.setInt32(mFormat,  c.keyColorFormat, colorFormat);
    f.setInt32(mFormat, "encoder", 0);
    f.setInt32(mFormat, "max-input-size", 0);
    
    //both sps and pps are extracted from the stream
    f.setBuffer(mFormat, "csd-0", sps, sizeof(sps));
    f.setBuffer(mFormat, "csd-1", pps, sizeof(pps));
    
    media_status_t status = f.configure (mDecoder, mFormat, NULL, NULL, 0);
    status = f.start(mDecoder);
    
    f.deleteMediaFormat(mFormat);
    
    lastOutputBufferIdx = -1;
    
    //this is executed every loop
    //data -> char* with this frame's H264 encoded data
    //I omitted error check for clarity
    
    const int TIMEOUT_US = -1; //-1 -> blocking mode
    AMediaCodecBufferInfo info;
    char* buf = NULL;
    
    if (lastOutputBufferIdx != -1){
        f.releaseOutputBuffer(mDecoder, lastOutputBufferIdx, false);
        lastOutputBufferIdx = -1;     
    }
    ssize_t iBufIdx = f.dequeueInputBuffer(mDecoder, TIMEOUT_US);
    if (iBufIdx >= 0){
         buf = f.getInputBuffer(mDecoder, iBufIdx, &bufsize);
         int usedBufSize = 0;
         if (buf){
             usedBufSize = dataSize;
             memcpy(buf, data, usedBufSize);
         }
         media_status_t res = f.queueInputBuffer(mDecoder, iBufIdx, 0, usedBufSize, getTimestamp(), 0);
    }
    
    //here's my nemesis (this line is both bottleneck and -10000 generator):
    ssize_t oBufIdx = f.dequeueOutputBuffer(mDecoder, &info, TIMEOUT_US);
    
    //I am not interested in processing any error codes from {-1,-2,-3}
    //INFO_TRY_AGAIN_LATER, INFO_OUTPUT_FORMAT_CHANGED, INFO_OUTPUT_BUFFERS_CHANGED)
    while (oBufIdx == -1 || oBufIdx == -2 || oBufIdx == -3){
        oBufIdx = f.dequeueOutputBuffer(mDecoder, &info, TIMEOUT_US);
    }
    
    while (oBufIdx >= 0)
    {
        buf = f.getOutputBuffer(mDecoder, oBufIdx, &bufsize);
        AMediaFormat format = f.getOutputFormat(mDecoder);
        f.getInt32(format, "width", &width);
        f.getInt32(format, "height", &height);
        f.deleteMediaFormat(format);
    
        //yuv_ is struct returned by my function
        yuv_.data = buf + info.offset;
    
        yuv_.size = bufsize;
        yuv_.width = width;
        yuv_.height = height;
    
        yuv_.yPlane = yuv_.data + info.offset;
        yuv_.uPlane = yuv_.yPlane + height * width;
        yuv_.vPlane = yuv_.uPlane + (height * width) / 4;
    
        yuv_.yStride = width;
        yuv_.uStride = width / 2;
        yuv_.vStride = width / 2;
    }
    
    lastOutputBufferIdx = oBufIdx;
    
  • 共有1个答案

    车诚
    2023-03-14

    最大的问题是,您只向解码器提供一个数据包,然后阻塞,等待返回单个解码帧。

    硬件解码器通常有一点延迟;通过解码器的一个单一帧所需的时间比在单个帧之间所需的时间更长,如果您只是不断地馈送它们。

    所以不要停下来等待输出,而是在可能的情况下提供更多的输入包(如果您有可用的)。从第一个数据包输入到第一个解码输出的时间可能是相同的,但在此之后,您应该更快地得到下一帧。有些解码器甚至不会返回任何东西,无论您等待多长时间,直到您给它提供了至少几个输入数据包。

     类似资料:
    • 我们已经使用jmap在Java 6下运行的大型多服务器应用程序上测量堆大小大约2年了。我们每分钟测量一次。每次测量所用的时间(经过的时间)不到1秒。 我们现在正在Java 7下测试同一个应用程序。现在突然之间,jmap通常需要10秒、20秒,有时甚至更长时间,而且它似乎慢了下来(甚至可能停止!)在那段时间里,JVM。 我们在jmap输出中看到的唯一区别(Java6和Java7之间)是关于有多少字符

    • 查看了以下帖子,但对我的案例没有帮助: BluetoothDevice在getName()上始终返回null 我的问题 我正在开发一个应用程序,可以扫描附近的蓝牙设备,并显示在主屏幕上。但在少数设备(如moto G、samsung note2)上,leScanCallback上的设备名称为空。 我的代码 注意:如果返回null,我需要抛出一个警报,但我不能在onlescan内这样做,因为扫描是连续

    • 问题内容: 我正在尝试获取文件的大小,然后再下载。我曾经这样做,它在我的家用计算机Android 2.1 Emulator上运行良好。 但是,一旦我从手机(WiFi或3G)运行我的应用程序,该功能将无法使用,而当我从工作笔记本电脑的Android 2.1仿真器中运行该应用程序时,它也将无法使用。 有谁知道解决方法?还有另一种方法可以获取文件的大小,而无需使用。 问题答案: 此信息并非始终可用。通常

    • 队列并发两个不同的锁:一个用于 enqueue() 以保护同时排队的多个线程 如果队列已满,Add(enqueue)将跳过(返回)插入。如果队列为空,则删除(出列)将跳过删除。 我使用doRandon()生成了一堆0到1之间的随机数。我使用这些数字来决定是否添加/删除。 性能:我已尝试使用静态/动态线程分配测试队列。的执行时间

    • 因此,当增加randint()中的界限时,排序列表上的循环会变慢。为什么?

    • 我试图在我的应用程序中解码AAC编码的文件,并初始化用于初始化我的对象的对象,这是为对象设置变量的代码 我面临的问题是语句为某些设备上的同一文件获取我。该应用程序正在生产中,我在android API级别为17、18和19的处理器设备上看到了这个错误,大多数错误都发生在三星设备上。有什么方向吗?