简体   繁体   English

使用MediaCodec获取H264 IP摄像机流

[英]Get H264 ip camera stream using mediacodec

I'm trying to get the stream produced by a camera on my network. 我正在尝试获取网络上的摄像机产生的流。 The main problem is that the stream produced by this camera is an H264 stream. 主要问题是此相机产生的流是H264流。 I've look around to find the best way to manage this kind of stream and I've found that mediacodec is the most suitable class. 我四处寻找找到管理这种流的最佳方法,并且我发现mediacodec是最合适的类。 Unfortunately the documentation for this API is very poor, but despite this i've found some resources where i get the base info on this class and I've write the following code. 不幸的是,该API的文档非常差,但是尽管如此,我还是找到了一些资源来获取此类的基本信息,并且编写了以下代码。

public class DecodeActivity extends Activity implements SurfaceHolder.Callback { 公共类DecodeActivity扩展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();
        }
    }
}

} }

The problem is that when I try to run this activity i get the following error: 问题是,当我尝试运行此活动时,出现以下错误:

(20736): Unable to instantiate a decoder for type '{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}'. (20736):无法实例化类型为'{csd-1 = java.nio.ReadWriteHeapByteBuffer'的解码器,状态:容量= 8位置= 0限制= 8,高度= 360,哑剧=视频/ avc,csd-0 = java .nio.ReadWriteHeapByteBuffer,状态:容量= 31位置= 0限制= 31,宽度= 640}'。

E/MediaCodec(20736): Codec reported an error. E / MediaCodec(20736):编解码器报告了一个错误。 (omx error 0x80001003, internalError -2147483648) (omx错误0x80001003,内部错误-2147483648)

F/libc (20736): Fatal signal 11 (SIGSEGV) at 0x00000008 (code=1), thread 20752 (Thread-324) F / libc(20736):致命信号11(SIGSEGV)位于0x00000008(代码= 1),线程20752(线程324)

Is someone able to point me in the right direction? 有人能够指出我正确的方向吗? Cause I can't see what I'm doing wrong. 因为我看不到我在做什么错。 Thanks in advance 提前致谢

Pretty sure the mediaextractor is missing. 可以肯定的是,MediaExtractor丢失了。 With ur solution you miss to parse out headerinformations, in which the decoder is not interested in. 使用您的解决方案时,您会错过解析出解码器不感兴趣的标头信息的机会。

This guy goes into the right direction, but still has no solution. 这个家伙走了正确的方向,但仍然没有解决方案。

Android using MediaExtractor and MediaCodec from a socket (streaming mpeg) Android从套接字使用MediaExtractor和MediaCodec(流mpeg)

Next you didnt instantiate the decoder correctly. 接下来,您没有正确实例化解码器。 Replace line 更换线

decoder = MediaCodec.createDecoderByType(format.toString());

with

decoder = MediaCodec.createDecoderByType(format.getString(MediaFormat.KEY_MIME));

If i come up with a concrete solution, i will post it. 如果我想出一个具体的解决方案,我将发布它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM