Метод onframeavailable() SurfaceTexture всегда вызывается слишком поздно
я пытаюсь получить следующий пример MediaExtractor для работы:
http://bigflake.com/mediacodec / - ExtractMpegFramesTest.java (требуется 4.1, API 16)
проблема у меня в том, что outputSurface.awaitNewImage (); кажется, всегда выбрасывает RuntimeException("время ожидания фрейма"), которое выбрасывается всякий раз, когда mFrameSyncObject.wait(TIMEOUT_MS)
время ожидания вызова. Независимо от того, что я установил TIMEOUT_MS
быть, onFrameAvailable()
всегда вызывается сразу после в происходит тайм-аут. Я пробовал с 50ms и с 30000ms, и это то же самое.
кажется onFrameAvailable()
вызов не может быть выполнен, пока поток занят, и как только тайм-аут завершает выполнение кода потока, он может проанализировать onFrameAvailable()
звонок.
кто-нибудь сумел заставить этот пример работать или знает, как MediaExtractor должен работать с текстурами GL?
Edit: пробовал это на устройствах с API 4.4 и 4.1.1 и то же самое бывает на обоих.
Edit 2:
получил его работу на 4.4 благодаря fadden. Проблема заключалась в том, что ExtractMpegFramesWrapper.runTest()
метод th.join();
который заблокировал основной поток и предотвратил onFrameAvailable()
вызов от обработки. Однажды я прокомментировал th.join();
он работает на 4.4. Я думаю, может быть ExtractMpegFramesWrapper.runTest()
сам должен был работать на еще одном потоке, чтобы основной поток не блокировался.
также была небольшая проблема на 4.1.2 при вызове codec.configure()
, он выдал ошибку:
A/ACodec(2566): frameworks/av/media/libstagefright/ACodec.cpp:1041 CHECK(def.nBufferSize >= size) failed.
A/libc(2566): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1), thread 2625 (CodecLooper)
который я решил, добавив следующее перед вызовом:
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);
однако проблема, которую я сейчас имею как на 4.1.1 (Galaxy S2 GT-I9100), так и на 4.1.2 (Samsung Galaxy Tab GT-P3110), заключается в том, что они оба всегда устанавливают информацию.размер 0 для всех кадров. Вот вывод журнала:
loop
input buffer not available
no output from decoder available
loop
input buffer not available
no output from decoder available
loop
input buffer not available
no output from decoder available
loop
input buffer not available
no output from decoder available
loop
submitted frame 0 to dec, size=20562
no output from decoder available
loop
submitted frame 1 to dec, size=7193
no output from decoder available
loop
[... skipped 18 lines ...]
submitted frame 8 to dec, size=6531
no output from decoder available
loop
submitted frame 9 to dec, size=5639
decoder output format changed: {height=240, what=1869968451, color-format=19, slice-height=240, crop-left=0, width=320, crop-bottom=239, crop-top=0, mime=video/raw, stride=320, crop-right=319}
loop
submitted frame 10 to dec, size=6272
surface decoder given buffer 0 (size=0)
loop
[... skipped 1211 lines ...]
submitted frame 409 to dec, size=456
surface decoder given buffer 1 (size=0)
loop
sent input EOS
surface decoder given buffer 0 (size=0)
loop
surface decoder given buffer 1 (size=0)
loop
surface decoder given buffer 0 (size=0)
loop
surface decoder given buffer 1 (size=0)
loop
[... skipped 27 lines all with size=0 ...]
surface decoder given buffer 1 (size=0)
loop
surface decoder given buffer 0 (size=0)
output EOS
Saving 0 frames took ? us per frame // edited to avoid division-by-zero error
поэтому изображения не сохраняются. Однако тот же код и видео работает на 4.3. Видео, которое я использую, является .mp4 файл с " H264-MPEG-4 AVC (avc1) "видеокодек и" MPEG Aaac Audio (mp4a) " аудиокодек.
я также пробовал другие видеоформаты, но они, похоже, умирают еще раньше на 4.1.х, в то время как оба работают на 4.3.
Edit 3:
я сделал, как вы предложили, и, похоже, правильно сохранить изображения кадров. Спасибо.
что касается KEY_MAX_INPUT_SIZE, я попытался не устанавливать или установить его в 0, 20, 200,... 200000000, все с таким же результатом информация.размер=0.
теперь я не могу установить рендеринг в SurfaceView или TextureView на моем макете. Я попытался заменить эту строку:
mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId());
С, где surfaceTexture
- это SurfaceTexture, определенный в моем xml-макете:
mSurfaceTexture = textureView.getSurfaceTexture();
mSurfaceTexture.attachToGLContext(mTextureRender.getTextureId());
но он бросает странную ошибку с getMessage()==null
на второй линии. Я не мог найти другого способа заставить его нарисовать какой-то вид. Как изменить декодер для отображения кадров на поверхности / SurfaceView/TextureView вместо того, чтобы спасти их?
1 ответов
путь SurfaceTexture
работает, делает это немного сложно, чтобы получить права.
на docs скажем, что доступный кадр обратного вызова "вызывается в произвольном потоке". The SurfaceTexture
класс имеет немного кода, который делает следующее При инициализации (строка 318):
if (this thread has a looper) {
handle events on this thread
} else if (there's a "main" looper) {
handle events on the main UI thread
} else {
no events for you
}
рамка-доступные события доставляются в ваше приложение через обычный Looper
/ Handler
механизм. Этот механизм является просто очередью сообщений, что означает, что поток нуждается сидеть в Looper
цикл событий, ожидающий их прибытия. Проблема в том, что если ты спишь в awaitNewImage()
, вы не смотрите Looper
очереди. Итак, событие наступает, но никто его не видит. В конце концов awaitNewImage()
тайм-аут, и поток возвращается к просмотру очереди событий, где он немедленно обнаруживает ожидающее сообщение "новый кадр".
таким образом, трюк состоит в том, чтобы убедиться, что доступные события фрейма поступают в другой поток, чем тот, который сидит в awaitNewImage()
. В ExtractMpegFramesTest
пример, это делается путем запуска теста во вновь созданном потоке (см. ExtractMpegFramesWrapper
class), который не имеет Looper
. (По какой-то причине поток, который выполняет тесты CTS, имеет looper.) Доступные события фрейма поступают в основной поток пользовательского интерфейса.
Update (для "edit 3"): мне немного грустно, что игнорирование поля "размер" помогло, но до 4.3 трудно предсказать, как будут вести себя устройства.
если вы просто хотите отобразить кадр, передай Surface
вы получаете от SurfaceView
или TextureView
на MediaCodec
декодер configure()
звонок. Тогда вам не придется возиться с SurfaceTexture
вообще -- кадры будут отображаться по мере их декодирования. См. два действия "воспроизведение видео" в графика для примера.
если вы действительно хотите пройти через SurfaceTexture
, вам нужно изменить CodecOutputSurface для рендеринга на поверхность окна, а не на pbuffer. (Внеэкранный рендеринг выполнен, поэтому мы можем использовать glReadPixels()
в безголовом тесте.)