Я пытаюсь показать видео rtsp с кодировкой H.264 на устройстве Android. Поток поступает из малины Pi, используя vlc для кодирования /dev/video1
, который является "Pi NoIR Camera Board".
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)
поступающий из системного процесса. Grepping для этих сообщений указывает на источники 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?) - Или неправильный код Pi-side?
- Или я просто пропустил один или два ключа в своем клиентском коде?
Обновление, 20140606:
MediaPlayer
docs говорят, что -1010 MEDIA_ERROR_UNSUPPORTED: "Битовый поток соответствует соответствующему стандарту кодирования или спецификации файла, но медиа-инфраструктура не поддерживает эту функцию". Это заставляет меня задаться вопросом, является ли проблема "стандартной" проблемой прогрессивной загрузки. То есть Поддерживаемые форматы носителей говорит
Для видеоконтента, транслируемого через HTTP или RTSP [в] MPEG-4 [контейнер], атом
moov
должен предшествовать любым атомамmdat
, но должен преуспеть вftyp
atom
в то время как большинство потоков помещают атом moov
последним.
Я не совсем уверен, как это проверить!
- Я не вижу атомов
moov
илиftyp
в источнике vlc. (Мне сказали, что vlc просто потоковая передача здесь, что фактическое содержимое H264 выходит из драйвера камеры.) - Я не вижу атомов
moov
илиftyp
в https://github.com/raspberrypi ветвях linux или userland. (Может быть, я просто готовлюсь к неправильным вещам.) - Когда у меня есть 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 = видео 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.