为什么 MediaConfig 在简单解码设置时抛出 UnsupportedOperationException?
Why is MediaConfig throwing UnsupportedOperationException on simple decode setup?
我正在尝试使用 Android 上的 MediaConfig 解码一个简单的 H.264 视频流。它只是在第一个数据包上抛出异常,对于我来说,我无法理解为什么。
这是最少的代码。它所做的只是获取编解码器、启动解码器、获取输入缓冲区、填充并提交。但是在调试设备上,我得到一个 UnsupportedOperationException(也在下面)。
这让我很生气,在模拟器上花了一天时间才意识到它根本没有进行任何视频解码,这无济于事。
代码(除去问题之外的所有内容):
package com.AndroidH264VideoTest;
import android.app.Activity;
import android.os.Bundle;
import android.media.MediaCodec;
import android.media.MediaFormat;
import java.nio.ByteBuffer;
public class AndroidH264VideoTest extends Activity
{
private static final int SHORT_TIMEOUT = 1; // Provide some timeout to getting a buffer
private MediaCodec m_decoder = null;
private ByteBuffer[] m_decoderInputBuffers;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
String MIME_TYPE = "video/avc"; // This is the type for H.264
int TEST_WIDTH = 8; // Our test image is 8x8 pixels
int TEST_HEIGHT = 8;
m_decoder = MediaCodec.createDecoderByType(MIME_TYPE);
MediaFormat decoderFormat = MediaFormat.createVideoFormat(MIME_TYPE, TEST_WIDTH, TEST_HEIGHT);
m_decoder.configure(decoderFormat, null, null, 0); // bytebuffer input/output
m_decoder.start();
m_decoderInputBuffers = m_decoder.getInputBuffers();
}
@Override
public void onResume()
{
byte SPS_PACKET[] =
{
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, // NAL header identifier
(byte)0x67, // NAL info - (byte)0x67 = 0 11 00111 = type 7 = SPS (1st packet)
(byte)0x42, // SPS profile_idc = (byte)0x42/66 - Baseline. Should be decodable by anything
(byte)0xC0, // Constrained - should be even more decodable.
(byte)0x0A, // SPS level_idc = (byte)0x0a/10
(byte)0xDA, (byte)0x7E, (byte)0x59, (byte)0x66, (byte)0xA0, (byte)0xC0, (byte)0x20,
(byte)0xC8, (byte)0x00, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x00,
(byte)0x03, (byte)0x03, (byte)0xc4, (byte)0x78, (byte)0x91, (byte)0x35,
};
super.onResume(); // Always call the superclass method first
// Take ownership of buffer
int inputBufferId = m_decoder.dequeueInputBuffer(SHORT_TIMEOUT);
if (inputBufferId < 0)
{
// No buffer available error
return;
}
// Copy data to buffer
m_decoderInputBuffers[inputBufferId] = ByteBuffer.wrap(SPS_PACKET);
// Submit buffer for processing
m_decoder.queueInputBuffer(inputBufferId, 0, SPS_PACKET.length, 0, MediaCodec.BUFFER_FLAG_SYNC_FRAME);
}
}
所以,那应该是无害的。不会做任何事情,因为我只传递了一个 NAL 数据包,但它不应该抱怨。
但是,在我的 Marshmallow 测试设备上发生了这种情况:
8-21 16:21:45.295 21871 21871 V ActivityThread: Performing resume of ActivityRecord{fa15f01 token=android.os.BinderProxy@e6df1a6 {com.AndroidH264VideoTest.AndroidH264VideoTest/com.AndroidH264VideoTest.AndroidH264VideoTest}}
08-21 16:21:45.296 21871 21871 D AndroidRuntime: Shutting down VM
--------- beginning of crash
08-21 16:21:45.297 21871 21871 E AndroidRuntime: FATAL EXCEPTION: main
08-21 16:21:45.297 21871 21871 E AndroidRuntime: Process: com.AndroidH264VideoTest.AndroidH264VideoTest, PID: 21871
08-21 16:21:45.297 21871 21871 E AndroidRuntime: java.lang.RuntimeException: Unable to resume activity {com.AndroidH264VideoTest.AndroidH264VideoTest/com.AndroidH264VideoTest.AndroidH264VideoTest}: java.lang.UnsupportedOperationException
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3325)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3356)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2670)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.-wrap11(ActivityThread.java)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1499)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:111)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.os.Looper.loop(Looper.java:207)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:5765)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: Caused by: java.lang.UnsupportedOperationException
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at java.nio.ByteBuffer.setAccessible(ByteBuffer.java:636)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.media.MediaCodec.invalidateByteBuffer(MediaCodec.java:2646)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.media.MediaCodec.queueInputBuffer(MediaCodec.java:2174)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at com.AndroidH264VideoTest.AndroidH264VideoTest.onResume(AndroidH264VideoTest.java:68)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1268)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.Activity.performResume(Activity.java:6392)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3310)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: ... 10 more
那么,为什么 queueInputBuffer() 中的操作不受支持?
更新:奇怪的是在不同的设备上(Android 4.4.4 KitKat,S4 mini)我收到了不同的错误消息,但它仍然爆炸:
E/AndroidRuntime(25080): FATAL EXCEPTION: main
E/AndroidRuntime(25080): Process: com.AndroidH264VideoTest.AndroidH264VideoTest, PID: 25080
E/AndroidRuntime(25080): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.AndroidH264VideoTest.AndroidH264VideoTest/com.AndroidH264VideoTest.AndroidH264VideoTest}: java.lang.IllegalStateException
E/AndroidRuntime(25080): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2548)
E/AndroidRuntime(25080): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2607)
E/AndroidRuntime(25080): at android.app.ActivityThread.access0(ActivityThread.java:174)
E/AndroidRuntime(25080): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1325)
E/AndroidRuntime(25080): at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime(25080): at android.os.Looper.loop(Looper.java:146)
E/AndroidRuntime(25080): at android.app.ActivityThread.main(ActivityThread.java:5756)
E/AndroidRuntime(25080): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(25080): at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime(25080): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
E/AndroidRuntime(25080): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
E/AndroidRuntime(25080): at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(25080): Caused by: java.lang.IllegalStateException
E/AndroidRuntime(25080): at android.media.MediaCodec.native_configure(Native Method)
E/AndroidRuntime(25080): at android.media.MediaCodec.configure(MediaCodec.java:262)
E/AndroidRuntime(25080): at com.AndroidH264VideoTest.AndroidH264VideoTest.onCreate(AndroidH264VideoTest.java:33)
E/AndroidRuntime(25080): at android.app.Activity.performCreate(Activity.java:5619)
E/AndroidRuntime(25080): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1093)
E/AndroidRuntime(25080): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2512)
E/AndroidRuntime(25080): ... 11 more
我会说你的问题出在这里:
// Copy data to buffer
m_decoderInputBuffers[inputBufferId] = ByteBuffer.wrap(SPS_PACKET);
这只会用对另一个 ByteBuffer 的引用替换本地数组中的缓冲区 - 但解码器本身仍会尝试读取 m_decoder.getInputBuffers()[inputBufferId]
。因此,您需要将输入字节复制到解码器提供的 ByteBuffer 中。
4.4.4 上的问题是试图解码 8x8 图像,这对于它的小大脑来说太小了。
如果我将 8x8 替换为 320x240、160x160 甚至 67x71,只要它是 64x64 或更大,它就会很高兴。
我得看看这个任意大小限制是否也适用于 Marshmallow 设备。
我正在尝试使用 Android 上的 MediaConfig 解码一个简单的 H.264 视频流。它只是在第一个数据包上抛出异常,对于我来说,我无法理解为什么。
这是最少的代码。它所做的只是获取编解码器、启动解码器、获取输入缓冲区、填充并提交。但是在调试设备上,我得到一个 UnsupportedOperationException(也在下面)。
这让我很生气,在模拟器上花了一天时间才意识到它根本没有进行任何视频解码,这无济于事。
代码(除去问题之外的所有内容):
package com.AndroidH264VideoTest;
import android.app.Activity;
import android.os.Bundle;
import android.media.MediaCodec;
import android.media.MediaFormat;
import java.nio.ByteBuffer;
public class AndroidH264VideoTest extends Activity
{
private static final int SHORT_TIMEOUT = 1; // Provide some timeout to getting a buffer
private MediaCodec m_decoder = null;
private ByteBuffer[] m_decoderInputBuffers;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
String MIME_TYPE = "video/avc"; // This is the type for H.264
int TEST_WIDTH = 8; // Our test image is 8x8 pixels
int TEST_HEIGHT = 8;
m_decoder = MediaCodec.createDecoderByType(MIME_TYPE);
MediaFormat decoderFormat = MediaFormat.createVideoFormat(MIME_TYPE, TEST_WIDTH, TEST_HEIGHT);
m_decoder.configure(decoderFormat, null, null, 0); // bytebuffer input/output
m_decoder.start();
m_decoderInputBuffers = m_decoder.getInputBuffers();
}
@Override
public void onResume()
{
byte SPS_PACKET[] =
{
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, // NAL header identifier
(byte)0x67, // NAL info - (byte)0x67 = 0 11 00111 = type 7 = SPS (1st packet)
(byte)0x42, // SPS profile_idc = (byte)0x42/66 - Baseline. Should be decodable by anything
(byte)0xC0, // Constrained - should be even more decodable.
(byte)0x0A, // SPS level_idc = (byte)0x0a/10
(byte)0xDA, (byte)0x7E, (byte)0x59, (byte)0x66, (byte)0xA0, (byte)0xC0, (byte)0x20,
(byte)0xC8, (byte)0x00, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x00,
(byte)0x03, (byte)0x03, (byte)0xc4, (byte)0x78, (byte)0x91, (byte)0x35,
};
super.onResume(); // Always call the superclass method first
// Take ownership of buffer
int inputBufferId = m_decoder.dequeueInputBuffer(SHORT_TIMEOUT);
if (inputBufferId < 0)
{
// No buffer available error
return;
}
// Copy data to buffer
m_decoderInputBuffers[inputBufferId] = ByteBuffer.wrap(SPS_PACKET);
// Submit buffer for processing
m_decoder.queueInputBuffer(inputBufferId, 0, SPS_PACKET.length, 0, MediaCodec.BUFFER_FLAG_SYNC_FRAME);
}
}
所以,那应该是无害的。不会做任何事情,因为我只传递了一个 NAL 数据包,但它不应该抱怨。
但是,在我的 Marshmallow 测试设备上发生了这种情况:
8-21 16:21:45.295 21871 21871 V ActivityThread: Performing resume of ActivityRecord{fa15f01 token=android.os.BinderProxy@e6df1a6 {com.AndroidH264VideoTest.AndroidH264VideoTest/com.AndroidH264VideoTest.AndroidH264VideoTest}}
08-21 16:21:45.296 21871 21871 D AndroidRuntime: Shutting down VM
--------- beginning of crash
08-21 16:21:45.297 21871 21871 E AndroidRuntime: FATAL EXCEPTION: main
08-21 16:21:45.297 21871 21871 E AndroidRuntime: Process: com.AndroidH264VideoTest.AndroidH264VideoTest, PID: 21871
08-21 16:21:45.297 21871 21871 E AndroidRuntime: java.lang.RuntimeException: Unable to resume activity {com.AndroidH264VideoTest.AndroidH264VideoTest/com.AndroidH264VideoTest.AndroidH264VideoTest}: java.lang.UnsupportedOperationException
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3325)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3356)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2670)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.-wrap11(ActivityThread.java)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1499)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:111)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.os.Looper.loop(Looper.java:207)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:5765)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: Caused by: java.lang.UnsupportedOperationException
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at java.nio.ByteBuffer.setAccessible(ByteBuffer.java:636)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.media.MediaCodec.invalidateByteBuffer(MediaCodec.java:2646)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.media.MediaCodec.queueInputBuffer(MediaCodec.java:2174)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at com.AndroidH264VideoTest.AndroidH264VideoTest.onResume(AndroidH264VideoTest.java:68)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1268)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.Activity.performResume(Activity.java:6392)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3310)
08-21 16:21:45.297 21871 21871 E AndroidRuntime: ... 10 more
那么,为什么 queueInputBuffer() 中的操作不受支持?
更新:奇怪的是在不同的设备上(Android 4.4.4 KitKat,S4 mini)我收到了不同的错误消息,但它仍然爆炸:
E/AndroidRuntime(25080): FATAL EXCEPTION: main
E/AndroidRuntime(25080): Process: com.AndroidH264VideoTest.AndroidH264VideoTest, PID: 25080
E/AndroidRuntime(25080): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.AndroidH264VideoTest.AndroidH264VideoTest/com.AndroidH264VideoTest.AndroidH264VideoTest}: java.lang.IllegalStateException
E/AndroidRuntime(25080): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2548)
E/AndroidRuntime(25080): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2607)
E/AndroidRuntime(25080): at android.app.ActivityThread.access0(ActivityThread.java:174)
E/AndroidRuntime(25080): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1325)
E/AndroidRuntime(25080): at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime(25080): at android.os.Looper.loop(Looper.java:146)
E/AndroidRuntime(25080): at android.app.ActivityThread.main(ActivityThread.java:5756)
E/AndroidRuntime(25080): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(25080): at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime(25080): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
E/AndroidRuntime(25080): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
E/AndroidRuntime(25080): at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(25080): Caused by: java.lang.IllegalStateException
E/AndroidRuntime(25080): at android.media.MediaCodec.native_configure(Native Method)
E/AndroidRuntime(25080): at android.media.MediaCodec.configure(MediaCodec.java:262)
E/AndroidRuntime(25080): at com.AndroidH264VideoTest.AndroidH264VideoTest.onCreate(AndroidH264VideoTest.java:33)
E/AndroidRuntime(25080): at android.app.Activity.performCreate(Activity.java:5619)
E/AndroidRuntime(25080): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1093)
E/AndroidRuntime(25080): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2512)
E/AndroidRuntime(25080): ... 11 more
我会说你的问题出在这里:
// Copy data to buffer
m_decoderInputBuffers[inputBufferId] = ByteBuffer.wrap(SPS_PACKET);
这只会用对另一个 ByteBuffer 的引用替换本地数组中的缓冲区 - 但解码器本身仍会尝试读取 m_decoder.getInputBuffers()[inputBufferId]
。因此,您需要将输入字节复制到解码器提供的 ByteBuffer 中。
4.4.4 上的问题是试图解码 8x8 图像,这对于它的小大脑来说太小了。 如果我将 8x8 替换为 320x240、160x160 甚至 67x71,只要它是 64x64 或更大,它就会很高兴。
我得看看这个任意大小限制是否也适用于 Marshmallow 设备。