WedX - журнал о программировании и компьютерных науках

Невозможно отображать потоковые данные в реальном времени на Android

Я пытаюсь отобразить необработанные данные H264 с устройства камеры в своем приложении для Android. Я могу получать данные в Textview, но не могу отображать их в Textureview. Я новичок в Android и не разбираюсь в декодировании необработанных данных. Был бы признателен, если бы кто-то мог предложить решение. Пожалуйста, найдите следующие коды:

Код для получения данных

    public class myVideoReceiver extends Thread {
    public boolean bKeepRunning2 = true;
    public String lastMessage2 = "";

    public void run() {
        String message2;
        byte[] lmessage2 = new byte[MAX_UDP_DATAGRAM_LEN2];
        DatagramPacket packet2 = new DatagramPacket(lmessage2, lmessage2.length);

        try {
            DatagramSocket socket2 = new DatagramSocket(UDP_SERVER_PORT2);

            while(bKeepRunning2) {
                socket2.receive(packet2);
                message2 = new String(lmessage2, 0, packet2.getLength());
                lastMessage2 = message2;
                runOnUiThread(updateTextMessage2);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }

        if (mysocket != null) {
            mysocket.close();
        }
    }

    public void kill() {
        bKeepRunning2 = false;
    }

    public String getLastMessage() {
        return lastMessage2;

    }  }
   //Added release function
    public void release() {
    }

    public Runnable updateTextMessage2 = new Runnable() {
    public void run() {
        if (myVideoReceiver == null) return;
        VIDEO_RESPONSE.setText(myVideoReceiver.getLastMessage());

    }
};

Код для отображения необработанных данных в представлении «Текстура»:

public class MainActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener{

private TextureView m_surface;// View that contains the Surface Texture

private myVideoReceiver provider;// Object that connects to our server and gets H264 frames

private MediaCodec m_codec;// Media decoder

private DecodeFramesTask m_frameTask;// AsyncTask that takes H264 frames and uses the decoder to update the Surface Texture

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Get a referance to the TextureView in the UI
    m_surface = (TextureView)findViewById(R.id.textureView);

    // Add this class as a call back so we can catch the events from the Surface Texture
    m_surface.setSurfaceTextureListener(this);
}

@Override
// Invoked when a TextureView's SurfaceTexture is ready for use
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        // when the surface is ready, we make a H264 provider Object. When its constructor runs it starts an AsyncTask to log into our server and start getting frames
        provider = new myVideoReceiver();

        // Create the format settinsg for the MediaCodec
        MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080);// MIMETYPE: a two-part identifier for file formats and format contents
        // Set the PPS and SPS frame
        format.setByteBuffer("csd-0", ByteBuffer.wrap(provider.lastMessage2.getBytes()));
        // Set the buffer size
        format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 100000);

        try {
            // Get an instance of MediaCodec and give it its Mime type
            m_codec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
            // Configure the Codec
            m_codec.configure(format, new Surface(m_surface.getSurfaceTexture()), null, 0);
            // Start the codec
            m_codec.start();
            // Create the AsyncTask to get the frames and decode them using the Codec
            m_frameTask = new DecodeFramesTask();
            m_frameTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        }catch(Exception e){
            e.printStackTrace();
        }
}

@Override
// Invoked when the SurfaceTexture's buffers size changed
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}

@Override
// Invoked when the specified SurfaceTexture is about to be destroyed
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    return false;
}

@Override
// Invoked when the specified SurfaceTexture is updated through updateTexImage()
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}

private class DecodeFramesTask extends AsyncTask<String, String, String> {

