Hi everyone.
Just wanna share some experience I got by investigating the possibility to develop
local recorder using Adobe AIR + Red5 media server .
You will ask,
why Adobe AIR? Because I have been working with Flex and AIR almost 4 years and i find these technologies very attractive for all - clients and developers.
Why Red5? Because it is free, easy to install and use.
Initially the task was to migrate web-based enterprise application which uses FMS remotely to standalone AIR app with own media server locally. But before, it was required to develop some proof of concept (POC) to understand all pro et contra.
Here is folders' structure of POC:
As you can see, folder server contains Red5 media server. Fortunately, Red5 doesn't require to be installed etc. You can just copy the server's folder and to run the server by red5.bat. Folder server is included in src folder and because it is not an embedded asset, it will be moved in bin-debug folder and after app installation, Red5 will be in installation directory, exactly what we need!!!
In general, we should do next steps:
1. Start Red5 server on application initialization.
Starting from AIR2.6 (if i am not mistaken), AIR can run native processes. HERE is just perfect article what it is and how to use it.
In my code, Red5 starting up is implemented in next way (code a bit messy but for POC and for general understanding it is enough I guess):
I will not deep into details of each line of code, but major points are here.
Server should be started somehow from AIR.
In the beginning I tried to run .bat file with native process however it is impossible. Than i tried to use cmd.exe to start run.bat but this solution is also bad, because your code will be platform and even OS versions dependent. Finally, after investigation of run.bat content, I moved it in actionscript. See function invokeRed5 which emulates invocation of run.bat (to start the Red5 if boolean parameter is 'true') and red5-shutdown.bat (to shutdown the server if parameter is 'false'). NativeProcess dispatches all the logs from server by so called ProgressEvents. And it even separates Java errors and standard Java logs by different event names!
LogsProcessor.as processes the logs and dispatches appropriate events. It is done in a bit dirty way by checking the text matching however, right now, I don't have any better solution. If you find smarter way, please let me know.
Oh... and don't forget to set this configuration in your AIR *-app.xml:
1.1. Check Java path
Notice, that executable file of native process is javaw.exe for Windows and java for Mac. AIR doesn't know where is Java installed, that's why you should check it once with user and save it in some configuration file.
Here are my ConfigurationManager and ConfigPopup which do all this stuff:
ConfigurationManager.as
ConfigPopup.mxml
ConfigVO.as
2. Establish connection with Red5 from Flash.
Now, when server is up and running as a Java process, it is the time to establish NetConnection and to create NetStream.
URL connection is always rtmp://localhost:{configured port}/{name of app}. In my case, it is rtmp://localhost:1935/mytest/.
As you can see from snippet above, on
Recording flow is primitive.
On record button click, the stream starts is published:
On record stop, the flag is set that stop is requested + camera and mic are unattached:
Application is waiting until stream's buffer is empty. Whet it is, stream should be closed:
Now, the last event in my flow - stream is unpublished successfully and I can enjoy my video.
Notice, that Red5 writes the FLVs into directory 'streams' under your server application directory. I don't know how to configure this path but looks like this
investigation will be required in the nearest feature :)
3. Server's application 'mytest'
Regarding to directory 'mytest'... this is your, so called, web application. In few words, Red5 is based on Apache Tomcat web-server and 'mytest' can contain all your html assets, JSPs, compiled Java classes etc. In my case, 'mytest' contains configured by default web.xml (nothing special inside),
red5-web.properties
and red5-web.xml which is based on Spring
This class should be extended from class org.red5.server.adapter.ApplicationAdapter.
All the public methods of this class can be invoked from AIR through the connection.
E.g. here is an invocation of public method pingServer of Java class com.test.MyTest from Flash:
4. Never forget to shut down the server when application is closing.
...because on next application starting, you will get "port is busy" error.
To do this, the easiest way is to suppress the closing event and to put the logic which will stop the Red5-Java process and, only after that, will close the application.
Summary
That's probably it what I wanted to share with wide Flex/AIR auditory.
Of course, code is not optimized and can contain some memory leaks etc. but for POC it is not necessary.
The source is here .
In folder setup, you will find already compiled AIR installion file.
Separate thanks to Christophe Coenraets for his great article Tomcat Launcher: Sample Application using the AIR 2.0 Native Process API which helped me a lot to launch Red5 under AIR.
Cheers...
Just wanna share some experience I got by investigating the possibility to develop
local recorder using Adobe AIR + Red5 media server .
You will ask,
why Adobe AIR? Because I have been working with Flex and AIR almost 4 years and i find these technologies very attractive for all - clients and developers.
Why Red5? Because it is free, easy to install and use.
Initially the task was to migrate web-based enterprise application which uses FMS remotely to standalone AIR app with own media server locally. But before, it was required to develop some proof of concept (POC) to understand all pro et contra.
Here is folders' structure of POC:
As you can see, folder server contains Red5 media server. Fortunately, Red5 doesn't require to be installed etc. You can just copy the server's folder and to run the server by red5.bat. Folder server is included in src folder and because it is not an embedded asset, it will be moved in bin-debug folder and after app installation, Red5 will be in installation directory, exactly what we need!!!
In general, we should do next steps:
1. Start Red5 server on application initialization.
Starting from AIR2.6 (if i am not mistaken), AIR can run native processes. HERE is just perfect article what it is and how to use it.
In my code, Red5 starting up is implemented in next way (code a bit messy but for POC and for general understanding it is enough I guess):
package com.localrecordingred5
{
import flash.desktop.NativeProcess;
import flash.desktop.NativeProcessStartupInfo;
import flash.events.DataEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.NativeProcessExitEvent;
import flash.events.ProgressEvent;
import flash.events.TimerEvent;
import flash.filesystem.File;
import flash.utils.Timer;
import mx.controls.Alert;
import mx.core.FlexGlobals;
[Event(name="started", type="flash.events.Event")]
[Event(name="stopped", type="flash.events.Event")]
[Event(name="logging", type="flash.events.DataEvent")]
public class Red5Manager extends EventDispatcher
{
private static const RED5_FOLDER:String = "server";
private var logsProcessor:LogsProcessor = new LogsProcessor();
private var forceCloseProcessTimer:Timer = new Timer(10000);
private var startRed5Process:NativeProcess;
/**
* Constructor
*/
public function Red5Manager() : void
{
logsProcessor.addEventListener("started", dispatchEvent);
logsProcessor.addEventListener("shuttedDown", shuttedDown);
logsProcessor.addEventListener("addressInUse", addressInUse);
}
private function shuttedDown(e:Event) : void
{
if (startRed5Process) startRed5Process.exit();
}
private function addressInUse(e:Event) : void
{
Alert.show("IP-port is busy. Please stop the process on port 1935.", "Ooops", 4, null, okHandler);
function okHandler(e:Event) : void
{
stop();
}
}
public function start() : void
{
launchRed5();
}
public function stop() : void
{
if (startRed5Process)
{
forceCloseProcessTimer.addEventListener(TimerEvent.TIMER, forcedExit);
forceCloseProcessTimer.start();
LocalRecordingRed5(FlexGlobals.topLevelApplication).blockUI("Shutting down...");
shutdownRed5();
}
else
{
onProcessExit();
}
function forcedExit(e:TimerEvent) : void
{
startRed5Process.exit(true);
}
}
public function launchRed5() : void
{
invokeRed5(true);
}
public function shutdownRed5() : void
{
invokeRed5(false);
}
private function invokeRed5(launch:Boolean) : void
{
var startupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
startupInfo.executable = ConfigurationManager.getInst().getJavaFile();
startupInfo.workingDirectory = File.applicationDirectory.resolvePath(RED5_FOLDER);
var separator:String = ConfigurationManager.isWin ? ";" : ":";
var arg5:String = File.applicationDirectory.resolvePath('server/boot.jar').nativePath;
arg5 += separator;
arg5 += File.applicationDirectory.resolvePath('server/conf').nativePath;
arg5 += separator + "." + separator;
var processArguments:Vector.<String> = new Vector.<String>();
processArguments[0] = "-Dpython.home=lib";
processArguments[1] = "-Dlogback.ContextSelector=org.red5.logging.LoggingContextSelector";
processArguments[2] = "-Dcatalina.useNaming=true";
processArguments[3] = "-Djava.security.debug=failure";
processArguments[4] = "-cp";
processArguments[5] = arg5;
processArguments[6] = launch ? "org.red5.server.Bootstrap" : "org.red5.server.Shutdown";
if (!launch)
{
processArguments[7] = "9999";
processArguments[8] = "red5user";
processArguments[9] = "changeme";
}
startupInfo.arguments = processArguments;
startProcess(startupInfo);
}
private function startProcess(startupInfo:NativeProcessStartupInfo) : void
{
startRed5Process = new NativeProcess();
startRed5Process.addEventListener(NativeProcessExitEvent.EXIT, onProcessExit);
startRed5Process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, onError);
startRed5Process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutput);
startRed5Process.start(startupInfo);
}
private function onProcessExit(e:Event = null) : void
{
dispatchEvent(new Event("stopped"));
}
private function onError(event:ProgressEvent) : void
{
var process:NativeProcess = event.target as NativeProcess;
var v:String = process.standardError.readUTFBytes(process.standardError.bytesAvailable);
dispatchLogEvent(v);
}
private function onOutput(event:ProgressEvent) : void
{
var process:NativeProcess = event.target as NativeProcess;
var v:String = process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable);
dispatchLogEvent(v);
}
private function dispatchLogEvent(log:String) : void
{
logsProcessor.process(log);
dispatchEvent(new DataEvent("logging", false, false, log));
}
}
}
I will not deep into details of each line of code, but major points are here.
Server should be started somehow from AIR.
In the beginning I tried to run .bat file with native process however it is impossible. Than i tried to use cmd.exe to start run.bat but this solution is also bad, because your code will be platform and even OS versions dependent. Finally, after investigation of run.bat content, I moved it in actionscript. See function invokeRed5 which emulates invocation of run.bat (to start the Red5 if boolean parameter is 'true') and red5-shutdown.bat (to shutdown the server if parameter is 'false'). NativeProcess dispatches all the logs from server by so called ProgressEvents. And it even separates Java errors and standard Java logs by different event names!
ProgressEvent.STANDARD_ERROR_DATA
and
ProgressEvent.STANDARD_OUTPUT_DATA
. Nice, ha?!...
LogsProcessor.as processes the logs and dispatches appropriate events. It is done in a bit dirty way by checking the text matching however, right now, I don't have any better solution. If you find smarter way, please let me know.
package com.localrecordingred5
{
import flash.events.Event;
import flash.events.EventDispatcher;
[Event(name="started", type="flash.events.Event")]
[Event(name="shuttedDown", type="flash.events.Event")]
[Event(name="addressInUse", type="flash.events.Event")]
public class LogsProcessor extends EventDispatcher
{
private static const LAUNCHED:String = "Installer service created";
private static const SHUTTED_DOWN:String = "Stopping Coyote";
private static const ADDRESS_IN_USE:String = "Address already in use";
public function process(log:String) : void
{
if (log.indexOf(LAUNCHED) > -1)
{
dispatchEvent(new Event("started", true));
}
if (log.indexOf(SHUTTED_DOWN) > -1)
{
dispatchEvent(new Event("shuttedDown", true));
}
if (log.indexOf(ADDRESS_IN_USE) > -1)
{
dispatchEvent(new Event("addressInUse", true));
}
}
}
}
Oh... and don't forget to set this configuration in your AIR *-app.xml:
<supportedProfiles>extendedDesktop</supportedProfiles>
1.1. Check Java path
Notice, that executable file of native process is javaw.exe for Windows and java for Mac. AIR doesn't know where is Java installed, that's why you should check it once with user and save it in some configuration file.
Here are my ConfigurationManager and ConfigPopup which do all this stuff:
ConfigurationManager.as
package com.localrecordingred5
{
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.system.Capabilities;
import mx.core.FlexGlobals;
import mx.managers.PopUpManager;
[Event(name="configured", type="flash.events.DataEvent")]
public class ConfigurationManager extends EventDispatcher
{
private static const CONFIG_FILE:String = "config.xml";
private static var instance:ConfigurationManager;
private var configFile:File;
private var _config:ConfigVO;
private var configPopup:ConfigPopup;
/**
* Configuration
*
*/
public function ConfigurationManager(enforcer:SingletonEnforcer)
{
if (!enforcer) throw new Error("ConfigurationManager can't be explicitly instantiated");
configFile = File.applicationStorageDirectory.resolvePath(CONFIG_FILE);
}
public static function getInst() : ConfigurationManager
{
if (!instance)
instance = new ConfigurationManager(new SingletonEnforcer());
return instance;
}
//-----------------------------------------------------
//
// PUBLIC methods
//
//-----------------------------------------------------
public function get config() : ConfigVO
{
return _config;
}
/**
* Check configuration.
* If everything is configured, dispatch the event and continue app loading.
* Otherwise, show config popup.
*/
public function check() : void
{
var cfgXml:XML = readConfig();
populateConfig(cfgXml);
if (isJavaHomeValid())
{
dispatchEvent(new Event("configured"));
}
else
{
configPopup = PopUpManager.createPopUp(FlexGlobals.topLevelApplication as DisplayObject, ConfigPopup, true) as ConfigPopup;
configPopup.addEventListener("saveConfig", save);
PopUpManager.centerPopUp(configPopup);
}
}
public function getJavaFile() : File
{
// Get a file reference to the JVM
var file:File = new File(_config.javaHome);
if (isWin)
{
file = file.resolvePath("bin/javaw.exe");
}
else
{
file = file.resolvePath("Home/bin/java");
}
return file;
}
private function save(e:Event) : void
{
var xml:XML =
<config>
<javaHome>{config.javaHome}</javaHome>
</config>;
var fs:FileStream = new FileStream();
fs.open(configFile, FileMode.WRITE);
fs.writeUTFBytes(xml);
fs.close();
PopUpManager.removePopUp(configPopup);
dispatchEvent(new Event("configured"));
}
private function populateConfig(configXml:XML) : void
{
_config = new ConfigVO();
if (configXml)
{
_config.javaHome = new String(configXml..javaHome);
}
}
public function isJavaHomeValid() : Boolean
{
if (!_config) return false;
// If no known last home, present default/sample values
if (!_config.javaHome) setDefaultJavaHome();
return getJavaFile().exists;
function setDefaultJavaHome() : void
{
_config.javaHome = (isWin) ?
"C:\\Program Files\\Java\\jre6" :
"/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0";
}
}
private function readConfig():XML
{
if (configFile.exists)
{
var fs:FileStream = new FileStream();
fs.open(configFile, FileMode.READ);
var xml:XML = XML(fs.readUTFBytes(fs.bytesAvailable));
fs.close();
return xml;
}
else
{
return null;
}
}
public static function get isWin() : Boolean
{
return Capabilities.os.toLowerCase().indexOf("win") > -1;
}
}
}
class SingletonEnforcer{}
ConfigPopup.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
title="Configuration"
creationComplete="init()">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.utils.StringUtil;
private var cfgMngr:ConfigurationManager;
private function init():void
{
cfgMngr = ConfigurationManager.getInst();
javaTI.text = cfgMngr.config.javaHome;
}
private function onSave():void
{
cfgMngr.config.javaHome = StringUtil.trim(javaTI.text);
if (cfgMngr.isJavaHomeValid())
{
dispatchEvent(new Event("saveConfig"));
}
else
{
errLbl.text = "Java Home is not configured properly. \nPlease check again";
}
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0"/>
</s:layout>
<s:Form width="100%" >
<s:layout>
<s:FormLayout gap="0" />
</s:layout>
<s:FormItem width="100%">
<s:Label maxDisplayedLines="3" width="100%" color="#6c8dae"
text="Please configure the application once and enjoy your local video recording."/>
</s:FormItem>
<s:FormItem label="Java Home:" width="100%">
<s:TextInput id="javaTI" width="100%"/>
</s:FormItem>
<s:FormItem width="100%">
<s:layout>
<s:VerticalLayout paddingTop="0"/>
</s:layout>
<mx:HRule width="100%"/>
<s:HGroup width="100%" height="30">
<s:Label id="errLbl" color="red" width="100%"/>
<s:Button label="Save" click="onSave()"/>
</s:HGroup>
</s:FormItem>
</s:Form>
</s:Panel>
ConfigVO.as
package com.localrecordingred5
{
[Bindable]
public class ConfigVO
{
public var javaHome:String;
}
}
2. Establish connection with Red5 from Flash.
Now, when server is up and running as a Java process, it is the time to establish NetConnection and to create NetStream.
URL connection is always rtmp://localhost:{configured port}/{name of app}. In my case, it is rtmp://localhost:1935/mytest/.
public function connect():void
{
connection = new NetConnection();
connection.addEventListener(NetStatusEvent.NET_STATUS, netConnectionStatusHandler);
connection.connect(RTMP_SERVER);
}
private function netConnectionStatusHandler(event:NetStatusEvent) : void
{
var evCode:String = event.info.code;
switch (evCode)
{
case "NetConnection.Connect.Success":
currentState = STOP_STATE;
createStream();
dispatchEvent(new Event("connected"));
break;
...
}
}
private function get cam() : Camera
{
var camera:Camera = Camera.getCamera();
camera.setQuality(0, qualityNS.value);
var res:Object = resCB.selectedItem;
camera.setMode(res.width, res.height, frameNS.value);
return camera;
}
private function get mic() : Microphone
{
return Microphone.getMicrophone();
}
private function createStream() : void
{
var cam:Camera = this.cam;
vd.attachCamera(cam);
stream = new NetStream(connection);
stream.addEventListener(NetStatusEvent.NET_STATUS, netConnectionStatusHandler);
stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
stream.attachCamera(cam);
stream.attachAudio(mic);
stream.bufferTime = BUFFER_TIME;
createStreamName();
}
//create some random name for test
private function createStreamName() : void
{
streamName = "video" + Math.round(Math.random() * 100000);
dispatchLogEvent("Stream name generated: " + streamName + "\n");
}
As you can see from snippet above, on
NetConnection.Connect.Success
the stream is created.Recording flow is primitive.
On record button click, the stream starts is published:
stream.publish(streamName, "record");
On record stop, the flag is set that stop is requested + camera and mic are unattached:
private function onStop() : void
{
stream.attachCamera(null);
stream.attachAudio(null);
stopRequested = true;
LocalRecordingRed5(FlexGlobals.topLevelApplication).blockUI("Processing...");
}
Application is waiting until stream's buffer is empty. Whet it is, stream should be closed:
case "NetStream.Buffer.Empty":
if (stopRequested)
{
stop();
LocalRecordingRed5(FlexGlobals.topLevelApplication).unblockUI();
stopRequested = false;
recording = false;
}
break;
...
private function stop():void
{
...;
stream.close();
}
Now, the last event in my flow - stream is unpublished successfully and I can enjoy my video.
case "NetStream.Unpublish.Success":
var f:File = File.applicationDirectory.resolvePath("server/webapps/mytest/streams/" + streamName + ".flv");
f.addEventListener(Event.SELECT, saveByPath);
f.browseForSave(streamName + ".flv");
break;
Notice, that Red5 writes the FLVs into directory 'streams' under your server application directory. I don't know how to configure this path but looks like this
investigation will be required in the nearest feature :)
3. Server's application 'mytest'
Regarding to directory 'mytest'... this is your, so called, web application. In few words, Red5 is based on Apache Tomcat web-server and 'mytest' can contain all your html assets, JSPs, compiled Java classes etc. In my case, 'mytest' contains configured by default web.xml (nothing special inside),
red5-web.properties
webapp.contextPath=/mytest
webapp.virtualHosts=localhost, 127.0.0.1
and red5-web.xml which is based on Spring
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">
<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/red5-web.properties" />
</bean>
<bean id="web.context" class="org.red5.server.Context" autowire="byType" />
<bean id="web.scope" class="org.red5.server.WebScope"
init-method="register">
<property name="server" ref="red5.server" />
<property name="parent" ref="global.scope" />
<property name="context" ref="web.context" />
<property name="handler" ref="web.handler" />
<property name="contextPath" value="${webapp.contextPath}" />
<property name="virtualHosts" value="${webapp.virtualHosts}" />
</bean>
<bean id="web.handler" class="com.test.MyTest" />
</beans>
Marked in red is integration of Java class.This class should be extended from class org.red5.server.adapter.ApplicationAdapter.
All the public methods of this class can be invoked from AIR through the connection.
E.g. here is an invocation of public method pingServer of Java class com.test.MyTest from Flash:
protected function pingServer():void
{
connection.call("pingServer", new Responder(res, fault));
function res(e:Object) : void
{
dispatchLogEvent("Ping is successfull. Returned value:" + e.toString() + "\n");
}
function fault(e:Object) : void
{
dispatchLogEvent("Ping is failed.\n");
}
}
This is fantastic feature cause you can move some heavy calculations, data processing etc. from AIR to Java. Incredible!4. Never forget to shut down the server when application is closing.
...because on next application starting, you will get "port is busy" error.
To do this, the easiest way is to suppress the closing event and to put the logic which will stop the Red5-Java process and, only after that, will close the application.
closing="onClosing(event)"
...
...
protected function onClosing(event:Event):void
{
event.preventDefault();
red5Starter.stop();
}
...
...
private function red5Stopped(event:Event):void
{
NativeApplication.nativeApplication.exit();
}
...
...
<fx:Declarations>
<localrecordingred5:Red5Manager id="red5Starter"
logging="red5Logging(event)"
started="red5Started(event)"
stopped="red5Stopped(event)"/>
</fx:Declarations>
Summary
That's probably it what I wanted to share with wide Flex/AIR auditory.
Of course, code is not optimized and can contain some memory leaks etc. but for POC it is not necessary.
The source is here .
In folder setup, you will find already compiled AIR installion file.
Separate thanks to Christophe Coenraets for his great article Tomcat Launcher: Sample Application using the AIR 2.0 Native Process API which helped me a lot to launch Red5 under AIR.
Cheers...
Thanks for a great post!
ReplyDeleteDid you try it on a Mac?
Thanks for feedback.
DeleteYeah. I tried it on Mac and it worked well. However I didn't have a chance to try it on Linux. So if anybody try it on Linux, please comment here about the results.
Thanks for this tutorial!!... by the way, it is posible to run bat files from Air.
Deleteyou say "I tried to run .bat file with native process however it is impossible." well it is, from version flash CS 5.5, if you know a little spanish i invite you to read my tutorial from this issue in http://www.cristalab.com/tutoriales/ejecutar-una-bat-file-con-air-2.0-c96486l/. thanks again!!
Hi Buddy
ReplyDeleteThanks for this Tutorial
Welcome! :)
DeleteWhen I run from Flash Builder I get the below, some issue with using classpath for red5.properties? Please help. Thanks
ReplyDeleteorg.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: class path resource [red5.properties] cannot be opened because it does not exist
at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:78)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:553)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:527)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:362)
at org.red5.server.Launcher.launch(Launcher.java:60)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.red5.server.Bootstrap.bootStrap(Bootstrap.java:106)
at org.red5.server.Bootstrap.main(Bootstrap.java:50)
Caused by: java.io.FileNotFoundException: class path resource [red5.properties] cannot be opened because it does not exist
at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:143)
at org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:182)
at org.springframework.core.io.support.PropertiesLoaderSupport.mergeProperties(PropertiesLoaderSupport.java:161)
at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:69)
The problem is that Flash Builder (by some strange reason) doesn't copy *.properties files as assets from src to bin-debug|bin-release.
DeleteThese .properties files are placed in folder 'src\server'. Just re-copy manually folder 'server' from 'src' to 'bin-debug|bin-release' to be sure that all the skipped files by Flash Builder are copied into output folder.
Excellent! Thank you so much, works great now.
ReplyDeleteI notice that sometimes I get the "IP-port is busy. Please stop the process on port 1935." error.
I believe locating and killing the process listening to 1935 will do the trick, but I cant seem to do that.... restarting fixes though.
Did you encounter this issues and find a workaround?
No problem! Glad that it works for you and you can the benefit from this article.
DeleteRe. your question: looks like you don't close the application properly (usually it happens when you kill the app through Ctrl+Alt+Delete or somehow else). That's what point #4 ("Never forget to shut down the server when application is closing.") of this article is responsible for. But anyway, if you see this message, just remove the instance of javaw.exe in Task Manager (that's Red5 Java instance).
Thanks Vadym, Great work and results! I'll be back later. Grant or AdobeMaster.
ReplyDeleteHi
ReplyDeleteI am testing this script with Adobe Flex 4.6 and with latest SDK
but it gives me this error
IP-port is busy. Please stop the process on port 1935.
I closed all of javaw.exe processes but still this error
and on flash builder application didn't open and gives me this
[SWF] LocalRecordingRed5.swf - 4,049,403 bytes after decompression
[Unload SWF] LocalRecordingRed5.swf
I tested JRE version 6 x86 and x64 , verion 7 x64
and JDK version 6 x64
but no result
please can you help me to find a way to run your sample?
Thanks
Hi AGB.
DeleteDo you have FMS installed locally on your PC? It uses 1935 as well.
If you are under Windows, go to system console (Start/Run/cmd) and type 'netstat /ano'. It will show you all the process IDs (PID) and its used ports (e.g. http://i46.tinypic.com/2zqy5a0.png ).
Hope it helps.
great work!!
ReplyDeleteinstead of Red5 is it possible to do with FFmpeg? if so where would we need to change this?
also after using a few times its just keeps crashing on windows..
actually, if i understand correctly, ffmpeg is used more for convertion of videos and it can't record netstream. to record the video from flash, you should connect your flash app to some media server and ffmpeg is not a media server (correct me if i am wrong). but you can integrate ffmpeg in this application to do some post-record convertion, processing etc.
DeleteRe. your issue with crashing: can you provide some logs and screenshots?
thanks for your quick response!!
ReplyDeleteyes i want to use ffmpeg to convert to h.264 on the fly and stream to my ipad with a segmenter....
i have a great little batch file i use now that does this for me now.
it just crashed the 1st time i tried using it now after a pc restart, where do i find the log file to show you? i did get a screenshot and it seems to be a ffdshow.ax
how do i show you the screenshot? dont see a upload here
you can use any file sharing system to upload the screenshot, the same for logs, e.g. http://www.speedyshare.com/.
Deleteto get logs, just click on checkbox "Show Logs" and copy them OR by path "your-app/server/log/". just put logs and screenshot in some zip and upload it.
Hi Vadym,
ReplyDeleteThank you for this awsome stuff :-)
I try to make your installer.exe work before diving into the code... Everything works well, but no luck : it does not record any video, in the recorder logs it keeps saying
netConnectionStatusHandler::event.info.code = NetStream.Buffer.Empty
Would you have any idea why the recording does not work ?
Thanks for your feedback !
JB
Hi JB.
DeleteI noticed such things under Win7 if program is installed in the folder where are no write permissions for the account (e.g. Program Files). Try to re-install it to some another permissions free folder (e.g. D:\MyTestApp\...).
Or do you have some other issue? Also check folder \server\webapps\mytest\streams\. This is a place where FLVs are stored by Red5.
Thanks.
Hey, that's quite a fast reply :-) Thanks dude !!!
DeleteGood catch : indeed Win7 and its Program Files folder was blocking things !
-> It records now well... but without the sound. In fact it looks like there's an audio track but kinda corrupted or something like this : VLC tells that it "doesn't support 'undf' audio or video format"...
Otherwise it reads well (man can guess there's some audio) in the flv you let in the streams folder... My generated flvs are obviously not as good as your ones... and I have not much idea where this could come from... ==> maybe is that necessary to install some audio codecs ?
If you have any idea about it... it will be welcome :-)
JB
I may have written something wrong : it looks like there's no audio at all... I tried to open the generated flv files into Adobe Media Encoder as it usually gives quite handy info about video files... and it says there's no audio track in the flv.
DeleteHey JB!
DeleteI`ve just downloaded this POC from the links above (to be sure that we both use the same app installer) and re-installed it in my PC. Recorded video with audio and it works just perfectly. Audio and video are both synchronized and played. I guess you have some local issues with codecs or something like that.
Also you are the first one who has the issue with audio. From my side, I would recommend you to reinstall your codecs. Try K-Lite http://www.codecguide.com/download_kl.htm
Hope it helps.
Vadym.
You're right : it was a local issue... The K-Lite pack solved this recording issue !
DeleteIf this can help anyone else...
Thank you for sharing your nice work Vadym !
JB
do i need to make any special adjustments to run this on a Mac? i try to run/debug it and it gets to the initial "Starting..." screen and then it just closes.
ReplyDeleteSame problem here.
DeleteMy Java home is properly set but I'm stuck on "Starting...".
Nothing show up on logs.
Jack and Fernando,
Deletein your case, I would import the source to your FlashBuilder to debug the code and to see where your code has a stuck. I guess that NativeProcess is just not started but some reason (see method 'invokeRed5').
Sorry. FDT Bug didn't allowed me to launch as extendedDesktop :(
DeleteCould you please give me an example to do a post video conversion with ffmpeg? I would like to convert video from one format to different format - eg: .avi to .flv
ReplyDeleteThanks in advance :)
http://ffmpeg.org/ffmpeg.html
DeleteI'm unable to create the native exe for windows. When i used adt in command prompt it says "not valid internal or external command". So i moved everything to "C:\Program Files\Adobe\Adobe Flash Builder 4.6\sdks\4.6.0\bin\". Now i have "C:\Program Files\Adobe\Adobe Flash Builder 4.6\sdks\4.6.0\bin\MyCam" (Mycam - project folder). And i open command prompt and the navigated to "C:\Program Files\Adobe\Adobe Flash Builder 4.6\sdks\4.6.0\bin\"
ReplyDeletei run the following command but not nothing happens with exe and got the following errors.
C:\Program Files\Adobe\Adobe Flash Builder 4.6\sdks\4.6.0\bin>adt -package -stor
etype pkcs12 -keystore MyCam\mycam.p12 -target native MyCam\MyCam.exe MyCam\bin-
debug\MyCam-app.xml MyCam\bin-debug\MyCam.swf
password:
C:\Program Files\Adobe\Adobe Flash Builder 4.6\sdks\4.6.0\bin\MyCam\bin-debug\My
Cam-app.xml: error 302: Root content MyCam.swf is missing from package
C:\Program Files\Adobe\Adobe Flash Builder 4.6\sdks\4.6.0\bin>
I guess console compilation of AIR using adt is out of this post scope.
DeleteThis comment has been removed by a blog administrator.
ReplyDeleteI want to make a desktop sharing application using red5 server in java.could you please suggest me that what will b the structure of this project.and what r the environment which needed to deploy this application.m trying to make it since one moth but i did not get any clue about it.please suggest me.
ReplyDeleteI am not sure I can help you here. It is quite complicated topic. Try search something in web.
DeleteE.g. here is something what can answer on your question http://stackoverflow.com/questions/2059309/java-applet-screen-capture-to-a-video
How can i change the path that videos stored by red5. Other than this "server/webapps/mytest/streams/" to "user/videos"..?
ReplyDeleteRed5 is responsible for stream saving. You can done it easily configuring your spring application context config file and adding one java class.
DeleteMore is here http://www.red5tutorials.net/index.php/Tutorials:Streaming_from_custom_directories
this page no longer is there are there alternatives to look at ?
Deletealso is there a time limit to record? what if i wanted to record a 2 hour event
and if we wanted to connect to the stream what would address be?
This is great stuff!
ReplyDeleteand your attention to each commenter is super
thanx!
Saariko, thanks for feedback.
Deleteur welcome , and now I have a problem of my own
ReplyDeleteI am coding on a Mac, with Flash Builder 4.6 and the 4.6 sdk.
running or debugging my app works fine but when i export a release build (within a native installer) the (successfully) installed app exits after a few seconds of running (meaning that the red5 server has stopped and therefore the app itself quits).
I have read all the comments and therefore I have manually copied the "server" folder to the bin-debug folder.
I cannot really debug code since it works fine within the Flash Builder just not on the released build.
any ideas?
"installed app exits after a few seconds of running" - is it because 'properties' files are missed in 'server' folder after release build installation? In other words, do you have a problem only with release build version because of the missed properties?
Deleteyes Vadym thank you!
Deleteapparently even copying the "server" folder inside Flash Builder isn't enough when coding on a Mac for multiple platforms. I did copy the "server" folder you provided and manually inserted it to the application installer for each platform
all works fine now
thanks
this works great!! i found that the video was pretty bad quality however, or if i raised the quality, then i would get terrible audio/video sync... but adding those lines solved it;
ReplyDeleteRecorder.mxml:createStream(), at the end of the function add:
var h264Settings:H264VideoStreamSettings = new H264VideoStreamSettings();
h264Settings.setProfileLevel(H264Profile.BASELINE, H264Level.LEVEL_2);
stream.videoStreamSettings = h264Settings;
now i can record in 1280x720 90 of quality, 30fps and its pretty decent!!
thanks a lot for getting me started in the right direction!
what file did you do that in? i cant find it in the folders anywhere
DeleteHi, Vadym:
ReplyDeleteThank you so much for sharing your hard-won knowledge.
I was able to successfully install your AIR application, and it works great. I also tried to use your source files in Adobe Flash Builder. Unfortunately, I have always used Flash Professional and have never used Adobe Flash Builder before. I get the following errors:
When I try to "Export Release Build," I get the error 'Unknown Flex SDK: "Flex 4.5.1"'. Also, when I try to use the design view for the file "Recorder.mxml" I get an error message saying "An unknown item is declared as the root of your MXML document. Switch to source mode to correct it." These errors happen in both a version 4 of Flash Builder, which I own, and a trial version of version 4.6.
I would sincerely appreciate any ideas you may have. Thank you.
Don
Hi Don,
DeleteI never worked with Flash Professional, so I can't help you here. According to your errors, you don't have installed Flex SDK 4.5.1 in Flash Professional. Try to research how to setup it.
Thank you,
Vadym.
Thank you for your quick reply, Vadym. I'll follow up on your suggestion.
ReplyDeleteDid you develop your video recording application in Eclipse?
Don
Nope, developed in Flash Builder.
DeleteActually, Flash Builder is extension from Eclipse, so if you are familiar with the second, it will be easy for you to work in Flash Builder. You can download latest trial 30-days release version from https://www.adobe.com/cfusion/tdrc/index.cfm?product=flash_builder
Vadym.
Hi Vadyam,
ReplyDeleteI have a Video Conference App which uses Adobe AIR and Red 5 as streaming server.I used to cross compile the same code base for Android and IOS...Now for IOS, due to unavailability of AEC feature for mobile devices, the voice/audio in my mobile app seems to be very choppy and cluttered...and there is a heavy lag in voice(Video-voice sync is not there also). I tried both speex and Nellymoser codecs...Nellymoser gives a decent audio feed but still will never match the requirements of a video chat over a mobile..I am badly stuck with this....Cold you please assist me on the optimal microphone settings to avoid these issues...
-Veeru
Thanks for sharing this useful info. Keep updating same way.
ReplyDeleteRegards,Siddu online Training
Hello friend, the file is deleted at 4shared. It has a mirror?
ReplyDeleteYeah, looks like 4shared dropped it.
DeleteUploaded to my dropbox.
Try it now!
Thank you! I'm testing it works fine! Because the H264 does not add much difference in the size of the video?
DeleteHi Vadym,
ReplyDeleteThanks for the great info & tutorial. I sent you an email recently about this technique and was wondering if you could gut check my log for major errors/things I misconfigured. I stripped the essential parts from your code so I do not have to rely on Flex/MXML, but am thinking I am missing something obvious.
My server is properly embedded and initialized, but NetStream objects close immediately and are flaky. No file is output/recorded.
I'd greatly appreciate any advice/tips you could provide!
Thanks,
— Ryan
Hi Ryan,
DeleteAnswered.
Please check your email.
This comment has been removed by the author.
ReplyDeleteHi,
ReplyDeleteI have managed to set the project up in Flash Builder 4.7.
When launching or debugging the app in FB, the application starts but stops straight away. I don't have any error showing.
I have tried to insert breakpoint at different place to follow the application process but once again nothing wrong there.
Any suggestion or help would be really appreciated.
Many thanks,
J
I can just recommend to check java version to be 1.6 and make sure that application has write permissions. Also flash builder has an issue - it doesnt copy properties files to bin-debug, so probably you will have to copy it manually (see comments thread for the details).
DeleteThanks,
Vadym
Sorry for late response, maybe it is not actual. So what you are saying is not possible because this recorder is standalone and if you need to have it in web, you need remote media server, another flash client... long story...
ReplyDeleteI love this.
ReplyDeleteYou Rock!
-jcp
Thanks for the great tool. I wonder if one could get it working on Android - I assume you would have to make a Native Extension?
ReplyDeleteHi vadym Klos, great work :D , but the source files are no longer available in the dropbox :( could you please upload it again ? or send it to my email: dx1290@hotmail.com
ReplyDeletethanks in advance !!
Damn... accidentally removed. Try again. Should be available now. Thank you.
DeleteThank you very much Vadym :D
DeleteThanks for the awsome post. I have a question but don't know where to ask you, so i hope you will answer here:
ReplyDeleteI am working on a project which requires me to record Audio from Mic and Soundcard (any audio input from computer). I was successful in recording audio from Microphone and save it as mp3 file using mp3 encoder from as3-lame-air library. My question is: Is there any possibility that i can record audio from any input source instead of Microphone?
Thanks in advance.
what is the actual ip address of the rtmp stream is say we want to view it on my LAN via VLC to watch it as well or even re broadcast it?
ReplyDeleteNo longer works with latest version of Adobe AIR SDK (3.7) and Flash Builder 4.7
ReplyDeleteHey Vadym Klos, Thanks for the great article. I am able to run it with newer AIR SDKs.
ReplyDeleteCan you make it work with newer RED5 server?
Wonderful information your website gives the best and the most interesting information.................................Please contact us for Oracle Fusion HCM Online Training Institute
ReplyDeleteIt is amazing to visit your site. Thanks for sharing this information, this is useful to me..
ReplyDeleteWorkday Online Course
Workday Training Online
Las vegas recording studios We are really grateful for your blog post. You will find a lot of approaches after visiting your post. I was exactly searching for. Thanks for such post and please keep it up. Great work.
ReplyDelete