AS3 NetStream AppendBytes Искать проблему
У меня проблемы с NetStream в AS3. Проект, над которым я работаю, позволяет пользователям просматривать видео (локально) и воспроизводить его. У меня проблема в том, что netStream.seek(0);
из того, что я могу сказать, он ничего не делает, хотя я попадаю в функцию NetStatusEvent и NetStream.Seek.Notify
срабатывает. Я использую NativeProcess, и следующая функция - это какая-то разница.
public function ProgressEventOutputHandler(e:ProgressEvent):void {
videoByteArray = new ByteArray();
nativeProcess.standardOutput.readBytes(videoByteArray, 0, nativeProcess.standardOutput.bytesAvailable);
netStream.appendBytes(videoByteArray);
}
Я что-то здесь упускаю? Я приостанавливаю netStream перед использованием netStream.seek(0);
,
РЕДАКТИРОВАТЬ:
В попытке решить эту проблему я следовал инструкциям VC.One я сделал следующее:
Перевезу
videoByteArray = new ByteArray();
к моей функции инициализации, а также создалtempVideoByteArray = new ByteArray();
в этой функции.Обновите мою функцию ProgressEventOutputHandler, чтобы она больше не создавала новый ByteArray для videoByteArray и изменила эту строку -
nativeProcess.standardOutput.readBytes(videoByteArray, videoByteArray.length, nativeProcess.standardOutput.bytesAvailable);
Я ничего не изменил, и теперь видео не будет загружаться. Если я позволю создать новый ByteArray внутри функции ProgressEventOutputHandler, видео снова загрузится.
1 ответ
Укороченная версия:
Попробуйте код, который я вставил сюда: ссылка на Github Snippet
Длинная версия:
это довольно долго, но надеюсь, что это поможет раз и навсегда... Не беспокойтесь о кирпичной стене, стены созданы для того, чтобы их разбить. Чтобы вдохновить вас, ознакомьтесь с некоторыми внутренними демонстрациями из VC:One labs, использующими appendBytes
:
- MP4 Ищу эксперимент: исследование для
appendBytes
доступ к данным кадра и обработка времени / поиска. Кадры в реальном времени конвертируются из формата MP4 в формат FLV, используя только код AS3. - Регулировка скорости аудио и видео: для воспроизведения звука в формате MP3 в реальном времени в эксперименте по разделению видео и эффектам. Требуется файл MP4/FLV с данными MP3 в аудиодорожке.
- Синхронизированные видеокадры: для отображения нескольких видео с одинаковым номером кадра.
PS: я буду использовать URLStream
метод, поскольку это более полезный ответ для тех, кто загружает локальные или онлайн-файлы. Вы можете изменить с urlstream.progressEvent
к твоему обычному nativeProcess.progressEvent
,
Я знаю FFMPEG, но использовал AIR только для создания приложений для Android. Так что для этого подключения AIR/FFMPEG вы знаете больше, чем я.
Также этот ответ предполагает, что вы используете FLV с MPEG H.264 видео и MP3 или AAC аудио.
ffmpeg -i input.mp4 -c:v copy -c:a mp3 -b:a 128k -ac 2 -ar 44100 FLV_with_MP3.flv
Это предположение имеет значение, потому что оно влияет на то, какие байты мы ищем. В случае вышеупомянутого FLV с видео H.264 и аудио AAC или MP3 мы можем ожидать следующее (при поиске):
- Поскольку это MPEG, первый тег видео будет содержать байты конфигурации декодера AVC, а первый тег аудио содержит байты конфигурации конкретного аудио. Эти данные не являются реальными медиакадрами, а просто упакованы как тег аудио / видео. Они необходимы для воспроизведения MPEG. Эти же байты можно найти в
STSD
запись метаданных (MOOV
атом) внутри контейнера MP4. Теперь следующий найденный тег видео будет (или должен) быть фактическим первым кадром видео. - Кадр видео: начинается 0x09, а следующий 11-й байт равен 0x17, а 12-й байт равен 0x01.
- Аудио TAG AAC: начинается 0x08, а следующий 11-й байт - 0xAF, а 12-й байт - 0x01.
- Аудио TAG MP3: начинается 0x08, а следующий 11-й байт равен 0x2F, а 12-й байт равен 0xFF
1) Копирование байтов и проверка значений:
Вы ищете байты, которые представляют видео "тег". Помимо тега метаданных, теперь вы можете ожидать, что "тег" будет означать контейнер аудио или видеокадра. Есть два способа получить байты тега в ваш "временный байтовый массив" (назовем его как temp_BA
).
ReadBytes
(медленно): извлекает значения отдельных байтов в диапазоне начала / конца вsource_BA
WriteBytes
(быстро): мгновенное дублирование начального / конечного диапазона байтов изsource_BA
Readbytes объяснил: говорит Source читать его байты в Target. Источник будет читать вперед до длины от его текущего смещения (позиции). Перейти к правильной позиции источника, прежде чем читать дальше... source_BA.readBytes( into Target_BA, Pos within Target_BA, length of bytes required );
После выполнения вышеуказанной строки исходная позиция будет перемещена вперед для учета новой пройденной длины. (формула: источник новый Pos = предыдущий Pos + BytesLengthRequired).
Writebytes объяснил: говорит Target дублировать диапазон байтов из источника. Быстро с момента копирования из уже известной информации (из источника). Цель пишет дальше от своей текущей позиции... target_BA.writeBytes( from source_BA, Pos within source_BA, length of bytes required );
После выполнения вышеуказанной строки обратите внимание, что позиции источника и цели не изменились.
Используйте вышеуказанные методы, чтобы получить необходимые байты тега в temp_BA
от конкретного source_BA.position = x
,
Чтобы проверить любой байт (его значение), используйте методы ниже, чтобы обновить некоторую переменную int
тип:
- Прочитайте однобайтовое значение: используйте
my_Integer = source_BA.readByte();
- Прочитайте двухбайтовое значение: используйте
my_Integer = source_BA.readUnsignedShort();
- Прочитайте четырехбайтовое значение: используйте
my_Integer = source_BA.readUnsignedInt();
- переменная
Number
для восьмибайтового значения: используйтеmy_Number = source_BA.readDouble();
примечание: не путайте .readByte();
который извлекает числовое значение (байта) с аналогичным звучанием .readBytes()
который копирует кусок байтов в другой байтовый массив.
2) Поиск ключевого кадра видео (или I-кадра):
[Иллюстрация изображения видео TAG с ключевым кадром H264/AAC ]
Чтобы найти видео ключевой кадр
- Из начального смещения используйте
while
цикл, чтобы теперь перемещаться [вперед] через байты, ища каждый байт для однобайтового значения " 9 " (hex:0x09
), при обнаружении мы проверяем дальнейшие байты, чтобы подтвердить, что это действительно настоящий ключевой кадр, а не просто случайное появление "9". - В случае видеокодека H.264, в правильной позиции " 9 " байтов (xPos) мы ожидаем, что 11-й и 12-й байты вперед всегда будут " 17 " и " 01 " соответственно.
If
то есть== true
затем мы проверяем три байта размера тега и добавляем 15 к этому целому числу для общей длины байтов, которые, как ожидается, будут записаны из источника в цель (temp_BA
). Мы добавили 15 для учета 11 байтов до, а также 4 байта после ожидаемых данных TAG. Эти 4 байта в конце тега являются "Предыдущим размером тега", и эта сумма фактически включает в себя 11 передних байтов, но не считая сами эти 4 байта конца.- Мы говорим
temp_BA
написать байты Source (вашvideoByteArray
) начиная с позиции " 9 " байт (xPos) для длины "Размер тега" + 15. Теперь вы извлекли ключевой кадр MPEG.
пример:temp_BA.writeBytes( videoByteArray, int (xPos), int (TAG_size) );
- это
temp_BA
с тегом ключевого кадра теперь можно добавить с помощью:
пример:netStream.appendBytes( temp_BA ); //displays a single frame
примечание: для чтения 3 байтов размера тега я покажу пользовательское преобразование bytes_toInt()
функция (поскольку процессоры считывают 1, 2 или 4 байта одновременно для целых чисел, чтение 3 байтов здесь является ответом на запрос).
Совет по поиску: теги всегда следуют друг за другом в след. Мы можем искать быстрее, также проверяя, являются ли байты для видео-тега без ключевого кадра (P-кадра) или даже некоторого аудио-тега. Если так, то мы проверяем этот конкретный tag size
и теперь увеличиваем наш xPos
прыгать эту новую длину. Таким образом, мы можем пропустить все размеры тегов, а не только отдельные байты. Остановка только тогда, когда у нас есть тег ключевого кадра.
3) Воспроизведение с ключевого кадра видео:
Когда вы думаете об этом, игра просто похожа на автопоиск по кадрам. Где ожидаемая скорость получения каждого следующего кадра определяется кодированной частотой кадров видео.
Таким образом, ваша функция воспроизведения может быть просто Timer
который получает X-количество видео тегов (кадров) каждую секунду (или 1000 милисек). Вы делаете это в качестве примера my_Timer = new Timer ( video_FPS )
, Когда таймер запускается и достигает каждого среза FPS секунды, он запускает append_PLAY();
функция, которая в свою очередь выполняет get_frame_Tag();
функция.
NS.seek(0)
: Переводит NetStream в "режим поиска". (номер не имеет значения, но должен существовать в команде). Любой буфер "впереди фреймы" очищается, и они не будут обновляться (пока не будут)RESET_SEEK
: Завершает "режим поиска" и теперь разрешает обновления изображений. Первый тег, который вы добавляете после использованияRESET_SEEK
команда должна быть тегом с ключевым кадром видео. (только для аудио это может быть любой тег, поскольку технически все аудио теги являются ключевыми кадрами аудио)END_SEQUENCE
: (для MPEG H.264) Воспроизведение всех оставшихся "кадров впереди" (опустошает буфер). После слива вы можете добавлять видео тег любого типа. Помните, что в H.264 ожидаются метки времени, движущиеся вперед. Если вы видите f**ked up пикселей, тогда ваша следующая метка времени неверна (слишком высокая или слишком низкая). Если вы добавляете только один кадр (изображение плаката?), Вы можете использоватьEND_SEQUEMCE
опустошить буфер и отобразить этот кадр (не дожидаясь, пока буфер заполнит до x количество кадров)...
Функция play действует как функция посредника для управления вещами, не загромождая функцию get frame If
операторы и т. д. Управление вещами означает, например, проверку, что загружено достаточно байтов, чтобы даже начать получать кадр в соответствии с размером тега.
4) Исходный код для рабочего примера:
Код слишком длинный.. см. Эту ссылку ниже: https://gist.github.com/Valerio-Charles-VC1/657054b773dba9ba1cbc
Надеюсь, поможет. VC