    @Override
    protected String doInBackground(String... data) {
        while(!isCancelled()) {
            // Get the next frame
            //byte[] frame = provider.nextFrame();
            //New code
            byte[] frame = provider.lastMessage2.getBytes();
            Log.e("Frame", "Value in frame data : "+frame);
            Log.e("Frame length","Frame length"+frame.length);
            // For getting the 'frame.length' 
            for(int i = 0; i < 10 && i < frame.length; i++) {
                Log.e("Framelength","Frame length"+frame.length);
            }

            // Now we need to give it to the Codec to decode into the surface

            // Get the input buffer from the decoder
            int inputIndex = m_codec.dequeueInputBuffer(-1);// Pass in -1 here as in this example we don't have a playback time reference
            Log.e("InputIndex","Value in Input index : "+inputIndex);
            // If  the buffer number is valid use the buffer with that index
            if(inputIndex>=0) {
                ByteBuffer buffer = null;
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
                    buffer = m_codec.getInputBuffer(inputIndex);

                    Log.e("MycustomData","Value in Buffer :"+buffer);
                    Log.e("If InputIndex","if Input index : "+inputIndex);
                }
                buffer.put(frame);
                // tell the decoder to process the frame
                m_codec.queueInputBuffer(inputIndex, 0, frame.length, 0, 0);
            }

            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            int outputIndex = m_codec.dequeueOutputBuffer(info, 0);
            Log.e("MycustomData","value in outputIndex: "+outputIndex);
            if (outputIndex >= 0) {
                Log.e("Inside if outputindex","value : "+outputIndex);
                m_codec.releaseOutputBuffer(outputIndex, true);
            }

            // wait for the next frame to be ready, our server makes a frame every 250ms
            try{Thread.sleep(250);}catch (Exception e){e.printStackTrace();}
        }
        return "";
    }

    @Override
    protected void onPostExecute(String result) {
        try {
            m_codec.stop();
            m_codec.release();
        }catch(Exception e){
            e.printStackTrace();
        }
        provider.release();
    }

}

@Override
public void onStop(){
    super.onStop();
    m_frameTask.cancel(true);
    provider.release();
}
}

Заранее спасибо

Пожалуйста, найдите приведенный ниже журнал ошибок:

