我正在尝试在我的网络上获取相机生成的流。主要问题是该摄像机产生的流是H264流。我环顾四周找到管理这种流的最佳方法,我发现mediacodec是最合适的类。 不幸的是,这个API的文档非常差,但是尽管如此我已经找到了一些资源,我在这个类上获得了基本信息,并且我编写了以下代码。
public class DecodeActivity extends Activity实现了SurfaceHolder.Callback {
private final String sampleStreamAddress = "http://192.168.0.240:80";
private PlayerThread mPlayer = null;
String mimeType = "video/avc";
MediaFormat format = MediaFormat.createVideoFormat(mimeType, 640, 360);
final byte[] header_sps = { 0, 0, 0, 1, 103, 100, 0, 40, -84, 52, -59, 1,
-32, 17, 31, 120, 11, 80, 16, 16, 31, 0, 0, 3, 3, -23, 0, 0, -22,
96, -108 };
final byte[] header_pps = { 0, 0, 0, 1, 104, -18, 60, -128 };
private static final String tag = DecodeActivity.class.getSimpleName();
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final SurfaceView sv = new SurfaceView(this);
sv.getHolder().addCallback(this);
setContentView(sv);
format.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps));
format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public void surfaceCreated(final SurfaceHolder holder) {
Log.i(tag, "surfaceCreated");
if (mPlayer == null) {
mPlayer = new PlayerThread(holder.getSurface());
mPlayer.start();
}
}
@Override
public void surfaceChanged(final SurfaceHolder holder, final int format,
final int width, final int height) {
Log.i(tag, "surfaceChanged");
if (mPlayer == null) {
mPlayer = new PlayerThread(holder.getSurface());
mPlayer.start();
}
}
@Override
public void surfaceDestroyed(final SurfaceHolder holder) {
Log.i(tag, "surfaceDestroyed");
if (mPlayer != null) {
mPlayer.interrupt();
}
}
private class PlayerThread extends Thread {
private MediaCodec decoder;
private final Surface surface;
public PlayerThread(final Surface surface) {
this.surface = surface;
}
@Override
public void run() {
try {
Log.i(tag, "running PlayerThrad");
decoder = MediaCodec.createDecoderByType(format.toString());
decoder.configure(format, surface, null, 0);
decoder.start();
final ByteBuffer[] inputBuffers = decoder.getInputBuffers();
ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
final BufferInfo info = new BufferInfo();
boolean isEOS = false;
final long startMs = System.currentTimeMillis();
//
HttpResponse res;
final DefaultHttpClient httpclient = new DefaultHttpClient();
while (!Thread.interrupted()) {
if (!isEOS) {
final int inIndex = decoder.dequeueInputBuffer(10000);
if (inIndex >= 0) {
final byte buffer2[] = new byte[18800 * 8 * 8 * 8];
final ByteBuffer buffer = inputBuffers[inIndex];
final HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(
httpParameters, 200);
httpclient.setParams(httpParameters);
Log.d(tag, " URL : " + sampleStreamAddress);
res = httpclient.execute(new HttpGet(URI
.create(sampleStreamAddress)));
final DataInputStream mjpegInputStream = new DataInputStream(
res.getEntity().getContent());
final int sampleSize = mjpegInputStream.read(
buffer2, 0, 18800 * 4);
buffer.clear();
buffer.put(buffer2, 0, sampleSize);
buffer.clear();
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, 0, 0);
// extractor.advance();
}
}
}
final 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:
final ByteBuffer buffer = outputBuffers[outIndex];
Log.v("DecodeActivity",
"We can't use this buffer but render it due to the API limit, "
+ buffer);
// We use a very simple clock to keep the video FPS, or
// the
// video
// playback will be too fast
while (info.presentationTimeUs / 1000 > System
.currentTimeMillis() - startMs) {
try {
sleep(10);
} catch (final InterruptedException e) {
e.printStackTrace();
break;
}
}
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;
}
}
decoder.stop();
decoder.release();
} catch (final Exception e) {
e.printStackTrace();
}
}
}
}
问题是,当我尝试运行此活动时,我收到以下错误:
(20736):无法为类型'{csd-1 = java.nio.ReadWriteHeapByteBuffer,status:capacity = 8 position = 0 limit = 8,height = 360,mime = video / avc,csd-实例化解码器0 = java.nio.ReadWriteHeapByteBuffer,status:capacity = 31 position = 0 limit = 31,width = 640}'。
E / MediaCodec(20736):编解码器报告错误。 (omx错误0x80001003,internalError -2147483648)
F / libc(20736):致命信号11(SIGSEGV)位于0x00000008(代码= 1),线程20752(线程324)
有人能指出我正确的方向吗?因为我看不出我做错了什么。 提前致谢
答案 0 :(得分:1)
非常确定缺少媒体提取器。使用ur解决方案,您将错过解析头信息,解码器对此不感兴趣。
这家伙走向正确的方向,但仍然没有解决方案。
Android using MediaExtractor and MediaCodec from a socket (streaming mpeg)
接下来你没有正确地实例化解码器。 替换行
decoder = MediaCodec.createDecoderByType(format.toString());
带
decoder = MediaCodec.createDecoderByType(format.getString(MediaFormat.KEY_MIME));
如果我想出具体的解决方案,我会发布它。