HLS plugin gives null pointer

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

HLS plugin gives null pointer

Gunaratnam Kuhajeyan
Hi Guys,

HLS plugin is giving me NPE at following location,seems it is unable to obtain the spring application context. what I might be doing wrong

RED5 1.0.7-Release



public class PlayList extends HttpServlet {



....




protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
......


if (service == null) {
 
ApplicationContext appCtx = (ApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
 
service = (SegmenterService) appCtx.getBean("segmenter.service");// <===== NPE HERE
 
}


}


Web.xml

<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>hlsapp</display-name>
 
 
<context-param>
 
<param-name>webAppRootKey</param-name>
 
<param-value>/hlsapp</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>
       
<description>Serves an HTTP streaming playlist</description>
       
<display-name>PlayList</display-name>
       
<servlet-name>PlayList</servlet-name>
       
<servlet-class>org.red5.stream.http.servlet.PlayList</servlet-class>
       
<init-param>
           
<param-name>minimumSegmentCount</param-name>
           
<param-value>3</param-value>
       
</init-param>
   
</servlet>


   
<servlet>
       
<description>Serves a segment</description>
       
<display-name>TransportSegment</display-name>
       
<servlet-name>TransportSegment</servlet-name>
       
<servlet-class>org.red5.stream.http.servlet.TransportSegment</servlet-class>
   
</servlet>    
   
   
<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>PlayList</servlet-name>
       
<url-pattern>*.m3u8</url-pattern>
   
</servlet-mapping>
   
   
<servlet-mapping>
       
<servlet-name>TransportSegment</servlet-name>
       
<url-pattern>*.ts</url-pattern>
   
</servlet-mapping>
 
 
<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>
       
   
<welcome-file-list>
       
<welcome-file>index.html</welcome-file>
       
<welcome-file>index.htm</welcome-file>
       
<welcome-file>index.jsp</welcome-file>
   
</welcome-file-list>
   
   
<mime-mapping>
       
<extension>m3u8</extension>
       
<mime-type>application/x-mpegURL</mime-type>
   
</mime-mapping>
   
   
<mime-mapping>
       
<extension>ts</extension>
       
<mime-type>video/MP2T</mime-type>
   
</mime-mapping>
   
</web-app>


red5-web.xml

<?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="schedulingService" class="org.red5.server.scheduling.ApplicationSchedulingService" />


 
<bean id="web.handler" class="org.red5.hlsapp.Application" />
 
   
<bean id="mux.service" class="org.red5.service.httpstream.MuxService" />
   
   
<bean id="segmenter.service" class="org.red5.service.httpstream.SegmenterService">
       
<!-- Mux service -->
       
<property name="muxService" ref="mux.service" />
       
<!-- Thread executor -->
       
<property name="segmentExecutor" ref="segmentExecutor" />
       
<!-- Length of a segment in milliseconds -->
       
<property name="segmentTimeLimit" value="2000" />
       
<!-- Store segments (.ts) in memory -->
       
<property name="memoryMapped" value="false" />
       
<!-- Directory where segments and playlist files are stored -->
       
<property name="segmentDirectory" value="webapps/hlsapp/WEB-INF/segments/" />
       
<!-- Maximum segments to keep in a segment facade -->
       
<property name="maxSegmentsPerFacade" value="8" />          
       
<property name="outputAudioCodec" value="libvo_aacenc" />        
       
<!--
        <property name="outputAudioCodec" value="libmp3lame" />        
         -->

       
<property name="outputVideoCodec" value="libx264" />    
   
</bean>
   
   
<bean id="segmentExecutor" scope="prototype" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
       
<property name="poolSize" value="4" />  
       
<property name="waitForTasksToCompleteOnShutdown" value="false"/>
       
<property name="daemon" value="true"/>
       
<property name="threadNamePrefix" value="SegmentExecutor-"/>
   
</bean>


</beans>



red5.properties

webapp.contextPath=/hlsapp
webapp
.virtualHosts=*, localhost, localhost:8088, 127.0.0.1:8088


Playlist.java - HLSPlugin

public class PlayList extends HttpServlet {


 
private static final long serialVersionUID = 978137413L;


 
private static Logger log = Red5LoggerFactory.getLogger(PlayList.class);


 
private static SegmenterService service;


 
// number of segments that must exist before displaying any in the playlist
 
private int minimumSegmentCount = 2;


 
@Override
 
public void init(ServletConfig config) throws ServletException {
 
super.init(config);
 
String minimumSegmentCountParam = getInitParameter("minimumSegmentCount");
 
if (!StringUtils.isEmpty(minimumSegmentCountParam)) {
 minimumSegmentCount
= Integer.valueOf(minimumSegmentCountParam);
 
}
 log
.debug("Minimum segment count - param: {} value: {}", minimumSegmentCountParam, minimumSegmentCount);
 
}


 
/**
 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
 */

 
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 doPost
(request, response);
 
}


 
/**
 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
 */

 
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 log
.debug("Playlist requested");
 
/*
 * EXT-X-MEDIA-SEQUENCE
 * Each media file URI in a Playlist has a unique sequence number.  The sequence number
 * of a URI is equal to the sequence number of the URI that preceded it plus one. The
 * EXT-X-MEDIA-SEQUENCE tag indicates the sequence number of the first URI that appears
 * in a Playlist file.
 *
 #EXTM3U
 #EXT-X-ALLOW-CACHE:NO
 #EXT-X-MEDIA-SEQUENCE:0
 #EXT-X-TARGETDURATION:10
 #EXTINF:10,
 http://media.example.com/segment1.ts
 #EXTINF:10,
 http://media.example.com/segment2.ts
 #EXTINF:10,
 http://media.example.com/segment3.ts
 #EXT-X-ENDLIST
 
 Using one large file, testing with ipod touch, this worked (149 == 2:29)
 #EXTM3U
 #EXT-X-TARGETDURATION:149
 #EXT-X-MEDIA-SEQUENCE:0
 #EXTINF:149, no desc
 out0.ts
 #EXT-X-ENDLIST
 
 Using these encoding parameters:
 ffmpeg -i test.mp4 -re -an -vcodec libx264 -b 96k -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8
 -subq 5 -trellis 1 -refs 1 -coder 0 -me_range 16 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt 200k -maxrate 96k
 -bufsize 96k -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -level 30 -aspect 320:240 -g 30 -async 2
 -s 320x240 -f mpegts out.ts


 Suggested by others for 128k
 ffmpeg -d -i 'rtmp://123.123.117.16:1935/live/abcdpc2 live=1' -re -g 250 -keyint_min 25 -bf 0 -me_range 16 -sc_threshold 40 -cmp 256 -coder 0 -trellis 0 -subq 6 -refs 5 -r 25 -c:a libfaac -ab:a 48k -async 1 -ac:a 2 -c:v libx264 -profile baseline -s:v 320x180 -b:v 96k -aspect:v 16:9 -map 0 -ar 22050 -vbsf h264_mp4toannexb -flags -global_header -f segment -segment_time 10 -segment_format mpegts /dev/shm/stream128ios%09d.ts 2>/dev/null
 */

 
// get red5 context and segmenter
 
if (service == null) {
 
ApplicationContext appCtx = (ApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
 service
= (SegmenterService) appCtx.getBean("segmenter.service");
 
}
 
// path
 
String servletPath = request.getServletPath();
 
//get the requested stream
 
final String streamName = servletPath.substring(1, servletPath.indexOf(".m3u8"));
 log
.debug("Request for stream: {} playlist", streamName);
 
//check for the stream
 
if (service.isAvailable(streamName)) {
 log
.debug("Stream: {} is available", streamName);
 
// get the segment count
 
int count = service.getSegmentCount(streamName);
 log
.debug("Segment count: {}", count);
 
// check for minimum segment count and if we dont match or exceed
 
// wait for (minimum segment count * segment duration) before returning
 
if (count < minimumSegmentCount) {
 log
.debug("Starting wait loop for segment availability");
 
long maxWaitTime = minimumSegmentCount * service.getSegmentTimeLimit();
 
long start = System.currentTimeMillis();
 
do {
 
try {
 
Thread.sleep(500);
 
} catch (InterruptedException e) {
 
}
 
if ((System.currentTimeMillis() - start) >= maxWaitTime) {
 log
.info("Maximum segment wait time exceeded for {}", streamName);
 
break;
 
}
 
} while ((count = service.getSegmentCount(streamName)) < minimumSegmentCount);
 
}
 
/*
 HTTP streaming spec section 3.2.2
 Each media file URI in a Playlist has a unique sequence number.  The sequence number of a URI is equal to the sequence number
 of the URI that preceded it plus one. The EXT-X-MEDIA-SEQUENCE tag indicates the sequence number of the first URI that appears
 in a Playlist file.
 */

 
// get the completed segments
 
Segment[] segments = service.getSegments(streamName);
 
if (segments != null && segments.length > 0) {
 
//write the playlist
 
PrintWriter writer = response.getWriter();
 
// set proper content type
 response
.setContentType("application/x-mpegURL");
 
// for the m3u8 content
 
StringBuilder sb = new StringBuilder("#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-ALLOW-CACHE:NO\n");
 
// get segment duration in seconds
 
long segmentDuration = service.getSegmentTimeLimit() / 1000;
 
// create the heading
 sb
.append(String.format("#EXT-X-TARGETDURATION:%s\n#EXT-X-MEDIA-SEQUENCE:%s\n", segmentDuration, segments[0].getIndex()));
 
// loop through them
 
for (int s = 0; s < segments.length; s++) {
 
Segment segment = segments[s];
 
// get sequence number
 
int sequenceNumber = segment.getIndex();
 log
.trace("Sequence number: {}", sequenceNumber);
 sb
.append(String.format("#EXTINF:%.1f, segment\n%s_%s.ts\n", segment.getDuration(), streamName, sequenceNumber));
 
// are we on the last segment?
 
if (segment.isLast()) {
 log
.debug("Last segment");
 sb
.append("#EXT-X-ENDLIST\n");
 
break;
 
}
 
}
 
final String m3u8 = sb.toString();
 log
.debug("Playlist for: {}\n{}", streamName, m3u8);
 writer
.write(m3u8);
 writer
.flush();
 
} else {
 log
.trace("Minimum segment count not yet reached, currently at: {}", count);
 response
.setIntHeader("Retry-After", 60);
 response
.sendError(503, "Not enough segments available for " + streamName);
 
}
 
} else {
 log
.debug("Stream: {} is not available", streamName);
 response
.sendError(404, "No playlist for " + streamName);
 
}
 
}


}





--

---
You received this message because you are subscribed to the Google Groups "red5" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.