E/Frame: Value in frame data : [B@aa987a7
E/InputIndex: Value in Input index : 0
E/MycustomData: Value in Buffer :java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
E/If InputIndex: if Input index : 0
E/MycustomData: value in outputIndex: -1
I/ViewRootImpl: jank_removeInvalidNode all the node in jank list is out of time
W/InputMethodManager: startInputReason = 1
W/libEGL: EGLNativeWindowType 0x7c452aa010 disconnect failed
D/ViewRootImpl[Page_01]: surface should not be released
E/Frame: Value in frame data : [B@5dcf743
E/InputIndex: Value in Input index : 1
E/MycustomData: Value in Buffer :java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
E/If InputIndex: if Input index : 1
E/MycustomData: value in outputIndex: -1
E/Frame: Value in frame data : [B@33de1c0
E/InputIndex: Value in Input index : 2
E/MycustomData: Value in Buffer :java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
E/If InputIndex: if Input index : 2
E/MycustomData: value in outputIndex: -1
E/Frame: Value in frame data : [B@6a1cef9
E/InputIndex: Value in Input index : 3
E/MycustomData: Value in Buffer :java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
E/If InputIndex: if Input index : 3
E/MycustomData: value in outputIndex: -1
E/Frame: Value in frame data : [B@d8b2e3e
E/InputIndex: Value in Input index : 4
E/MycustomData: Value in Buffer :java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
E/If InputIndex: if Input index : 4
E/MycustomData: value in outputIndex: -1
E/Frame: Value in frame data : [B@e4dc49f
E/InputIndex: Value in Input index : 0
E/MycustomData: Value in Buffer :java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
E/If InputIndex: if Input index : 0
E/MycustomData: value in outputIndex: -1
E/Frame: Value in frame data : [B@aff59ec
E/InputIndex: Value in Input index : 1
E/MycustomData: Value in Buffer :java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
E/If InputIndex: if Input index : 1
E/MycustomData: value in outputIndex: -1
E/Frame: Value in frame data : [B@4a761b5
E/InputIndex: Value in Input index : 2
E/MycustomData: Value in Buffer :java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
E/If InputIndex: if Input index : 2
E/MycustomData: value in outputIndex: -1
E/Frame: Value in frame data : [B@207f04a
E/InputIndex: Value in Input index : 3
E/MycustomData: Value in Buffer :java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
E/If InputIndex: if Input index : 3
E/MycustomData: value in outputIndex: -1
E/Frame: Value in frame data : [B@bd40bbb
E/InputIndex: Value in Input index : 4
E/MycustomData: Value in Buffer :java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
E/If InputIndex: if Input index : 4
E/MycustomData: value in outputIndex: -1

Скриншот:

потоковые данные

Обновлен скриншот Передача изображения


  • Итак, какая часть не работает? Какие части работают? 03.01.2020
  • Код для получения данных работает. Я могу просматривать поток данных в текстовом представлении: VIDEO_RESPONSE.setText(myVideoReceiver.getLastMessage()). Но код для отображения данных на TextureView не работает. Есть ли ошибка в приведенном выше коде? 03.01.2020
  • Есть ли ошибка? Я бы так и подозревал. Заполняется ли buffer правильными данными? Удачны ли вызовы queueInputBuffer()? И если да, то успешны ли вызовы dequeueOutputBuffer()/releaseOutputBuffer()? 03.01.2020
  • Я внес изменение в код. Вместо byte[]frame = provider.nextFrame(); новым кодом будет byte[]frame = provider.lastMessage2.getBytes();. И когда я проверил, значение в буфере равно java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304], для queueInputBuffer() значение входного индекса равно 3, для dequeueOutputBuffer() значение outputIndex равно: - 1. 05.01.2020
  • Позвольте мне объяснить, как это работает. Код для получения данных прослушивает определенный порт. Я разместил кнопку, чтобы начать потоковую передачу. Поэтому, когда я нажимаю кнопку, данные из порта будут передаваться и отображаться в TextView с именем VIDEO_RESPONSE. Пожалуйста, найдите скриншот. 05.01.2020
  • Я подозреваю, что buffer не заполняется правильными данными. Почему бы вам не сбросить длину frame, а также первые 10 или около того байтов этого массива в течение примерно 10 итераций. Я хочу убедиться, что присутствуют разделители NALU и что кадры являются правильными кадрами H.264. Вы также должны добавить содержимое csd-0 к своему вопросу. 05.01.2020
  • Я пробовал следующее, но мое приложение падает. for(int i = 0; i ‹= frame.length; i++) { Log.e(данные кадра,данные+кадр[i]); }. Должен ли я поместить код внутри onPostExecute()?. Кроме того, когда я запускаю приложение, я вижу разбитое изображение в представлении Surface. Раньше это было незаметно. Он начал отображать данные в представлении Surface? Пожалуйста, найдите новый скриншот. 10.01.2020
  • Вы должны быть более осторожны с индексами массива. Вы вызываете ArrayIndexOutOfBoundsException, потому что читаете за конец массива. Измените условие цикла на i < 10 && i < frame.length (обратите внимание на отсутствие <=). Я не хочу видеть здесь тысячи байтов; только первые 10 байтов каждого кадра плюс длина кадра для первых 10 кадров. Я не знаю, почему вы получаете неработающее изображение; Я никогда раньше не видел, чтобы SurfaceView так делал. 10.01.2020
  • Я попытался зарегистрировать внутренний цикл 'frame.length', но он не работает. Поэтому я поместил отдельный журнал для «frame» и «frame.length» вне цикла. Пожалуйста, найдите обновленный код выше. Значение, которое я получаю, для «frame.length»: 0 и для «frame»: [B@a4eb94b. Я также обновил строку для настройки кадров PPS и SPS как format.setByteBuffer(csd-0, ByteBuffer.wrap(provider.lastMessage2.getBytes())); 12.01.2020
  • Я не хочу, чтобы вы поняли это неправильно, но, похоже, у вас много проблем при работе с массивами Java. Новичкам не следует писать сложные системы потокового видео. Я занимался кодированием более 10 лет, прежде чем попытался декодировать свой первый кадр H.264. Я считаю, что здесь вы откусили больше, чем можете прожевать, и вам, вероятно, следует потратить несколько лет, чтобы сосредоточиться и изучить основы, прежде чем пытаться решить проблему такого масштаба. Я не могу вам помочь, пока вы не сможете успешно выгрузить точные данные, которые я просил; это не то. 12.01.2020

Новые материалы

Как создать диаграмму градиентной кисти с помощью D3.js
Резюме: Из этого туториала Вы узнаете, как добавить градиентную кисть к диаграмме с областями в D3.js. Мы добавим градиент к значениям SVG и применим градиент в качестве заливки к диаграмме с..

Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что это выглядит сложно…
Просто начните и учитесь самостоятельно Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что он кажется мне сложным, и я бросил его. Это в основном инструмент..

Лицензии с открытым исходным кодом: руководство для разработчиков и создателей
В динамичном мире разработки программного обеспечения открытый исходный код стал мощной парадигмой, способствующей сотрудничеству, инновациям и прогрессу, движимому сообществом. В основе..

Объяснение документов 02: BERT
BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

Как проанализировать работу вашего классификатора?
Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

Работа с цепями Маркова, часть 4 (Машинное обучение)
Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..


Для любых предложений по сайту: [email protected]