Red5 Media Streaming Custom Files Location

  • Post published:August 31, 2015

Red5 is a media streaming server and it has been used as a core streaming engine for most popular video conferencing solutions like Apache OpenMeetings, BigBlueButton and many others. Sometimes you have to use the core engine just to stream files without adding extra layers on top of it. The way you can do so is as follows:

  • Install Red5 Server
  • Create New Application for Red5
  • Install the Red5 application
  • Restart the Red5 Server
    .

Red5 Server Installation:

Installing Red5 on Debian based machines is quite simple. You need to run the following command:

sudo apt-get install red5-server

For Ubuntu 14.04, there is a known bug due to which standard installation does not work. You can simply download the release from this site and can start server from sudo red5.sh.

Create A New Application For Red5:

For creating a new application for Red5, you need to install Eclipse for Java EE and create a standard dynamic web application. Setup the file structure as given below:

src\
META-INF\
WEB-INF\
  classes\
  lib\
  web.mxl
  red5-web.xml
  red5-web.properties

Now follow the steps given below:

  1. Set WEB-INF\classes\ as default build target
  2. Right click on src and choose Build Path -> Configure Output Folder. Paste WEB-INF/classes in the “Specify output folder” field
  3. Right click on Project and choose Properties from the Menu. Click on Java Build Path and choose Librarise tab
  4. Click on Add External JARs. You should add red5.jar from your red5 directory (/usr/share/red5/red5.jar). You may add all the files from lib directory (/usr/share/red5/lib) as External JARs to let your new application see all the available Red5 classes methods
    .

Now start building your application in src directory. Below is the default application class:

package org.nb.red5;

import java.util.Map;

import org.red5.server.adapter.ApplicationAdapter;<br>import org.red5.server.api.IConnection;<br>import org.red5.server.api.scope.IScope;<br>import org.red5.server.api.stream.IServerStream;

public class Application extends ApplicationAdapter {

   private IScope appScope;

   private IServerStream serverStream;

   /** {@inheritDoc} */
   @Override
   public boolean appStart(IScope app) {
       super.appStart(app);
       log.info("oflaDemo appStart");
       System.out.println("NextStreamer appStart");
       appScope = app;
       return true;
   }

   /** {@inheritDoc} */
   @Override
   public boolean appConnect(IConnection conn, Object[] params) {
       log.info("NextStreamer appConnect");
       IScope appScope = conn.getScope();
       log.debug("App connect called for scope: {}", appScope.getName());
       // getting client parameters
       Map<String, Object> properties = conn.getConnectParams();
       if (log.isDebugEnabled()) {
           for (Map.Entry<String, Object> e : properties.entrySet()) {
               log.debug("Connection property: {} = {}", e.getKey(), e.getValue());
           }
       }

       return super.appConnect(conn, params);
   }

   /** {@inheritDoc} */
   @Override
   public void appDisconnect(IConnection conn) {
       log.info("NextStreamer appDisconnect");
       if (appScope == conn.getScope() && serverStream != null) {
           serverStream.close();
       }
       super.appDisconnect(conn);
   }
}

The file which actually makes stream from custom path is as follows:

package org.nb.red5;

import org.red5.server.api.scope.IScope;
import org.red5.server.api.stream.IStreamFilenameGenerator;

public class CustomFilenameGenerator implements IStreamFilenameGenerator {

    /** Path that will store recorded videos. */
    public String recordPath = "recordedStreams/";
    /** Path that contains VOD streams. */
    public String playbackPath = "videoStreams/";
    /** Set if the path is absolute or relative */
    public boolean resolvesAbsolutePath = false;

    @Override
    public String generateFilename(IScope scope, String name, GenerationType type)
    {
    return generateFilename(scope, name, null, type);
    }

    @Override
    public String generateFilename(IScope scope, String name, String extension, GenerationType type)
    {
        String filename;

        if (type == GenerationType.RECORD)
         filename = recordPath + name;
        else
        filename = playbackPath + name;

        if (extension != null)
         filename += extension;

        return filename;
    }

    public boolean resolvesToAbsolutePath()
    {
        return resolvesAbsolutePath;
    }

    public void setRecordPath(String path)
    {
        recordPath = path;
    }

    public void setPlaybackPath(String path)
    {
        playbackPath = path;
    }

    public void setAbsolutePath(Boolean absolute)
    {
        resolvesAbsolutePath = absolute;
    }
}

To get these things workable, the configuration for web.xml is as follows:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app
   xmlns="http://java.sun.com/xml/ns/j2ee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
   version="2.4">

   <display-name>streamingDemo</display-name>

   <context-param>
       <param-name>webAppRootKey</param-name>
       <param-value>/streamingDemo</param-value>
   </context-param>

   <listener>
       <listener-class>org.red5.logging.ContextLoggingListener</listener-class>
   </listener>

   <filter>
       <filter-name>LoggerContextFilter</filter-name>
       <filter-class>org.red5.logging.LoggerContextFilter</filter-class>
   </filter>

   <filter-mapping>
       <filter-name>LoggerContextFilter</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>

   <servlet>
       <servlet-name>rtmpt</servlet-name>
       <servlet-class>org.red5.server.net.rtmpt.RTMPTServlet</servlet-class>
       <load-on-startup>1</load-on-startup>
   </servlet>

   <servlet-mapping>
       <servlet-name>rtmpt</servlet-name>
       <url-pattern>/fcs/*</url-pattern>
   </servlet-mapping>

   <servlet-mapping>
       <servlet-name>rtmpt</servlet-name>
       <url-pattern>/open/*</url-pattern>
   </servlet-mapping>

   <servlet-mapping>
       <servlet-name>rtmpt</servlet-name>
       <url-pattern>/close/*</url-pattern>
   </servlet-mapping>

   <servlet-mapping>
       <servlet-name>rtmpt</servlet-name>
       <url-pattern>/send/*</url-pattern>
   </servlet-mapping>

   <servlet-mapping>
       <servlet-name>rtmpt</servlet-name>
       <url-pattern>/idle/*</url-pattern>
   </servlet-mapping>

   <security-constraint>
       <web-resource-collection>
          <web-resource-name>Forbidden</web-resource-name>
          <url-pattern>/streams/*</url-pattern>
       </web-resource-collection>
       <auth-constraint/>
   </security-constraint>

</web-app>

The configuration for Red5 is as follows:

<?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-3.0.xsd
    http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.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.scope.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="org.nb.red5.Application" />

    <bean id="demoService.service" class="org.nb.red5.Service" />

    <bean id="streamFilenameGenerator" class="org.nb.red5.CustomFilenameGenerator">
       <property name="recordPath" value="/home/deploy/backup/streams/" />
       <property name="playbackPath" value="/home/deploy/backup/streams/" />
       <property name="absolutePath" value="true" />
    </bean>


</beans>

At the end, build the project from Eclipse, upload full directory to /usr/share/red5/webapps/. After uploading the directory, start the server by using sudo service red5 restart command. All the files present in /home/deploy/backup/streams/ will now be ready for stream.