Подтвердить что ты не робот

Синтаксис VLC для перекодирования и потока в stdout?

Цель: Я пытаюсь использовать VLC в качестве локального сервера для расширения возможностей видео приложение, созданное с помощью Adobe AIR, Flex и Actionscript. Я использую VLC для потока до stdout и чтения этого вывода из моего приложения.

Возможности потоковой передачи VLC
VLC Flash Video
Поток VLC на сайт с asf и Flash

Статус: Я могу запустить VLC в качестве фонового процесса и управлять им через интерфейс удаленного управления (подробнее). Я могу загружать, транскодировать и транслировать локальный видеофайл. Пример приложения ниже - это тестовый стенд barebones, демонстрирующий это.

Проблема: Я получаю данные в своем приложении, но это не рендеринг как видео. Я не знаю, если это проблема с моими командами VLC или с записью/чтением из stdout. Эта методика чтения из stdout в AIR работает (например, с ffmpeg).

Одна из различных команд транскодирования, которую я пробовал:

-I rc  // remote control interface  
-vvv   // verbose debuging  
--sout  // transcode, stream to stdout 
"#transcode{vcodec=FLV1}:std{access=file,mux=ffmpeg{mux=flv},dst=-}"

Это приводит к тому, что данные поступают в мое приложение, но по какой-то причине оно не отображается как видео при использовании appendBytes с экземпляром NetStream.

