web developer & system programmer

coder . cl

ramblings and thoughts on programming...


wowza media server scheduler

published: 24-12-2010 / updated: 24-12-2010
posted in: development, java, programming, tips
by Daniel Molina Wegener

Wowza Media Server is a Tomcat server adapted to serve audio and video streams using the RTMP protocol. One great advantage of this Media Server is the fact that you can extend it using Java modules. The API is a little bit hard to understand, but very flexible. One of my recent projects was to create a Dynamic Scheduler Module for the Wowza Media Server.

cycled media files

Media Files stored on the hard drive, were encoded using the MP4 codec. Those files were cycling in ranges of 10 to 20 files and updated dynamically, like rotation logs, but with video file names being reused along the Media Server play list. In other cases, the Media Server play list is very dynamic, and the list of media files changes during the playback recording. I’ve created a very simple algorithm which lists the files and sorts them. The plug-in must handle that list of files on the cycle and realign the file order to use the last modified file according to the given time of minutes.

/***
 * Returns a list of File objects that match the given stream
 * name on the given directory.
 *
 * @param strm      Stream name.
 * @param bdir      Directory to List.
 * @return          An array of found File objects.
 */
public static File[] getFileNamesForStream(String strm, String bdir) {
    ArrayList<File> res = new ArrayList<File>();
    if (strm == null || bdir == null) {
        return new File[0];
    }
    if (strm.trim().length() == 0 || bdir.trim().length() == 0) {
        return new File[0];
    }
    File fdir = new File(bdir);
    if (fdir == null) {
        return new File[0];
    }
    File[] rawdir = fdir.listFiles();
    if (rawdir == null || rawdir.length == 0) {
        return new File[0];
    }
    for (int i = 0; i < rawdir.length; i++) {
        String name = rawdir[i].getName();
        if (name.indexOf(strm) >= 0) {
            res.add(new File(bdir + File.separator + name));
        }
    }
    Collections.sort(res, new FileDateComparator());
    return (File[]) res.toArray(new File[0]);
}

Getting the file list sorted is quite easy. And reorder the the files according to the cycle and the given timestamp is very easy too.

/**
 * Returns the cycled list of File object to be scheduled on the
 * Wowza Server.
 *
 * @param strm          input stream File name pattern.
 * @param bdir          input base directory.
 * @param mins          minutes elapsed.
 * @return              a list of files to cycle in the Wowza Server.
 */
public static File[] getCycledFileList(String strm, String bdir, long mins) {
    if (mins <= 0) {
        return new File[0];
    }
    File[] lst1 = WowzaTools.getFileNamesForStream(strm, bdir);
    ArrayList<File> lst2 = new ArrayList<File>();
    ArrayList<File> lst3 = new ArrayList<File>();
    ArrayList<File> retLst = new ArrayList<File>();
    for (int i = 0; i < lst1.length; i++) {
        if (WowzaTools.getMatchElapsedTime(lst1[i], mins)) {
            lst2.add(lst1[i]);
        } else {
            lst3.add(lst1[i]);
        }
    }
    for (int i = 0; i < lst2.size(); i++) {
        retLst.add((File)lst2.get(i));
    }
    for (int i = 0; i < lst3.size(); i++) {
        retLst.add((File)lst3.get(i));
    }
    return (File[]) retLst.toArray(new File[0]);
}

Probably the trick is in the timestamp comparing routine:

/***
 * Looks if the file is immediate before the given range
 * of minutes.
 *
 * @param inf       input file.
 * @param mins      minutes.
 * @return          true if the file matches.
 */
public static boolean getMatchElapsedTime(File inf, long mins) {
    long ct = System.currentTimeMillis();
    long ct_d = ct - (mins * 60000);
    long modtm = inf.lastModified();
    return (modtm >= ct_d);
}

the matching timestamp

The Wowza Media Server recordings are stored sequentially on the <StorageDir/> directory. Since the recordings are dynamically generated, and opening each stream to get his time is a little bit hard to the Media Server, I’ve used the File modification time to get information about the time sequence of each file. Once the files are sorted, timestamps looks as follows:

if (filesToAdd.length > 1) {
    File prev = filesToAdd[(filesToAdd.length - 1)];
    for (int pi = 0; pi < filesToAdd.length; pi++) {
        File cs = filesToAdd[pi];
        int secs = (int)((prev.lastModified() - cs.lastModified()) / 1000);
        secs = secs > 0 ? secs : secs * -1;
        if (pi == 0) {
            playlist.addItem(cs.getName(), ft, secs);
        } else {
            playlist.addItem(cs.getName(), 0, secs);
        }
        prev = cs;
    }
}

The algorithm looks very simple. But the ft is the key argument for first list item: ((c - s) - (c - m)). Where c is the current time, m is the modification time and c is the time shifting argument. So, the difference is used to calculate the first file on the playlist time shifting to use the proper offset in seconds. The solution is simple, and can be used in other tasks on the Wowza Media Server. I think that you can avoid many complex tasks using the modification time on media files.

Now I’m thinking on the creation of commercial extensions for the Wowza Media Server ;)


No coments yet.

post a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>