Mediacodec, decode byte packet from server and render it on surface

房东的猫 提交于 2020-01-21 10:20:30

问题


I have some issues with MediaCode.

I have 3 components; Decoder, Downloader and Render. And Simple FragmentStreamVideo that initialize the 'SurfaceView' and the 'Downloader'.

The other components like the Render and Decoder are initialized in the SurfaceView. Then, a syncronize is done between the Decoder and the Dowloader, implemented by BlockingQueue<String> queue where String = Filename (Each frame has its file).

Another syncronize between Decode and Render is done by the standard ByteBuffer as stated in documentation.

Find below my piece of code. Would be very grateful if you can help.

SurfaceView

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private Render drawThread;
private Decoder decoder;
BlockingQueue<String> queue;
Context mContext;
public MySurfaceView(Context context,BlockingQueue<String> queue){
    super(context);
    this.queue = queue;
    mContext = context;
    getHolder().addCallback(this);
}
public void setRunning(boolean value){
    drawThread.setRunning(value);
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    //drawThread = new Render(getHolder().getSurface(),300,300);
    drawThread = new Render(getHolder().getSurface(),352,288);
    decoder = new Decoder(drawThread,queue,mContext);
    decoder.start();
    drawThread.setRunning(true);
    drawThread.start();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    boolean retry = true;
    // завершаем работу потока
    drawThread.setRunning(false);
    while (retry) {
        try {
            drawThread.join();
            retry = false;
        } catch (InterruptedException e) {
            // если не получилось, то будем пытаться еще и еще
        }
    }

}

Render

public class Render extends Thread{
private boolean mConfigured = false;
private long mTimeoutUs;
private MediaCodec mDecoder;
volatile boolean mRunning = false;
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 };
byte[] header_pps = { 0, 0, 0, 1, 104, -18, 60, -128 };

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public Render(Surface surface, int width, int height){
    mTimeoutUs = 1000l;
    //возможны проблемы с инициализацией
    ByteBuffer csd0 = ByteBuffer.allocate(2);
    csd0.put((byte) MediaCodec.BUFFER_FLAG_CODEC_CONFIG);

    try {
        configure(surface,width,height,csd0);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

synchronized void configure(Surface surface, int width, int height,
                            ByteBuffer csd0) throws IOException {
    if (mConfigured) { // просто флаг, чтобы знать, что декодер готов
        throw new IllegalStateException();
    }
    // создаем видео формат
    MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);
    // передаем наш csd-0
    //format.setByteBuffer("csd-0", csd0);
    //format.setByteBuffer("csd-0",ByteBuffer.wrap(header_sps));
    format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
    // создаем декодер
    mDecoder = MediaCodec.createDecoderByType("video/avc");
    // конфигурируем декодер
    mDecoder.configure(format, surface, null, 0);
    mDecoder.start();
    mConfigured = true;
}
void decodeSample(byte[] data, int offset, int size, long presentationTimeUs, int flags) {
    if (mConfigured) {
        // вызов блокирующий
        int index = mDecoder.dequeueInputBuffer(mTimeoutUs);
        if (index >= 0) {
            ByteBuffer buffer = mDecoder.getInputBuffers()[index];
            buffer.clear(); // обязательно сбросить позицию и размер буфера
            buffer.put(data, offset, size);
            // сообщаем системе о доступности буфера данных
            mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
        }
    }
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void run() {
    try {
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); // переиспользуем BufferInfo
        long startMs = System.currentTimeMillis();
        while (mRunning) {
            if (mConfigured) { // если кодек готов
                int index = mDecoder.dequeueOutputBuffer(info, mTimeoutUs);
                if (index >= 0) { // буфер с индексом index доступен
                    // info.size > 0: если буфер не нулевого размера, то рендерим на Surface
                    while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
                        try {
                            sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            break;
                        }
                    }
                    mDecoder.releaseOutputBuffer(index, info.size > 0);
                    // заканчиваем работу декодера если достигнут конец потока данных
                    if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                        mRunning = false;
                        break;
                    }
                }
            } else {
                // просто спим, т.к. кодек не готов
                try {
                    Thread.sleep(10);
                } catch (InterruptedException ignore) {
                }
            }
        }
    } finally {
        // освобождение ресурсов
        release();
    }
}
public void setRunning(boolean value){
    mRunning = value;
}
void release() {
    if (mConfigured) {
        mDecoder.stop();
        mDecoder.release();
    }
}

Decoder