Если вместо этого я напишу данные в .flv файл, будет создан действительный файл - так что сломанная часть, похоже, записывает его в stdout. Одна вещь, которую я заметил: я не получаю метаданные через метод stdout`. Если я воспроизведу файл, созданный с помощью приведенной ниже команды, я вижу метаданные.

// writing to a file
var output:File = File.desktopDirectory.resolvePath("stream.flv");
var outputPath:String = output.nativePath;
"#transcode{vcodec=FLV1}:std{access=file,mux=ffmpeg{mux=flv},dst=" + outputPath + "}");

Надеюсь, что кто-то увидит, где я ошибаюсь.


Обновление 1:. Чтобы добавить дополнительную информацию (!) - я взглянул на файл .flv, созданный для проверки метаданных. Он отображается во главе файла, как показано ниже. У меня есть правильный обработчик onMetaData и вижу след этих данных, если я воспроизвожу файл с диска. Я не вижу эту трассировку при чтении из stdout и NetStream в режиме Data Generation. Возможно ли, что по какой-либо причине она не отправляется на stdout? Я попытался создать собственный заголовок и добавить его до начала потока. Возможно, у меня неправильный формат заголовка.

введите описание изображения здесь


Обновление 2: Итак, в моем приложении AIR я смог грубо разобрать входящий поток stdout, исходящий из VLC. Я хотел посмотреть, были ли отправлены данные заголовка FLV - и, похоже, это так. Я не знаю, находится ли он в правильном формате и т.д., Но, как я упоминал выше, если я пишу в .flv файл вместо stdout, создается действительный .flv файл.

Полностью в убыток сейчас - попробовали все, что я мог придумать, и следил за каждой веб-ссылкой, которую я мог найти по затронутым вопросам. Увы - так близко, и было бы так здорово использовать VLC из AIR. 🙁


Обновление 3: На предложение VC ONE's я использовал свой код для проверки входящих байтов для правильных данных. Я получаю массивную строку (1000 символов), но это первые:

    What I get:
    464C560105000000090000000012000111000000000000000200
    46 4C 56 01 05 00 00 00 09 00 00 00 00 // check outs  

    What it should be:
    46 4C 56 01 05 00 00 00 09 00 00 00 00

Примечание.. Чтобы заставить это работать в AIR, вам необходимо определить профиль приложения как "extendedDesktop"


  <?xml version="1.0" encoding="utf-8"?>
  <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
                       xmlns:s="library://ns.adobe.com/flex/spark" 
                       xmlns:mx="library://ns.adobe.com/flex/mx"
                       width="1024" height="768"
                       showStatusBar="false"
                       applicationComplete="onApplicationCompleteHandler(event)">

    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;

            public var dataIn:Number = 0;
            public var dataTotal:Number = 0;
            private var processExe:File;
            private var processArgs:Vector.<String>;
            private var process:NativeProcess;
            private var nc:NetConnection;
            private var ns:NetStream;
            private var vid:Video;

            private var videoPath:String; // video to be streamed

            protected function onApplicationCompleteHandler(event:FlexEvent):void {
                var testFile:File = File.desktopDirectory.resolvePath("test.mp4");
                if (testFile.exists){
                    videoPath = testFile.nativePath;
                }

                setUpNetStream();
                createNativeProcess();
                startNativeProcess();
            }

            protected function setUpNetStream():void {
                nc = new NetConnection();
                nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, errorHandler);
                nc.addEventListener(NetStatusEvent.NET_STATUS, connStatusHandler);
                nc.connect(null);

                ns = new NetStream(nc);
                ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, errorHandler);
                ns.addEventListener(NetStatusEvent.NET_STATUS, streamStatusHandler);

                var client:Object = new Object();
                client.onMetaData = onMetaDataHandler;
                ns.client = client;

                vid = new Video(640,480);
                vid.x= 100;
                vid.y = 200;

                this.stage.addChild(vid);

                vid.attachNetStream(ns);
                ns.play(null);
            }

            private function createNativeProcess():void {
                if(NativeProcess.isSupported) {
                    // This is for OSX; 
                    var pathToVLC:String = "utils/OSX/VLC.app/Contents/MacOS/VLC";
                    processExe = File.applicationDirectory.resolvePath(pathToVLC);

                    if (processExe.exists){
                        process = new NativeProcess();
                        process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData); 
                        process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, onErrorData); 
                        process.addEventListener(ProgressEvent.PROGRESS, onOutputData); 
                        process.addEventListener(ProgressEvent.SOCKET_DATA, onOutputData); 
                        process.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
                        process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
                    } else {
                        trace("process not found");
                    }
                } else {
                    trace("Native Process not supported");
                }
            }

            private function startNativeProcess():void {
                processArgs = new Vector.<String>();
                processArgs.push("-I rc");
                processArgs.push("-vvv"); // verbose debug output
                processArgs.push("--sout");

                // -------TO WRITE TO A FILE ----------
                // file to playback from
                //var output:File = File.desktopDirectory.resolvePath("stream.flv");
                //var outputPath:String = output.nativePath;
                //processArgs.push("#transcode{vcodec=FLV1}:std{access=file,mux=ffmpeg{mux=flv},dst=" + outputPath + "}");

                processArgs.push("#transcode{vcodec=FLV1,acodec=mp3}:gather:std{access=file,mux=flv,dst=-}");
                processArgs.push("--sout-keep");

                // ------VARIATIONS-------
                //processArgs.push("#transcode{vcodec=FLV1,acodec=mp3}:std{access=file,mux=flv,dst=-}");
                //processArgs.push("#transcode{vcodec=h264,vb=512,acodec=mp3,ab=128,samplerate=44100}:std{mux=ffmpeg{mux=flv},access=file,dst=-}");

                var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
                nativeProcessStartupInfo.executable = processExe;
                nativeProcessStartupInfo.arguments = processArgs;
                process.start(nativeProcessStartupInfo);

                // add video to playlist and play
                process.standardInput.writeUTFBytes("add " + videoPath + " \n" );
                process.standardInput.writeUTFBytes("play" + "\n" );
            }

            public function onOutputData(event:ProgressEvent):void { 
                if (process && process.running){
                    if (process.standardOutput.bytesAvailable){
                        var videoStream:ByteArray = new ByteArray();
                        process.standardOutput.readBytes(videoStream,0, process.standardOutput.bytesAvailable);

                        dataIn = videoStream.length;
                        dataTotal+= dataIn;
                        report.text = String("Current Bytes: " + dataIn + "\t Total Bytes: "+ dataTotal);

                        if (videoStream.length){
                            ns.appendBytes(videoStream);
                        }
                        //trace(ns.info);
                    }   
                }
            }

            private function errorHandler(e:AsyncErrorEvent):void {
                trace('ERROR: ' + e.text);
            }

            private function connStatusHandler(e:NetStatusEvent):void {
                trace('CONN_STATUS: ' + e.info.code);

                switch(e.info.code){
                    case "NetConnection.Connect.Success":
                        //onFinishSetup();
                        break;
                }
            }

            private function streamStatusHandler(e:NetStatusEvent):void {
                trace('STREAM_STATUS: ' + e.info.code);
            }

            private function streamMetadataHandler(info:Object):void {
                for (var key:String in info) {
                    trace("STREAM_METADATA: " + key + "=" + info[key]);
                }               
            }

            public function onErrorData(event:ProgressEvent):void {
                if (process && process.running){
                    trace(process.standardError.readUTFBytes(process.standardError.bytesAvailable));
                }
            }
            public function onIOError(event:IOErrorEvent):void {
                trace(event.toString());
            }

            private function onMetaDataHandler(metadata:Object):void {
                trace("### Begin Metadata listing : FLV Entries ### "  );
                for (var entry:* in metadata) 
                {
                    var value:Object = metadata[ entry ];
                    trace(" > " + entry + " : " + value);
                }
                trace("### End of Metadata listing for this FLV ### "  );
            }
        ]]>
    </fx:Script>

    <s:Label id="report" x="25" y="25" fontSize="18" />
</s:WindowedApplication>
4b9b3361

Ответ 1

В других ваших комментариях к Вопросу вы просили мои мысли:

Я заметил в вашем коде, что вы запускаете процесс VLC в среде OSX.
На ПК с Windows помните, что -I rc не отвечает на отправленные команды standardInput. Я пользователь Windows, поэтому не могу помочь с этой частью.

Пробовал использовать --no-rc-fake-tty или даже --rc-fake-tty, VLC по-прежнему не отвечал на stdout на ПК.

Вы хотите выполнять воспроизведение и поиск в VLC, но смотреть результат в AS3 (например, проекционный экран), правильно? но я даже не уверен, что VLC вернет вам FLV-теги, начиная с ваших выбранных штампов времени и т.д. (путем поиска вашего доступа к тегу FLV определенной метки времени и связанных данных a/v)...

Другие игроки, работающие с FFmpeg/Mencoder, такие как MPlayer I, тестировали только отправку текстовых данных "статус" в stdout во время воспроизведения (поэтому не могут быть переданы на декодер NetStream для отображения).

Я смог грубо разобрать входящий поток stdout, исходящий из VLC. Я хотел посмотреть, были ли отправлены данные заголовка FLV - и это кажется, что это так. Я не знаю, находится ли он в правильном формате и т.д.

Проверьте байты: (действительный заголовок FLV начинается с 46 4C 56 01 05 00 00 00 09 00 00 00 00)

Просто обновите свой вопрос с помощью копий "результат проверки байтов" из приведенной ниже функции. Тогда вам легче сказать, может ли он быть воспроизведен, или, может быть, вам понадобится альтернатива.

1) Настройте некоторые общедоступные (или частные) vars...

  • Сделайте public var temp_String : String = "";
  • Сделайте public var videoStream:ByteArray = new ByteArray();

2) Замените свою функцию onOutputData на код ниже...

public function onOutputData(event:ProgressEvent):void 
{ 
    if (process && process.running)
    {
        if (process.standardOutput.bytesAvailable)
        {
            //# make a private/public bytearray outside of this function 
            //var videoStream:ByteArray = new ByteArray();

            process.standardOutput.readBytes(videoStream, videoStream.length, process.standardOutput.bytesAvailable);

            dataIn = process.standardOutput.bytesAvailable; 
            dataTotal += dataIn;
            //report.text = String("Current Bytes: " + dataIn + "\t Total Bytes: "+ dataTotal);

            if (videoStream.length >= 1000 )
            {
                //ns.appendBytes(videoStream);

                temp_String = bytes_toString(videoStream);
                trace("bytes checking : " + "\n");
                trace( temp_String ); //see hex of FLV bytes

                //# temporary pausing of progress events 
                process.removeEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
            }
            //trace(ns.info);
        }
    }
}

Вспомогательная функция bytes_toString code:

public function bytes_toString ( ba:ByteArray ) : String
{
    var str_Hex:String = "";    var len:uint = ba.length;

    ba.position = 0;

    for (var i:uint = 0; i < len; i++) 
    {
        var n:String=ba.readUnsignedByte().toString(16); 

        if(n.length<2) //padding
        { n="0"+n; }    str_Hex += n ;
    }

    return str_Hex.toUpperCase();
}

Некоторые другие примечания:

Каждый запуск событий прогресса фиксирует только 32kb/64kb пакетов входящих stdout байтов за раз.

Вы делаете свой videoStream:ByteArray = new ByteArray(); вне progressEvent, чтобы каждый запуск события не создавал новый byteArray (который отбрасывает старые данные, которые могут понадобиться позже для полного FLV-тега).

Не записывайте каждый пакет в позицию 0, так как это будет перезаписывать существующие данные. Добавьте к концу существующий, используя videoStream.length в качестве новой позиции записи.

process.standardOutput.readBytes(videoStream, videoStream.length, process.standardOutput.bytesAvailable);

Также if (videoStream.length){ ns.appendBytes(videoStream); } является опасным. Любые неполные данные (заголовка, фрейма или любого другого) будут затормозить декодер NetStream, если вы добавите слишком скоро. Он не перезапустится, если вы не reset все и не начнете снова (повторно добавьте байты полного заголовка FLV, полный тег кадра и т.д.).

Ответ 2

Несколько вещей, которые я хотел бы попробовать, некоторые из которых вы могли бы подумать уже:

  • Попробуйте отключить отладку в случае, если ваш поток stdout загрязняет ваш поток.
  • Попробуйте использовать другой формат видео (а не FLV), если это проблема с форматом. Например, вы можете попробовать mpeg4
  • Попробуйте подключить поток stdout к чему-то другому, например ffplay, чтобы узнать, является ли проблема потоком или предположениями вашего получателя о потоке.