需求是一直接受后台返回的视频流数据,使用 MediaCodec 解码视频 SurfaceView 播放。后台数据只有宽、高、mimeType 、时间戳 pts 、数据包;我该怎么配置 MediaFormat ?当前遇到的问题是有一台定制平板什么视频都播放不了,dequeueOutputBuffer 一直返回的是-1,其它的设备都是能够正常播放;麻烦各位帮忙看看。
代码:
收到后台数据
Object[] objects = message.getObjects();
int res = (int) objects[0];
int codecid = (int) objects[1];
int width = (int) objects[2];
int height = (int) objects[3];
byte[] packet = (byte[]) objects[4];
byte[] codecdata = (byte[]) objects[5];
long pts = (long) objects[6];
String mimeType = Macro.getMimeType(codecid);
int length = 0;
if (packet != null) {
lastPushTime = System.currentTimeMillis();
length = packet.length;
if (!saveMimeType.equals(mimeType) || initW != width || initH != height || mediaCodec == null) {
//有变更则重新初始化 MediaCodec
if (mediaCodec != null) {
LogUtil.e(TAG, "重新配置 MediaCodec");
mediaCodec.stop();
}
saveMimeType = mimeType;
initCodec(width, height, codecdata);
}
}
mediaCodecDecode(packet, length, pts);
初始化 MediaCodec
private void initCodec(int w, int h, byte[] codecdata) {
try {
mediaCodec = MediaCodec.createDecoderByType(saveMimeType);
MediaCodecInfo mediaCodecInfo = null;
mediaCodecInfo = getSupportedMediaCodecInfo(saveMimeType);
if (mediaCodecInfo == null) {
mediaCodecInfo = mediaCodec.getCodecInfo();
}
/** 宽高要判断是否是解码器所支持的范围 */
MediaCodecInfo.CodecCapabilities capabilitiesForType = mediaCodecInfo.getCapabilitiesForType(saveMimeType);
MediaCodecInfo.VideoCapabilities videoCapabilities = capabilitiesForType.getVideoCapabilities();
Range<Integer> supportedWidths = videoCapabilities.getSupportedWidths();
Integer lower = supportedWidths.getLower();
Integer upper = supportedWidths.getUpper();
Range<Integer> supportedHeights = videoCapabilities.getSupportedHeights();
Integer lower1 = supportedHeights.getLower();
Integer upper1 = supportedHeights.getUpper();
initW = w;
initH = h;
if (w > upper) {
w = upper;
} else if (w < lower) {
w = lower;
}
if (h > upper1) {
h = upper1;
} else if (h < lower1) {
h = lower1;
}
initMediaFormat(w, h, codecdata);
info = new MediaCodec.BufferInfo();
try {
mediaCodec.configure(mediaFormat, surface, null, 0);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
mediaCodec.start();
} catch (IOException e) {
e.printStackTrace();
}
}
public MediaCodecInfo getSupportedMediaCodecInfo(String mimeType) {
//=REGULAR_CODECS 常规,=ALL_CODECS 所有
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
MediaCodecInfo[] codecInfos = mediaCodecList.getCodecInfos();
for (MediaCodecInfo codecInfo : codecInfos) {
if (!codecInfo.isEncoder()) {
continue;
}
String[] types = codecInfo.getSupportedTypes();
for (String type : types) {
if (type.equalsIgnoreCase(mimeType)) {
return codecInfo;
}
}
}
return null;
}
private void initMediaFormat(int w, int h, byte[] csddata) {
mediaFormat = MediaFormat.createVideoFormat(saveMimeType, w, h);
mediaFormat.setInteger(MediaFormat.KEY_WIDTH, w);
mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, h);
mediaFormat.setLong(MediaFormat.KEY_MAX_INPUT_SIZE, w * h);
if (csddata != null) {
ByteBuffer csd_0 = ByteBuffer.wrap(csddata);
ByteBuffer csd_1 = ByteBuffer.wrap(csddata);
mediaFormat.setByteBuffer("csd-0", csd_0);
mediaFormat.setByteBuffer("csd-1", csd_1);
}
LogUtil.d(TAG, "initMediaFormat : --> " + mediaFormat);
}
MediaCodecDecode 方法
private void mediaCodecDecode(byte[] bytes, int size, long pts) {
try {
int inputBufferIndex = mediaCodec.dequeueInputBuffer(0);
LogUtil.i(TAG, "mediaCodecDecode -->dequeueInputBuffer index= " + inputBufferIndex);
if (inputBufferIndex >= 0) {
ByteBuffer byteBuffer = mediaCodec.getInputBuffer(inputBufferIndex);
byteBuffer.clear();
byteBuffer.put(bytes);
mediaCodec.queueInputBuffer(inputBufferIndex, 0, size, pts, 0);
LogUtil.i(TAG, "mediaCodecDecode dequeueInputBuffer pts= " + pts + ", index = " + inputBufferIndex + ", inputCount:" + (++inputCount));
}
} catch (IllegalStateException e) {
LogUtil.e(TAG, "mediaCodecDecode dequeueInputBuffer 异常 -->"+e.getMessage());
//如果解码出错,需要提示用户或者程序自动重新初始化解码
mediaCodec = null;
return;
}
try {
//使输出缓冲区出队,最多阻塞“ timeoutUs”微秒。 返回已成功解码的输出缓冲区的索引
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(info, 0);
switch (outputBufferIndex) {
case MediaCodec.INFO_TRY_AGAIN_LATER:
LogUtil.d(TAG, "mediaCodecDecode -->dequeueOutputBuffer 无可用输出");
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
MediaFormat outputFormat = mediaCodec.getOutputFormat();
LogUtil.e(TAG, "The output format has changed, new format:" + outputFormat);
mediaFormat = outputFormat;
break;
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
LogUtil.e(TAG, "this event signals that the video scaling mode may have been reset to the default");
break;
default:
ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex);
//position 和 limit 方法 解决输出混乱问题
outputBuffer.position(info.offset);
outputBuffer.limit(info.offset + info.size);
//如果配置编码器时指定了有效的 surface,传 true 将此输出缓冲区显示在 surface
mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
break;
}
} catch (Exception e) {
LogUtil.e(TAG, "mediaCodecDecode dequeueOutputBuffer 错误 -->");
e.printStackTrace();
}
}
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.