public class Render extends Thread{
private boolean mConfigured = false;
private long mTimeoutUs;
private MediaCodec mDecoder;
volatile boolean mRunning = false;
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 };
byte[] header_pps = { 0, 0, 0, 1, 104, -18, 60, -128 };

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public Render(Surface surface, int width, int height){
    mTimeoutUs = 1000l;
    //возможны проблемы с инициализацией
    ByteBuffer csd0 = ByteBuffer.allocate(2);
    csd0.put((byte) MediaCodec.BUFFER_FLAG_CODEC_CONFIG);

    try {
        configure(surface,width,height,csd0);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

synchronized void configure(Surface surface, int width, int height,
                            ByteBuffer csd0) throws IOException {
    if (mConfigured) { // просто флаг, чтобы знать, что декодер готов
        throw new IllegalStateException();
    }
    // создаем видео формат
    MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);
    // передаем наш csd-0
    //format.setByteBuffer("csd-0", csd0);
    //format.setByteBuffer("csd-0",ByteBuffer.wrap(header_sps));
    format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
    // создаем декодер
    mDecoder = MediaCodec.createDecoderByType("video/avc");
    // конфигурируем декодер
    mDecoder.configure(format, surface, null, 0);
    mDecoder.start();
    mConfigured = true;
}
void decodeSample(byte[] data, int offset, int size, long presentationTimeUs, int flags) {
    if (mConfigured) {
        // вызов блокирующий
        int index = mDecoder.dequeueInputBuffer(mTimeoutUs);
        if (index >= 0) {
            ByteBuffer buffer = mDecoder.getInputBuffers()[index];
            buffer.clear(); // обязательно сбросить позицию и размер буфера
            buffer.put(data, offset, size);
            // сообщаем системе о доступности буфера данных
            mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
        }
    }
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void run() {
    try {
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); // переиспользуем BufferInfo
        long startMs = System.currentTimeMillis();
        while (mRunning) {
            if (mConfigured) { // если кодек готов
                int index = mDecoder.dequeueOutputBuffer(info, mTimeoutUs);
                if (index >= 0) { // буфер с индексом index доступен
                    // info.size > 0: если буфер не нулевого размера, то рендерим на Surface
                    while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
                        try {
                            sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            break;
                        }
                    }
                    mDecoder.releaseOutputBuffer(index, info.size > 0);
                    // заканчиваем работу декодера если достигнут конец потока данных
                    if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                        mRunning = false;
                        break;
                    }
                }
            } else {
                // просто спим, т.к. кодек не готов
                try {
                    Thread.sleep(10);
                } catch (InterruptedException ignore) {
                }
            }
        }
    } finally {
        // освобождение ресурсов
        release();
    }
}
public void setRunning(boolean value){
    mRunning = value;
}
void release() {
    if (mConfigured) {
        mDecoder.stop();
        mDecoder.release();
    }
}
}

Downloader (Not full because more function Work)
in readFramePacket function I'm save file and put it on queue it's work fine.

public class Downloader extends Thread{
private final byte COMMAND_ONE = (byte)0x01;
private final byte COMMAND_TEN = (byte)0x10;

private final byte MAIN_TYPE_STREAM =(byte) 0x00;
private final byte SUPPORT_TYPE_STREAM =(byte) 0x01;
private final byte CONTROL_CODE_DISCONNECT =(byte) 0x00;
private final byte CONTROL_CODE_CONNECT =(byte) 0x01;

private final byte DELIMITER = (byte)':';
private final int FIX_PACKET_HEAD_SIZE = 5;
private final int SECOND_HEAD_FIX_SIZE = 4;
private final int FIX_DELIMITER_SIZE = 1;
private static final int HEAD_OF_PACKET = -1;
private static final int SECOND_HEAD_OF_PACKET = 0;
private static final int DATA_PACKET = 1;
private static final int PACKETS_END = -2;
private static final int LAST_PACKET = 8;
private static final int COMMAND_INDEX = 0;
private static final int SOCKET_TIMEOUT_LENGTH = 5000;
private String log;
private String pass;
String host;
int portNumber;
BlockingQueue<String> queue;
Socket socket;
Context mContext;
OutputStream out;
int cameraID;
int value = 0;

volatile boolean mRunning = false;


public Downloader(Server server,BlockingQueue<String> queue,int cameraID,Context context){
    log = server.getLogin();
    pass = server.getPassword();
    host = server.getIP();
    portNumber = Integer.parseInt(server.getPort());
    this.queue = queue;
    mRunning = true;
    this.cameraID = cameraID;
    mContext = context;
}
public void stopStreaming(){
    mRunning = false;
}

@Override
public void run() {
    try {
        SocketAddress sockaddr = new InetSocketAddress(host, portNumber);
        socket = new Socket();
        socket.connect(sockaddr, SOCKET_TIMEOUT_LENGTH);
        //socket.setSoTimeout(SOCKET_TIMEOUT_LENGTH);

        InputStream inputStream = new BufferedInputStream(socket.getInputStream());
        out = socket.getOutputStream();
        byte[] command = createAuthenticationPacket(log, pass);
        out.write(command);
        readAuthPacket(inputStream);
        byte[] connectCommand = packetToConnectDisconnectCamera(cameraID,SUPPORT_TYPE_STREAM,CONTROL_CODE_CONNECT);
        out.write(connectCommand);
        initializeVideoStream(inputStream);
        readFramePacket(inputStream);

    } catch (SocketException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchProviderException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

来源:https://stackoverflow.com/questions/29897701/mediacodec-decode-byte-packet-from-server-and-render-it-on-surface

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!