actionscript-3 - VLC 语法转码和流式传输到标准输出?

标签 actionscript-3 apache-flex ffmpeg vlc libvlc

目标:我正在尝试使用 VLC作为本地服务器来扩展使用 Adobe AIR 创建的应用程序的视频功能, FlexActionscript .我正在使用 VLC流式传输到 stdout并从我的应用程序中读取该输出。

VLC Streaming capabilities
VLC Flash Video
Stream VLC to Website with asf and Flash

状态:我能够启动 VLC作为后台进程并通过其remote control interface 对其进行控制(more detail)。我可以加载、转码和流式传输本地视频文件。下面的示例应用程序是一个准系统测试平台,展示了这一点。

问题:我正在将数据输入我的应用程序,但它没有呈现为视频。我不知道这是否是我的 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 读取时,我没有看到此痕迹和 NetStreamData Generation模式。 有没有可能它没有被发送到 stdout由于某些原因?我已经尝试生成自己的 header 并在流开始之前附加它——我的 header 格式可能不正确。

enter image description here

更新 2:所以在我的AIR应用程序我能够粗略地解析传入的 stdout来自 VLC 的流.我想看看 FLV header 数据是否正在发送 - 看起来确实如此。我不知道它的格式是否正确等,但正如我上面提到的,如果我写入 .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>

最佳答案

在您的其他问题评论中,您询问了我的想法:

我在您的代码中注意到您在 OSX 环境下运行 VLC 进程。
在 Windows PC 上请注意 -I rc稍后不回复 standardInput发送的命令。我是 Windows 用户,所以无法帮助解决这部分问题。

尝试使用 --no-rc-fake-tty甚至--rc-fake-tty , VLC 仍然没有响应 stdout在电脑上。

您想在 VLC 中进行播放和搜索,但在 AS3 中观看结果(如投影屏幕),对吗?但我什至不确定 VLC 是否会从您选择的时间戳等开始返回 FLV 标签(通过寻找您正在访问特定时间戳的 FLV 标签和相关的 a/v 数据)...

其他 FFmpeg/Mencoder 驱动的播放器,如我测试过的 MPlayer,仅将“状态”文本数据发送回 stdout。播放期间(因此无法馈送到 NetStream 解码器进行显示)。

I was able to crudely parse the incoming stdout stream coming from VLC. I wanted to see if the FLV header data was being sent – and it appears that it is. I don't know if it is in the correct format, etc.



检查字节:(有效的 FLV header 以 46 4C 56 01 05 00 00 00 09 00 00 00 00 开头)

只需使用以下函数的“字节检查”结果的复制粘贴来更新您的问题。然后更容易告诉你它是否可玩,或者你可能需要一些替代品。

1)设置一些公共(public)(或私有(private))变量......
  • 制作 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代码 :
    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 解码器。除非您重置所有内容并重新开始(重新附加完整 FLV header 、完整帧标记等的字节),否则它不会重新启动。

    关于actionscript-3 - VLC 语法转码和流式传输到标准输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39859378/

    相关文章:

    ffmpeg - 如何更改 FFMPEG 中的音频帧速率?

    actionscript-3 - 将 MovieClip 拖动到一个圆圈

    actionscript-3 - 控制声音/音量

    apache-flex - Flex 4.5 移动应用程序中的多行标签?

    flash - 没有 Flex 的 ActionScript 3 可绑定(bind)元数据标签?

    c - 采样率转换函数中的段错误

    android - 有谁知道 AIR 是否支持 iOS 或 Android 上的本地多人游戏?

    xml - Flex 中多首歌曲的问题 - AS3

    css - 将 styleSheet 应用于 flex 中 ColumnChart 控件的数据提示

    javascript - 在 HTML 视频标签中流式传输实时视频流量