Цель: Я пытаюсь использовать VLC в качестве локального сервера, чтобы расширить возможности видео из приложения, созданного с Adobe AIR
, Flex
и Actionscript
. Я использую VLC
для потока до stdout
и чтения этого вывода из моего приложения.Синтаксис VLC для перекодирования и потока в stdout?
VLC Streaming capabilities
VLC Flash Video
Stream VLC to Website with asf and Flash
Статус: Я могу запустить VLC
как фоновый процесс и управлять им через его remote control interface (more detail). Я могу загружать, транскодировать и транслировать локальный видеофайл. Пример приложения ниже - это тестовый стенд 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`method. Если я воспроизведу файл, созданный с помощью приведенной ниже команды, я вижу метаданные.
// 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 + "}");
Надеясь кто-то видит, где я неправильно здесь.
Update 1: Просто добавить еще несколько деталей - Я посмотрел на файл .flv, который создается для изучения метаданных (!). Он отображается во главе файла, как показано ниже. У меня есть правильный обработчик onMetaData
и посмотреть трассировку этих данных, если я воспроизвожу файл с диска. Я не вижу этот след при чтении от stdout
и NetStream
находится в Data Generation
режиме. Возможно ли, что по какой-то причине он не отправляется в stdout
? Я попытался создать собственный заголовок и добавить его до начала потока - возможно, формат заголовка не правильный.
Update 2: Так что в моем AIR
приложение, которое я смог разобрать грубо входящий stdout
поток, приходящий из VLC
. Я хотел посмотреть, были ли отправлены данные заголовка FLV - и, похоже, это так. Я не знаю, соответствует ли он в правильном формате и т. Д., Но, как я упоминал выше, если я пишу в.flv вместо stdout
, создается действительный файл .flv.
Полностью в затруднительном положении сейчас - попробовал все, что я мог придумать, и отслеживал каждую ссылку в Интернете, которую я мог найти по вопросам, связанным с этим. Увы - так близко, и было бы так здорово использовать VLC
с точностью до AIR
.
Update 3: Per 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>
Итак, я не пробовал ваш реальный сценарий ... но у меня была мысль, что я буду бежать от вас. Когда у меня были проблемы с файлом, не читающим поток, он отлично воспроизводится при загрузке в виде целого файла ... проблема всегда была в том, что данные видео MOOV ATOM были в конце кодированного видео, а не в начале. И поскольку вы упомянули «Я не вижу метаданных», я подумал, что я расскажу об этом и посмотрю, может ли это быть применимо. Если это выглядит так, как это может быть применимо к вам ... проверьте qtfaststart https://github.com/danielgtaylor/qtfaststart, чтобы преобразовать их с данными спереди. –
его не VLC. У меня была эта проблема с различными потоковыми источниками. Это исходный файл mp4. По умолчанию многие кодировщики помещают данные в конец оболочки mp4 ... и если вы попытаетесь передать это ... у получателя нет метаданных для работы. Если вы используете этот инструмент, я дал вам ссылку на ваш mp4, он переместит данные с конца mp4 в начало ... и начнется счастливая потоковая передача! (если это тоже ваша проблема) –
Ну, это была мысль. Я сделал это довольно много с ffmpeg и помимо проблемы с метаданными, все мои проблемы возникли из-за правильного соответствия/определения входных и выходных данных кодека. (что я вижу, вы уже играете). Если у вас этого не будет сделано завтра ... У меня будет некоторое время после работы, чтобы настроить VLC на моей машине и играть с потоком и посмотреть, смогу ли я помочь вам справиться с этим! –