Я пытаюсь показать rtsp-видео в формате H.264 на устройстве Android. Поток исходит от Raspberry Pi, используя vlc для кодирования /dev/video1
, который является «платой камеры Pi NoIR».
vlc-wrapper -vvv v4l2:///dev/video1 --v4l2-width $WIDTH --v4l2-height $HEIGHT --v4l2-fps ${FPS}.0 --v4l2-chroma h264 --no-audio --no-osd --sout "#rtp{sdp=rtsp://:8000/pi.sdp}" :demux=h264 > /tmp/vlc-wrapper.log 2>&1
Я использую очень минимальный код Android прямо сейчас:
final MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDisplay(holder);
try {
mediaPlayer.setDataSource(url);
mediaPlayer.prepare();
и получаю сообщение «Ошибка подготовки: статус = 0x1» IOException
. Когда я смотрю на журналы, я вижу такие строки, как
06-02 16:28:05.566 W/APacketSource( 316): Format:video 0 RTP/AVP 96 / MIME-Type:H264/90000
06-02 16:28:05.566 W/MyHandler( 316): Unsupported format. Ignoring track #1.
06-02 16:28:05.566 I/MyHandler( 316): SETUP(1) completed with result -1010 (Unknown error 1010)
исходящие из системного процесса. Поиск этих сообщений указывает на источники libstagefright/rtsp
и похоже означает, что вызов ASessionDescription::getDimensions
в конструкторе APacketSource::APacketSource
завершается ошибкой. Это не похоже на то, что это должно происходить, потому что VLC точно знает, какие размеры выводить:
[0x1c993a8] v4l2 demux debug: trying specified size 800x600
[0x1c993a8] v4l2 demux debug: Driver requires at most 262144 bytes to store a complete image
[0x1c993a8] v4l2 demux debug: Interlacing setting: progressive
[0x1c993a8] v4l2 demux debug: added new video es h264 800x600
Что похоже происходит, так это то, что ASessionDescription::getDimensions
ищет атрибут framesize
в (казалось бы правильно сформированных) DESCRIBE
результатах.
06-02 16:28:05.566 I/MyHandler( 316): DESCRIBE completed with result 0 (Success)
06-02 16:28:05.566 I/ASessionDescription( 316): v=0
06-02 16:28:05.566 I/ASessionDescription( 316): o=- 15508012299902503225 15508012299902503225 IN IP4 pimple
06-02 16:28:05.566 I/ASessionDescription( 316): s=Unnamed
06-02 16:28:05.566 I/ASessionDescription( 316): i=N/A
06-02 16:28:05.566 I/ASessionDescription( 316): c=IN IP4 0.0.0.0
06-02 16:28:05.566 I/ASessionDescription( 316): t=0 0
06-02 16:28:05.566 I/ASessionDescription( 316): a=tool:vlc 2.0.3
06-02 16:28:05.566 I/ASessionDescription( 316): a=recvonly
06-02 16:28:05.566 I/ASessionDescription( 316): a=type:broadcast
06-02 16:28:05.566 I/ASessionDescription( 316): a=charset:UTF-8
06-02 16:28:05.566 I/ASessionDescription( 316): a=control:rtsp://192.168.1.35:8000/pi.sdp
06-02 16:28:05.566 I/ASessionDescription( 316): m=video 0 RTP/AVP 96
06-02 16:28:05.566 I/ASessionDescription( 316): b=RR:0
06-02 16:28:05.566 I/ASessionDescription( 316): a=rtpmap:96 H264/90000
Это похоже, что это может быть ошибка Stagefright: он знает (или должен знать), что имеет поток, закодированный в H.264, но, похоже, ожидает атрибут H.263 framesize
. Отсюда мои вопросы:
- Я правильно читаю источники? Проблема в вызове
ASessionDescription::getDimensions
? (Поддерживает ли stagefright только потоковую передачу H.263?) - Или код на стороне Пи в чем-то неверен?
- Или я просто пропустил ключевой шаг или два в своем коде на стороне клиента?
Обновление, 20140606:
В документах MediaPlayer
говорится, что -1010 — это MEDIA_ERROR_UNSUPPORTED: "Bitstream соответствует соответствующему стандарту кодирования или спецификации файла, но среда мультимедиа не поддерживает эту функцию». Это заставляет меня задаться вопросом, является ли проблема «стандартной» прогрессивной загрузкой. То есть Поддерживаемые форматы мультимедиа
Для видеоконтента, который передается через HTTP или RTSP [в] MPEG-4 [контейнере], атом
moov
должен предшествовать любым атомамmdat
, но должен следовать за атомомftyp
.
в то время как большинство потоков ставят атом moov
последним.
Я не совсем уверен, как это проверить, хотя!
- Я не вижу атомов
moov
илиftyp
в источнике vlc. (Мне сказали, что здесь vlc просто транслируется; что фактический контент H264 выходит из драйвера камеры.) - Я не вижу атомов
moov
илиftyp
в https://github.com/raspberrypi linux или пользовательских ветках. (Возможно, я просто ищу не те вещи, хотя.) - Когда vlc сохраняет поток, я получаю файл mp4 с
moov
передmdat
, но, конечно, здесь vlc может выполнять транскодирование.
Обновление, 20140610:
проигрыватель GPAC "Osmo4" может отображать поток на планшете Android 4.3. Плохо (большая задержка, чем у VLC на ноутбуке, и склонность к зависаниям), но может отображать это.
Обновление, 20140616:
Когда я снова попытался просмотреть исходники VLC (на этот раз без учета регистра и без ориентации по словам), я нашел макросы FOURCC, определяющие атомы moov
и ftyp
в modules/mux/mp4.c
, что быстро привело к переключателям --sout-mp4-faststart
(и --no-sout-mp4-faststart
)... которые не имеют никакого значения.
Таким образом, похоже, что на самом деле это может быть не проблема порядка атомов. Это хорошо знать, если это закрывает целый класс тупиков, но это заставляет меня биться головой о стену (что, кажется, всегда наносит больше вреда моей голове, чем стене) без подсказки.
Обновление, 20140702:
Я скомпилировал VLC для Android, и он может отображать поток, сгенерированный VLC, на пи. Он помещает изображение в верхний левый угол экрана; Я попытался написать свой собственный скин для их .so и не смог найти никаких «кнопок», которые позволяли бы мне приближаться к поверхности или что-то в этом роде. (Плюс .apk стал около 12M!)
Итак, я нашел соответствующие RFC и написал свой собственный RTSP-клиент. Или пытался: я могу разобрать SDP и сгенерировать достаточно действительный RTSP для получения датаграммы RTP и RTCP, и я могу анализировать заголовки RTP и RTCP. Но даже несмотря на то, что SDP утверждает, что доставляет m=video 0 RTP/AVP 96 и a=rtpmap :96 H264/90000, MediaCodec
не будет отображать видео на моей поверхности, независимо от того, какой из трех кодеков H264 на моем планшете я перейду на MediaCodec.createByCodecName(), и когда я смотрю на полезную нагрузку RTP, я не слишком удивлен: я не не видите шаблон синхронизации NAL в любом из пакеты.
Вместо этого они все начинаются либо с 21 9A __ 22 FF
(обычно), либо иногда с 3C 81 9A __ 22 FF
, где __ кажется всегда четным числом, которое увеличивается на 2 с каждым пакетом. Я не узнаю этот узор, а вы?
Обновление, 20140711:
Оказывается, пакеты H264 не обязательно должны начинаться с шаблона синхронизации NAL — это необходимо только в том случае, если блоки NAL могут быть встроены в больший поток данных. Мои RTP-пакеты имеют формат RFC 6184.