• How to use Wowza nDVR Playlist Request API

    This article demonstrates the usage of a Wowza nDVR API for controlling playlist requests.

    Notes:

    • This functionality is supported in Wowza Media ServerŪ 3.0.3.02 or later.
    • The UTC-based playlist request functionality is supported in Wowza Media Server 3.0.4 or later.
    • Microsoft Silverlight players do not currently recognize non-live streams. Recorded streams are presented as live streams. This is a known issue that we are working on.

    Default Behavior

    By default, nDVR determines the playlist to play when a DVR manifest or playlist is requested via URL.

    • Strobe/OSMF player:
      Code:
      http://[wowza-address]:1935/dvr/myStream/manifest.f4m?DVR
    • Apple iOS device:
      Code:
      http://[wowza-address]:1935/dvr/myStream/playlist.m3u8?DVR
    • Microsoft Silverlight:
      Code:
      http://[wowza-address]:1935/dvr/myStream/Manifest?DVR


    The default logic is as follows:
    • If the stream is being recorded it is considered live, otherwise it is considered recorded.
    • Both live and recorded use as a starting time:
      • The earliest point in the recording, or
      • The latest time minus the DVR window if one is been specified.
    • A live stream has no end time.
    • A recorded stream is played back with the end time of the recording.


    Due to the nature of live HTTP Streaming, live streams must cache extra chunks after the currently playing live point. Recorded streams do not have this restriction and include these cached chunks in their playlist.

    The playlist/manifest presented to the player looks different based on whether the DVR store is live or recorded. Different player technologies handle these playlists differently as well. For example, live playlists typically start playing at the "live point" while recorded playlists start playing at the earliest time.

    Playlist Request Delegate

    nDVR provides a delegate property "dvrPlaylistRequestDelegate" which is a mechanism to provide a different playlist request via Java.

    The property should be set in Application.xml under Application/DVR/Properties and should reference a fully-qualified classname that extends the class "com.wowza.wms.dvr.DvrBasePlaylistRequestDeleg ate".

    When the playlist is requested, this delegate's method getDvrPlaylistRequest() will be called to provide the playlist request.



    Playlist Request

    The Playlist Request Delegate is responsible for generating a playlist request object. It is passed an application context, the DVR store object (which contains methods for querying the underlying known chunks), and a queryMap of URL parameters that were passed in.

    A playlist request has a beginning time and an optional end-time.

    If the start time specified is not valid (for example, before the beginning of the recording or after the end of the recording or the specified end time), the beginning of the recording is defaulted to as the start time.

    If no end-time is specified:

    • For a live store (one that is currently recording), the playlist presented to the player will be a live playlist.
    • For a recorded store (one that is not recording), the playlist presented to the player will be a recorded playlist, using the end time of the recording.


    If a specific end-time is specified for a live DVR store, the resultant playlist will be of the recorded variety because live playlists do not have an end-time.

    An example

    Wowza nDVR includes one playlist request delegate: "com.wowza.wms.dvr.impl.DvrStartDurationPlaylistRe questDelegate". This delegate generates the playlist request based on URL query parameters when specifying the playlist or manifest. For example to play from minute 1 through minute 6, we specify a start time of 60 seconds (60000 ms) and a duration of 300 seconds (300000 ms).

    • Strobe/OSMF player:
      Code:
      http://[wowza-address]:1935/dvr/myStream/manifest.f4m?DVR&wowzadvrplayliststart=60000&wowzadvrplaylistduration=300000
    • Apple iOS device:
      Code:
      http://[wowza-address]:1935/dvr/myStream/playlist.m3u8?DVR&wowzadvrplayliststart=60000&wowzadvrplaylistduration=300000
    • Microsoft Silverlight:
      Code:
      http://[wowza-address]:1935/dvr/myStream/Manifest?DVR&wowzadvrplayliststart=60000&wowzadvrplaylistduration=300000


    To use this delegate, add the following property to your Application.xml under Application/DVR/Properties:
    Code:
    <Properties>
    	<Property>
    		<Name>dvrPlaylistRequestDelegate</Name>
    		<Value>com.wowza.wms.dvr.impl.DvrStartDurationPlaylistRequestDelegate</Value>
    	</Property>	
    </Properties>
    To use different query parameters, also include these properties in the same location and change the values:
    Code:
    <Properties>
    	<Property>
    		<Name>dvrPlaylistRequestDelegate</Name>
    		<Value>com.wowza.wms.dvr.impl.DvrStartDurationPlaylistRequestDelegate</Value>
    	</Property>
    	<Property>
    		<Name>dvrPlaylistDurationQueryParameter</Name>
    		<Value>wowzadvrplaylistduration</Value>
    	</Property>	
    	<Property>
    		<Name>dvrPlaylistStartQueryParameter</Name>
    		<Value>wowzadvrplayliststart</Value>
    	</Property>	
    </Properties>
    To add debug logging for this delegate, add the following property to your Application.xml under Application/DVR/Properties:
    Code:
    <Properties>
    	<Property>
    		<Name>dvrDebugPlaylistRequest</Name>
    		<Value>true</Value>
                     <Type>Boolean</Type>
    	</Property>	
    </Properties>
    Example Delegate

    To provide your own playlist delegate, it would look very much like "DvrStartDurationPlaylistRequestDelegate". You must provide 2 public methods as well as logic to determine your playlist request.

    This method is called for a single bitrate playlist request:
    Code:
    public DvrPlaylistRequest getDvrPlaylistRequest(IHTTPStreamerApplicationContext appContext, IDvrStreamStore store, Map<String, String> queryMap)
    This method is to request an ABR playlist:
    Code:
    public DvrPlaylistRequest getDvrPlaylistRequest(IHTTPStreamerApplicationContext appContext, List<IDvrStreamStore> stores, Map<String, String> queryMap)


    Consult the User's Guide on how to add your own code to Wowza.

    Code:
    package com.example.dvr.impl;
    
    import java.util.*;
    
    import com.wowza.wms.application.WMSProperties;
    import com.wowza.wms.dvr.*;
    import com.wowza.wms.dvr.IDvrConstants.DvrTimeScale;
    import com.wowza.wms.httpstreamer.model.IHTTPStreamerApplicationContext;
    import com.wowza.wms.logging.WMSLoggerFactory;
    
    public class DvrStartDurationPlaylistRequestDelegate extends DvrBasePlaylistRequestDelegate {
    	private static final String CLASSNAME = "DvrStartDurationPlaylistRequestDelegate";
    	private static final Class<DvrStartDurationPlaylistRequestDelegate> CLASS = DvrStartDurationPlaylistRequestDelegate.class;
        
        public static final String DVR_QUERYSTR_PLAYLIST_DURATION = "wowzadvrplaylistduration";
        public static final String DVR_QUERYSTR_PLAYLIST_START = "wowzadvrplayliststart";
        public static final String PROPKEY_DVR_PLAYLIST_DURATION_QUERY_PARAMETER = "dvrPlaylistDurationQueryParameter";
        public static final String PROPKEY_DVR_PLAYLIST_START_QUERY_PARAMETER = "dvrPlaylistStartQueryParameter";
        public static final String PROPKEY_DVR_PLAYLIST_LOG_REQUESTS = "dvrPlaylistDebugRequests";
        
        private boolean doDebug = false;
    
        
        public DvrPlaylistRequest getDvrPlaylistRequest(IHTTPStreamerApplicationContext appContext, IDvrStreamStore store, Map<String, String> queryMap) {       
            DvrPlaylistRequest availablePlaylist = getDefaultPlaylistRequest(DvrTimeScale.DVR_TIME, store);
            
            DvrPlaylistRequest newRequest = createRequestFromQueryParams(appContext, queryMap, availablePlaylist);
            
            return newRequest;
        }
    
    
        public DvrPlaylistRequest getDvrPlaylistRequest(IHTTPStreamerApplicationContext appContext, 
                                                                            List<IDvrStreamStore> stores, Map<String, String> queryMap) {
            DvrPlaylistRequest availablePlaylist = getDefaultPlaylistRequest(DvrTimeScale.DVR_TIME, stores);
    
            DvrPlaylistRequest newRequest = createRequestFromQueryParams(appContext, queryMap, availablePlaylist);
    
            return newRequest;
        }
    
        private DvrPlaylistRequest createRequestFromQueryParams(IHTTPStreamerApplicationContext appContext, 
                                                                                              Map<String, String> queryMap, DvrPlaylistRequest availablePlaylist) {
            DvrPlaylistRequest newRequest = new DvrPlaylistRequest();
            if (availablePlaylist != null) {
                newRequest.setPlaylistEnd(availablePlaylist.getPlaylistEnd());
                newRequest.setPlaylistStart(availablePlaylist.getPlaylistStart());
            }
            
            WMSProperties dvrProperties = getDvrProperties(appContext);
            String playStartQueryParameter = dvrProperties.getPropertyStr(PROPKEY_DVR_PLAYLIST_START_QUERY_PARAMETER, DVR_QUERYSTR_PLAYLIST_START);
            String playDurationQueryParameter = dvrProperties.getPropertyStr(PROPKEY_DVR_PLAYLIST_DURATION_QUERY_PARAMETER, DVR_QUERYSTR_PLAYLIST_DURATION);
    
    	this.doDebug = dvrProperties.getPropertyBoolean(PROPKEY_DVR_PLAYLIST_LOG_REQUESTS, doDebug);
    
    
            String playStartStr = queryMap.get(playStartQueryParameter);
            String playDurationStr = queryMap.get(playDurationQueryParameter);
    
            if (doDebug) {
                WMSLoggerFactory.getLogger(CLASS).info(String.format("%s : Request: %s:%s %s:%s ", CLASSNAME, playStartQueryParameter, playStartStr, playDurationQueryParameter, playDurationStr)); 
                WMSLoggerFactory.getLogger(CLASS).info(String.format("%s : Available Playlist: %s ", CLASSNAME, availablePlaylist));   	
            }
    
            if (availablePlaylist == null) {
                if (doDebug) {
                    WMSLoggerFactory.getLogger(CLASS).warn(String.format("%s : availablePlaylist is null.", CLASSNAME));    
                }
                return newRequest;
            }
    
            if (playStartStr != null)
            {
                try
                {
                    long playStart = Long.parseLong(playStartStr);
                    if (playStart < availablePlaylist.getPlaylistStart()) {
                    	if (doDebug) {
                    		WMSLoggerFactory.getLogger(CLASS).warn(String.format("%s : requestedStart:%d < availableStart:%d.  Using availableStart.", CLASSNAME, playStart, availablePlaylist.getPlaylistStart()));   	
                    	}
                    } 
                    else if (availablePlaylist.hasSpecifiedEnd() && playStart > availablePlaylist.getPlaylistEnd()) {
                    	if (doDebug) {
                    		WMSLoggerFactory.getLogger(CLASS).warn(String.format("%s : requestedStart:%d > availableEnd:%d.", CLASSNAME, playStart, availablePlaylist.getPlaylistEnd()));   	
                    	}
                    } else {
                        newRequest.setPlaylistStart(playStart);
                    } 
                    
                }
                catch(Exception e)
                {
                }
            }
            
    
    
            if (playDurationStr != null)
            {
                try
                {
                    long playDuration = Long.parseLong(playDurationStr);
                    long playEnd = newRequest.getPlaylistStart() + playDuration;
                    if (playEnd < availablePlaylist.getPlaylistStart()) {
                    	if (doDebug) {
                    		WMSLoggerFactory.getLogger(CLASS).warn(String.format("%s : requestedEnd:%d < availableStart:%d.  Using availableStart.", CLASSNAME, playEnd, availablePlaylist.getPlaylistStart()));   	
                    	}
                    } else if (availablePlaylist.hasSpecifiedEnd() && playEnd > availablePlaylist.getPlaylistEnd()) {
                    	if (doDebug) {
                    		WMSLoggerFactory.getLogger(CLASS).warn(String.format("%s : requestedEnd:%d > availableEnd:%d.  Using availableEnd.", CLASSNAME, playEnd, availablePlaylist.getPlaylistEnd()));   	
                    	}
                    } else {
                        newRequest.setPlaylistEnd(playEnd);
                    } 
                }
                catch(Exception e)
                {
                }
            }
            if (doDebug) {
                WMSLoggerFactory.getLogger(CLASS).info(String.format("%s : Resolved Playlist: %s ", CLASSNAME, newRequest));   	
            }
    
            return newRequest;
        }
    }
    Example code: Querying DVR Store times.

    This snippet demonstrates how to query the DVR store in your playlist delegate to determine the available times. Similar code may be useful when creating your own playlist request delegate.


    The Time Map contains mapping between dvr-time, packet time, and utc time every time the time has been reset.

    Code:
    private static final DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
    
    public DvrPlaylistRequest getDvrPlaylistRequest(IHTTPStreamerApplicationContext appContext, IDvrStreamStore store, Map<String, String> queryMap) {
        // . . .
            
        // Look at store and determine type (audio or video)
        int type = this.chooseManifestType(store);
            
        IDvrManifest manifest = store.getManifest();
    
        DvrManifestEntry firstEntry = manifest.getFirstEntry(type);
        System.out.printf("first   : %s\n", formatTime(firstEntry));
    
        // The last live DVR chunk is earlier than the last recorded for "live" stores
        if (store.isLive()) {
            DvrManifestEntry lastLiveEntry = manifest.getLastLiveEntry(type);
            System.out.printf("lastLive: %s\n", formatTime(lastLiveEntry));
        }
            
        DvrManifestEntry lastRecordedEntry = manifest.getLastRecordedEntry(type);
        System.out.printf("lastRec : %s\n", formatTime(lastRecordedEntry));
            
        IDvrTimeMap timeMap = manifest.getTimeMap();
        Collection<DvrManifestEntry> times = timeMap.getIndexMap().values();
        for (DvrManifestEntry e : times) {
            DvrManifestTimeMapEntry te = (DvrManifestTimeMapEntry)e;
            System.out.printf("timeSpan: %s\n", formatTime(te));
        }
            
        // . . .
    }
    
    private String formatTime(DvrManifestEntry entry) {
        if (formatter == null || entry == null) {
            return "format error";
        }
        return String.format("dvrTime:%12d pt:%12d utcTime:%s", 
                                      entry.getStartTimecode(), entry.getPacketStartTime(), formatter.format(new Date(entry.getUtcStartTime())));
    }

    Example code: Creating a UTC time based playlist request

    This snippet demonstrates how to create a playlist request based on UTC time. This snippet can be used with code above.

    Code:
    String UTC_FORMAT = "yyyy-MM-dd-HH:mm:ss";
    DateFormat formatter = new SimpleDateFormat(UTC_FORMAT);
    
    public DvrPlaylistRequest getDvrPlaylistRequest(IHTTPStreamerApplicationContext appContext, IDvrStreamStore store, Map<String, String> queryMap) {
        // . . . This could come from URL param or some other manner
        String startStr= "2012-02-14-11:30:00";
    
         // This is entire playlist request in UTC
         DvrPlaylistRequest fullPlaylistRequest = getDefaultLivePlaylistRequest(DvrTimeScale.UTC_TIME, store);
    
        // Convert start String to UTC
        Date date = null;
        if (!StringUtils.isEmpty(startStr)) {
             try {
                date = (Date)formatter.parse(startStr);  
             } catch (ParseException e) {
                date = null;
                //e.printStackTrace();
            }
        }
        // System.out.printf("'%s' --> date:%s\n", startStr, date);
    
         // If the date specified is less than the initial date we have to play, its not valid
         if (date != null && date.before(new Date(fullPlaylistRequest.getPlaylistStart())))
         {
              System.out.println("Requested start time before actual recording.");
              date = new Date(fullPlaylistRequest.getPlaylistStart());
         }
    
        DvrPlaylistRequest req;
        if (date != null) {
            req = new DvrPlaylistRequest(DvrTimeScale.UTC_TIME);
            req.setPlaylistStart(date.getTime());
        } else {
            // Use default
            req = super.getDvrPlaylistRequest(appContext, store, queryMap);
        }
        return req;
    }
    Consult the User's Guide on how to add your own code to Wowza.



    Comments 26 Comments
    1. Meinaart -
      This does not work in Wowza 3.0.5.

      String playStartQueryParameter = appContext.getProperties().getPropertyStr("dvrPlay listStartQueryParameter", "wowzadvrplayliststart");
      String playDurationQueryParameter = appContext.getProperties().getPropertyStr("dvrPlay listDurationQueryParameter", "wowzadvrplaylistduration");

      It always returns the default value and not the value in the configuration XML.

      Took me a while to figure it out.
    1. ScottKell -
      Quote Originally Posted by Meinaart View Post
      This does not work in Wowza 3.0.5.

      String playStartQueryParameter = appContext.getProperties().getPropertyStr("dvrPlay listStartQueryParameter", "wowzadvrplayliststart");
      String playDurationQueryParameter = appContext.getProperties().getPropertyStr("dvrPlay listDurationQueryParameter", "wowzadvrplaylistduration");

      It always returns the default value and not the value in the configuration XML.

      Took me a while to figure it out.
      I assume appContext is IHTTPStreamerApplicationContext. Then you are correct-- the appContext will not allow you to get properties defined in Application/DVR. It will let you get properties defined in HTTPStreamer.xml or Application/HTTPStreamer/Properties. For example, it will also not let you retrieve properties defined in Application/Properties.
      So you need to get the DVR properties from the HTTP Streamer context. Here's a code snippet that shows how to navigate to it. The class DvrBasePlaylistRequestDelegate, which you may be sub-classing, also provides a convenience method which will do the work for you. Also demonstrated below:


      Code:
      @Override
      public DvrPlaylistRequest getDvrPlaylistRequest(IHTTPStreamerApplicationContext appContext, IDvrStreamStore store, Map<String, String> queryMap) {
      
          String startParam = appContext.getProperties().getPropertyStr("dvrPlaylistStartQueryParameter", "undefined");
          String durParam = appContext.getProperties().getPropertyStr("dvrPlaylistDurationQueryParameter", "undefined");
          System.out.printf("App context does not hold them: %s|%s\n", startParam, durParam);
              
          IApplicationInstance appInstance = appContext.getAppInstance();
          WMSProperties properties = appInstance.getDvrProperties();
          startParam = properties.getPropertyStr("dvrPlaylistStartQueryParameter", "undefined");
          durParam = properties.getPropertyStr("dvrPlaylistDurationQueryParameter", "undefined");
          System.out.printf("Dvr Properties holds them: %s|%s\n", startParam, durParam); 
              
          WMSProperties props = getDvrProperties(appContext);
          startParam = props.getPropertyStr("dvrPlaylistStartQueryParameter", "undefined");
          durParam = props.getPropertyStr("dvrPlaylistDurationQueryParameter", "undefined");
          System.out.printf("Or use the convenience method: %s|%s\n", startParam, durParam);        
      . . .
    1. notreg -
      Is there a way iterates through manifest entries? Below code returns only one first entry:

      IDvrTimeMap timeMap = manifest.getTimeMap();
      Collection<DvrManifestEntry> times = timeMap.getIndexMap().values();
      for (DvrManifestEntry e : times) {
      DvrManifestTimeMapEntry te = (DvrManifestTimeMapEntry)e;
      System.out.printf("timeSpan: %s\n", formatTime(te));
      }
    1. ScottKell -
      Quote Originally Posted by notreg View Post
      Is there a way iterates through manifest entries? Below code returns only one first entry:

      IDvrTimeMap timeMap = manifest.getTimeMap();
      Collection<DvrManifestEntry> times = timeMap.getIndexMap().values();
      for (DvrManifestEntry e : times) {
      DvrManifestTimeMapEntry te = (DvrManifestTimeMapEntry)e;
      System.out.printf("timeSpan: %s\n", formatTime(te));
      }
      This note describes the types of information that are in the manifest.

      Basically the manifest consists of 'channel manifests' of specific types: audio and video are the most basic and these are the ones you want to iterate through. There are also specialized channels that are sparse such as metadata info, time map (which is what you are trying to iterate over). In your case there was only one of these items.

      The DvrChannelManifest has many functions to query info about the channel in many different ways.

      There is a difference between getRecordedEntries and getLiveEntries. getRecordedEntries will return info for every chunk that is in the recording. getLiveEntries will not return the last few as HTTP streaming holds back a couple chunks that are not presented as part of the live stream.

      The code you want looks like this:

      Code:
      // Get audio with IVHost.CONTENTTYPE_AUDIO or video channel with IVHost.CONTENTTYPE_VIDEO
      
      DvrChannelManifest audioManifest = manifest.getManifestChannel(IVHost.CONTENTTYPE_AUDIO);
      Collection<DvrManifestEntry> entries= audioManifest.getRecordedEntries();
      for (DvrManifestEntry e : entries) {
          System.out.printf("audio entry: %s\n", e);
      }
    1. notreg -
      Multibitrate with smil does not work, does it?
    1. ScottKell -
      Quote Originally Posted by notreg View Post
      Multibitrate with smil does not work, does it?
      The playlist API does work with dvr using Adaptive bitrate (multiple bitrates in a smil file), if that's what you are asking.

      Scott
    1. notreg -
      I mean that kind of request http://&#91;wowza-address]:1935/dvr/smil...uration=300000

      Delegate does not work.
    1. ScottKell -
      Quote Originally Posted by notreg View Post
      I mean that kind of request http://&#91;wowza-address]:1935/dvr/smil...uration=300000

      Delegate does not work.
      It should. Can you give more details if it is not.
      Also note you can turn on some additional logging to help debug your particular case. The article contains how to do that.
      Also if the times you specify are invalid, the default behavior is to return the entire playlist, not shortening it at all.
    1. notreg -
      wowza.3.1.2

      When it is http://localhost:1935/dvr/Stream1_15...ration=1800000 I see my log from my class delegate and request is work. When it is http://localhost:1935/dvr/smil:Strea...ration=1800000 I don't see my delegate class log any message instead I have this log and request does not work:

      INFO server comment - DvrBasePlaylistRequestDelegate.getDvrPlaylistReque st(store
      s:[dvr/_definst_/Stream1_150/Stream1_150.0, dvr/_definst_/Stream1_300/Stream1_30
      0.0, dvr/_definst_/Stream1_650/Stream1_650.0, ]) returns {DvrPlaylistRequest: [n
      ot live] timeScale:PACKET_TIME playlistStart:404773, playlistEnd:11159716}.
    1. ScottKell -
      Quote Originally Posted by notreg View Post
      wowza.3.1.2

      When it is http://localhost:1935/dvr/Stream1_15...ration=1800000 I see my log from my class delegate and request is work. When it is http://localhost:1935/dvr/smil:Strea...ration=1800000 I don't see my delegate class log any message instead I have this log and request does not work:

      INFO server comment - DvrBasePlaylistRequestDelegate.getDvrPlaylistReque st(store
      s:[dvr/_definst_/Stream1_150/Stream1_150.0, dvr/_definst_/Stream1_300/Stream1_30
      0.0, dvr/_definst_/Stream1_650/Stream1_650.0, ]) returns {DvrPlaylistRequest: [n
      ot live] timeScale:PACKET_TIME playlistStart:404773, playlistEnd:11159716}.
      From the log output, it looks like you've created your own custom playlist delegate class.

      You're going to have to provide more information: how doesn't it work, what is the behavior?

      Is the default one that ships with the product working?

      From the one line of log output, it looks like it is returning a playlist-- it has detected that one or more of the streams is not live (not being recorded), which means that the DVR will present it to the player as not live. It has also detected a common playlist that starts at packetTime of 404773 and ends with playlistEnd:11159716, which is approx 180 minutes. It looks like you've requested 30 minutes. My guess is that 180 minutes is the common recording areas of the streams.

      I've updated the sample code in the article with an example that has a bit more logging. Take a look at that, it may help you.

      The algorithm is this:
      1. Your getDvrPlaylistRequest(appContext, List<> stores, Map<> queryMap) gets called
      2. (At least in the default delegate provided) The base class is called to get the currently available playlist. This method uses areStoresLive() to determine if the stores are recording (live) or not recording.
      3. If not recording (your case from the log), getDefaultRecordedPlaylistRequest is called. This finds the common area of the stores.
      4. It returns to your getDvrPlaylistRequest. At this point I would print out the returned available playlist to see what it is.
      5. Then this playlist is chopped using createRequestFromQueryparam

      My guess is that this chopping is not working as you expect. I would add some logging to this method.
    1. yildizib -
      hi notreg, does not work, because com.example.dvr.impl.DvrStartDurationPlaylistReque stDelegate class alreadey exist in the wms-dvr.jar . if your class name is same as com.example.dvr.impl.DvrStartDurationPlaylistReque stDelegate, class loader coludn't load your class. Change your package name...
    1. notreg -
      Package is different. I know how to use packages. And if it was package it does not work at all, but it does not work only with multibitrate.
    1. notreg -
      It is obvious for me: multibitrate not trigger this simple code which means it can't work.

      package ru.somecompany.wms.dvr.delegate;

      import java.text.DateFormat;
      import java.text.SimpleDateFormat;
      import java.util.Map;

      import com.wowza.wms.dvr.DvrBasePlaylistRequestDelegate;
      import com.wowza.wms.dvr.DvrPlaylistRequest;
      import com.wowza.wms.dvr.IDvrStreamStore;
      import com.wowza.wms.httpstreamer.model.IHTTPStreamerAppl icationContext;
      import com.wowza.wms.logging.WMSLogger;
      import com.wowza.wms.logging.WMSLoggerFactory;

      public class RequestsDelegate extends DvrBasePlaylistRequestDelegate {

      private WMSLogger logger = WMSLoggerFactory.getLogger(getClass());

      private WMSLogger getLogger() {
      return logger;
      }

      String UTC_FORMAT = "yyyy-MM-dd-HH:mm:ss";
      DateFormat formatter = new SimpleDateFormat(UTC_FORMAT);

      public DvrPlaylistRequest getDvrPlaylistRequest(
      IHTTPStreamerApplicationContext appContext, IDvrStreamStore store,
      Map<String, String> queryMap) {
      try {
      getLogger().info(getClass().getSimpleName());
      return super.getDvrPlaylistRequest(appContext, store, queryMap);
      } catch (Exception e) {
      getLogger().error(getClass().getSimpleName(), e);
      }
      return null;
      }
      }
    1. ScottKell -
      Quote Originally Posted by notreg View Post
      It is obvious for me: multibitrate not trigger this simple code which means it can't work.


      }
      This method is called for mbr:
      Code:
         public DvrPlaylistRequest getDvrPlaylistRequest(IHTTPStreamerApplicationContext appContext, List<IDvrStreamStore> stores, Map<String, String> queryMap)
      This one is called for single:
      Code:
          public DvrPlaylistRequest getDvrPlaylistRequest(IHTTPStreamerApplicationContext appContext, IDvrStreamStore store, Map<String, String> queryMap)
      You are looking at the single bitrate one.
      So ensure that is called, and add the recommended logging.

      I also verified the default playlist that ships with the produce is working for MBR.

      Cheers,

      Scott
    1. notreg -
      That's it. I miss mbr method.

      So can I do like this in mbr method?
      return getDvrPlaylistRequest(appContext, stores.get(0), queryMap);

      What if mbr stores not equal? How it managed with one returned DvrPlaylistRequest and list of stores?
    1. ScottKell -
      Quote Originally Posted by notreg View Post
      That's it. I miss mbr method.

      So can I do like this in mbr method?
      return getDvrPlaylistRequest(appContext, stores.get(0), queryMap);

      What if mbr stores not equal? How it managed with one returned DvrPlaylistRequest and list of stores?

      Just FYI, for success with MBR streams, they need to be keyframe aligned (keyframes in the streams you are using in MBR should start at same time) and the should be (generally) equal in length.

      I would not do it the way you suggested looking at only one stream unless you are sure this will give you what you want-- as you said, how will that work if the streams are not alogned.


      If you look at the sample code in the article that's a good model for how to do it:

      The getDefaultPlaylistRequest looks at all the DVR streams, and returns a playlist based on the union of all of them. It considers start and stop of entire stream.
      Then createRequestFromQueryparams takes that original playlist and chops it based on the start and duration params. The source from this method is in the article.


      Code:
      public DvrPlaylistRequest getDvrPlaylistRequest(IHTTPStreamerApplicationContext appContext, List<IDvrStreamStore> stores, Map<String, String> queryMap) {
              DvrPlaylistRequest availablePlaylist = getDefaultPlaylistRequest(DvrTimeScale.DVR_TIME, stores);
      
              DvrPlaylistRequest newRequest = createRequestFromQueryParams(appContext, queryMap, availablePlaylist);
      
              return newRequest;
      }
    1. lee2510 -
      i found this method doesn't work on flowplayer
      when i pass http://&#91;wowza-address]:1935/dvr/mySt...uration=300000
      instead of http://&#91;wowza-address]:1935/dvr/mySt...nifest.f4m?DVR
      the flowplayer show only black screen and there's no stream, of course without those 2 parameter, everything works fine
      any help would be appreciated.
    1. rrlanham -
      I think the problem is that the OSMF framework, which Flowplayer is using to playback HDS streams, does not pass querystring parameters.

      Or the values are out of range. Did you see the section "Querying DVR Store times" of this guide?

      Richard
    1. lee2510 -
      Thank Richard.
      I've found the way to make it work, simply using "escape" in flowplayer
      For example:
      url: escape("http://[wowza-address]:1935/dvr/myStream/manifest.f4m?DVR&wowzadvrplayliststart=60000&wowza dvrplaylistduration=300000")
      That would work!
    1. lee2510 -
      Hi Richard, I have 1 more question:
      How can I only set "wowzadvrplayliststart" without "wowzadvrplaylistduration" parameter? So that user can start at 1 start point and play to live position
      Or is there a way to set "wowzadvrplaylistduration" parameter = unlimited (go to live)? Sorry for my bad english