Using Angular’s $log provider

The AngularJS $logProvider

Logging and debugging is a daily occurrence when developing JavaScript based applications. AngularJS provides a simple, effective way to enable logging in your applications – the $logProvider. $logProvider is a used to enable logging and the $log object is used to log within your applications objects.

Enabling

Enabling the $logProvider is as simple as injecting the $log object into your object and then calling $log.debug(); to send logs to the console.
Example:

Turning off logging

You can disable logging (AngularJS 1.1.2+) by calling
$logProvider.debugEnabled(false);
Example:

Extending

AngularJS provides a mechanisms to extend the built in $logProvider, $provider.decorator(). An example will get the point across much more quickly than me trying to explain it all.
Example:

Basically we intercept the call using decorate so we can add on the features and functionality we need to the $log.debug() call.

AngualrJS version 1.2.16 was used as a basis for this article.
Thanks to Burleson Thomas and this post for the extension example.

AngularJS: Getting Started

Getting Started

AngularJS is a JavaScript framework that provides lots of solutions to many of web development’s problems. This isn’t to say that it is the end-all-be-all of web development tools, but it does provide a great jumping off point for your web application.

There are a few things that you’ll need to wrap your head around before jumping in full force but let’s take a quick look at the basics with the following super-simple web application.

HTML

Nothing in the HTML should surprise you. We’re working with a few HTML tags, attributes and a bit of “templating”. If you’re not familiar with things like HandlebarsJS, check it out – not that it has much to do with AngularJS, but it is useful to know about.

JavaScript

We haven’t written any JavaScript. We’re only including the angular.min.js script in the fiddle.

And what have we gained? Well, admittedly the sample application isn’t overly useful or exciting. We are, however, leveraging a couple of the main concepts of Angular with this sample:

  1. Model/view/controller (MVC): Your HTML template is the “view”. That view is affected by Angular’s model, called the scope. The scope is kind of hidden in this example, but you’ll become very familiar with scope as you work more with Angular.
  2. 2-way binding (wha?!?!): A “super feature” of angular – when we type into the text field, the text value of the H2 updates as we type.

For those of us who aren’t so good with delayed gratification, then this sample is pretty great. But, this is only the beginning. AngularJS has so much more to offer. We’re starting with small steps & keeping it simple…for now. There will be plenty more AngularJS to follow.

AngularJS, what is it?

AngularJS, Simply Put

AngularJS

AngularJS is a JavaScript framework. It provides an Application Programming Interface (API) that allows you to build web applications using JavaScript, HTML, & CSS. The API that Angular provides is a well thought out and robust set of solutions that provide you the ability to create powerful web applications more simply & quickly.

AngularJS lets you extend HTML vocabulary for your application. The resulting environment is extraordinarily expressive, readable, and quick to develop. – angularjs.org

Thinking Differently

For me, and I’m imagining many others, Angular required a paradigm shift in the way I thought about application development. Angular doesn’t just allow you to manipulate the DOM. What angular does is allow you as the developer to extend the existing features of HTML and add on to the capabilities of the browser in a very slick way. Angular’s angle (ha!) is to control the view by inspecting the DOM for an injection point where you can hen create the features of your application. These injections can be objects that act as the logical control center for an existing tag like an <h1> or adding on attributes to that same tag. Angular also allows you to create completely new tags that can be used in you application. The result then become the components, views and logic of you’re application.

The point being is that now you have control over the features and functionality of the browser for your application. Understanding how your new features features are added to the browser, and how you can best structure your application to allow for simplified and maintainable development is the first step. The way I tend to think of it is that Angular is responsible for telling the browser what to do, you just need to provide the logic and data to allow it to do so. That is a very important concept to grasp with Angular – the data is what drives the application. You don’t, and shouldn’t, manipulate the DOM directly. You change the data, then it is up to angular to render the view correctly based on that data.

For example, instead of using jQuery (or angular’s jQLite) to manipulate the DOM to show some text or change a class, we change the data model. This change is evaluated and angular then updates and renders the view appropriately based on that changed data. You can see this in the sample below:

The displayed H1 value changes when we type into the text input. This is Angular being notified that the model has updated and that the view needs to re-render the updated data. There is nothing we need to do programmatically to set the “innerHTML” of a specific div with the new data. Also notice that there is no custom JavaScript in the jsFiddle above. We only have to include the library & specify the model & bindings.

For me, it took some time to get it. But, once I did, I loved it!

AngularJS: Simple Tabs

Because of projects and personal interest, I’ve been working with AngularJS lately. A recent project needed a tab component to switch between views. The following is a simple prototype of the tab component that I created.

With a background in Flash and Flex it is interesting and exciting to have to change my perspective on how to create and use these types of UI components. Angular forces you to think about the structure of not just the components, but also the entire application in a different way. Before, I’d look to a component framework to provide the tab control. Now, these types of components seem to come together rather simply. So far, Angular is impressing me.

JavaScript Chops Meetup

JavaScript Chops Meetup

I was hanging out with Matt O’Donnell this weekend and we got to talking about javascript & how we both want to get better at it (along with HTML & CSS). We’re both super busy with work and life and don’t have a ton of spare time, but still want to make improving our javascript chops a priority.

So, here is the idea – a short (30-60 minute) online meetup.

Here is what I’m thinking a Google Hangout in the evening, once or twice a month. We spend 30-60 minutes talking mainly about javascript with some HTML & CSS tossed in to make sure we are covering our bases. Everyone involved takes turns presenting on and discussing concepts like “the basics” & “getting started”, best practices, tools, libraries, & frameworks, specific projects, problems and/or questions that we have.

Initially I’d like to keep it to a small group so the conversations don’t get out of hand.
What do you think? Want to do it?

Sign up to let me know you’re interesteed & I’ll let you about the when’s and where’s.

#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
/* Add your own MailChimp form style overrides in your site stylesheet or in this style block.
We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */

Subscribe to our mailing list

* indicates required

Email Format

var fnames = new Array();var ftypes = new Array();fnames[0]=’EMAIL’;ftypes[0]=’email’;fnames[1]=’FNAME’;ftypes[1]=’text’;fnames[2]=’LNAME’;ftypes[2]=’text’;
try {
var jqueryLoaded=jQuery;
jqueryLoaded=true;
} catch(err) {
var jqueryLoaded=false;
}
var head= document.getElementsByTagName(‘head’)[0];
if (!jqueryLoaded) {
var script = document.createElement(‘script’);
script.type = ‘text/javascript’;
script.src = ‘//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js’;
head.appendChild(script);
if (script.readyState && script.onload!==null){
script.onreadystatechange= function () {
if (this.readyState == ‘complete’) mce_preload_check();
}
}
}

var err_style = ”;
try{
err_style = mc_custom_error_style;
} catch(e){
err_style = ‘#mc_embed_signup input.mce_inline_error{border-color:#6B0505;} #mc_embed_signup div.mce_inline_error{margin: 0 0 1em 0; padding: 5px 10px; background-color:#6B0505; font-weight: bold; z-index: 1; color:#fff;}’;
}
var head= document.getElementsByTagName(‘head’)[0];
var style= document.createElement(‘style’);
style.type= ‘text/css’;
if (style.styleSheet) {
style.styleSheet.cssText = err_style;
} else {
style.appendChild(document.createTextNode(err_style));
}
head.appendChild(style);
setTimeout(‘mce_preload_check();’, 250);

var mce_preload_checks = 0;
function mce_preload_check(){
if (mce_preload_checks>40) return;
mce_preload_checks++;
try {
var jqueryLoaded=jQuery;
} catch(err) {
setTimeout(‘mce_preload_check();’, 250);
return;
}
var script = document.createElement(‘script’);
script.type = ‘text/javascript’;
script.src = ‘http://downloads.mailchimp.com/js/jquery.form-n-validate.js’;
head.appendChild(script);
try {
var validatorLoaded=jQuery(“#fake-form”).validate({});
} catch(err) {
setTimeout(‘mce_preload_check();’, 250);
return;
}
mce_init_form();
}
function mce_init_form(){
jQuery(document).ready( function($) {
var options = { errorClass: ‘mce_inline_error’, errorElement: ‘div’, onkeyup: function(){}, onfocusout:function(){}, onblur:function(){} };
var mce_validator = $(“#mc-embedded-subscribe-form”).validate(options);
$(“#mc-embedded-subscribe-form”).unbind(‘submit’);//remove the validator so we can get into beforeSubmit on the ajaxform, which then calls the validator
options = { url: ‘http://forkbites.us1.list-manage.com/subscribe/post-json?u=db7398b2dfeb32d6f5b9b98df&id=e8c62e786c&c=?’, type: ‘GET’, dataType: ‘json’, contentType: “application/json; charset=utf-8”,
beforeSubmit: function(){
$(‘#mce_tmp_error_msg’).remove();
$(‘.datefield’,’#mc_embed_signup’).each(
function(){
var txt = ‘filled’;
var fields = new Array();
var i = 0;
$(‘:text’, this).each(
function(){
fields[i] = this;
i++;
});
$(‘:hidden’, this).each(
function(){
var bday = false;
if (fields.length == 2){
bday = true;
fields[2] = {‘value’:1970};//trick birthdays into having years
}
if ( fields[0].value==’MM’ && fields[1].value==’DD’ && (fields[2].value==’YYYY’ || (bday && fields[2].value==1970) ) ){
this.value = ”;
} else if ( fields[0].value==” && fields[1].value==” && (fields[2].value==” || (bday && fields[2].value==1970) ) ){
this.value = ”;
} else {
if (/[day]/.test(fields[0].name)){
this.value = fields[1].value+’/’+fields[0].value+’/’+fields[2].value;
} else {
this.value = fields[0].value+’/’+fields[1].value+’/’+fields[2].value;
}
}
});
});
$(‘.phonefield-us’,’#mc_embed_signup’).each(
function(){
var fields = new Array();
var i = 0;
$(‘:text’, this).each(
function(){
fields[i] = this;
i++;
});
$(‘:hidden’, this).each(
function(){
if ( fields[0].value.length != 3 || fields[1].value.length!=3 || fields[2].value.length!=4 ){
this.value = ”;
} else {
this.value = ‘filled’;
}
});
});
return mce_validator.form();
},
success: mce_success_cb
};
$(‘#mc-embedded-subscribe-form’).ajaxForm(options);

});
}
function mce_success_cb(resp){
$(‘#mce-success-response’).hide();
$(‘#mce-error-response’).hide();
if (resp.result==”success”){
$(‘#mce-‘+resp.result+’-response’).show();
$(‘#mce-‘+resp.result+’-response’).html(resp.msg);
$(‘#mc-embedded-subscribe-form’).each(function(){
this.reset();
});
} else {
var index = -1;
var msg;
try {
var parts = resp.msg.split(‘ – ‘,2);
if (parts[1]==undefined){
msg = resp.msg;
} else {
i = parseInt(parts[0]);
if (i.toString() == parts[0]){
index = parts[0];
msg = parts[1];
} else {
index = -1;
msg = resp.msg;
}
}
} catch(e){
index = -1;
msg = resp.msg;
}
try{
if (index== -1){
$(‘#mce-‘+resp.result+’-response’).show();
$(‘#mce-‘+resp.result+’-response’).html(msg);
} else {
err_id = ‘mce_tmp_error_msg’;
html = ‘

‘+msg+’

‘;

var input_id = ‘#mc_embed_signup’;
var f = $(input_id);
if (ftypes[index]==’address’){
input_id = ‘#mce-‘+fnames[index]+’-addr1′;
f = $(input_id).parent().parent().get(0);
} else if (ftypes[index]==’date’){
input_id = ‘#mce-‘+fnames[index]+’-month’;
f = $(input_id).parent().parent().get(0);
} else {
input_id = ‘#mce-‘+fnames[index];
f = $().parent(input_id).get(0);
}
if (f){
$(f).append(html);
$(input_id).focus();
} else {
$(‘#mce-‘+resp.result+’-response’).show();
$(‘#mce-‘+resp.result+’-response’).html(msg);
}
}
} catch(e){
$(‘#mce-‘+resp.result+’-response’).show();
$(‘#mce-‘+resp.result+’-response’).html(msg);
}
}
}

The Browser Is Where It’s At

I used to create a lot of Flash based content – be it video players, widgets, or full on applications what I built was built using the Flash Platform. I haven’t opened the Flash IDE or Flash Builder to create a SWF in a long, long…long time. What does this mean? First, technology is changing – no doubt. Second, I get to learn some new stuff. Guess what? I’m okay with that.

Users, platforms, and developers have have forced browsers to evolve. The browser is no longer just a window to view content. It is an environment that applications execute in. It used to be that you’d open a browser, search for something, or read something, then close it down and get to work. Now, what you work on is in in the browser & those browsers are open all day.

What should I learn? What should you learn, if you’re not already? Learn about the stuff that happens in the browser – Yep,  JavaScript, CSS, & HTML. Learn the existing APIs as well as the upcoming API changes and additions.

Banner of Browser Logos

The browser as a first-class citizen?

Browsers are more powerful, they are more feature rich, and are becoming first class citizens when it comes to how people use them. I don’t think that it is 100% where it needs to be, but it won’t be long until that tipping point that causes a shift in how people use and think about the browser. Consider Google services – to name a few, GMail, Drive & Docs, & Calendar. These services all run in the browser and each week new features are added to make them more comparable and sometimes better than their desktop counterparts. It used to be that everyone relied on Microsoft Office and Word to create documents, edit and track changes, Outlook to manage their email and calendar. Now, all of that and more is in the browser, of course you can still use desktop applications to manage that data, but, like I said there will be a tipping point. The point of this? Pay attention to the browser, the browser is where things are headed.

Why the browser?

Because:

  • Browsers are familiar to users
  • They exist for all major platforms
  • Browsers have established a quick and easy update path
  • Browsers will become more accepted by the enterprise
  • They take advantage of HTTP protocols
  • Browsers leverage new and existing technology

Familiarity

A browsers is an easy path to entry. A browser is a simple concept to grasp and easy to explain and learn. Although it has a low learning curve, browsers have and can be extended on to provide functionality needed for today and tomorrow’s users.

All major platforms

All major platforms have a browser. Desktop & mobile, even TVs and DVD players have browsers. For developers the headache is support different platforms. You will have to provide platform specific code. But, the main point to get across here is that HTTP, JavaScript, & CSS are will be supported by more and more platforms.

Quick and easy update path

Chrome and Firefox update at lightning speed, and for many users without them even knowing. This helps roll out new features (Web RTC, Media APIs etc) more quickly. There is a major barrier when it comes to the enterprise and government, but this is something that I think will change in the near future.

Accepted by the enterprise & government

Currently these are two areas where updates and browser versions can really hold back innovation. But, with the current direction of and additions to APIs and security, this issue should become a problem of the past as browser updates are easier, more secure and become the norm rather than the exception for the enterprise and government.

HTTP Protocol

HTTP has been around forever and for good reason. It works. It is flexible and powerful. Innovation and increased bandwidth allow for more innovative and more interesting uses of the protocol. HTTP video streaming is a great example of this. The client is responsible for managing the HTTP requests that it will need to successfully play back video served up in HTTP chunks, while still providing expected functionality to the user. We still have conversations about “chatty” applications, but these conversations will be minimised as a different perspective and different technologies emerge that leverage HTTP to a greater and more efficient degree.

Leveraging Existing & New Technology

As with HTTP, other established technologies will be accepted and leveraged by the browser. For instance, browsers are finally getting around to integrating media playback. WebRTC is another example things like WebSockets, node.js, socket.io, there are some really interesting things going on excite about the next-gen applications and tools that will be created.

What I see

All of this isn’t to say what we as developers are doing now will go away. Things certainly won’t change immediately. But, I am looking to the future, evaluating trends and technology, and emerging conversations, and what I see is the browser. Maybe not in it’s current incarnation, but the browser is what I see.

What do you see?

HDS & Bootstrap Data

inspect-hds-bootstrap

Working with HDS Bootstrap Data

I’ve always been curious about the bootstrap data for HDS content. Recently, I had the chance to find out more about it and get in some fun development with Node.js. We’ve been kicking around the idea of building a tool set for Adobe Media Server using Node.js and possibly socket.io. Last weekend we got some of the ideas going and one of those was parsing the hds bootstrap data created when content is packages for HDS delivery.

The bootstrap data can live in a couple of places:

  1. In the <metadata> node of an F4M file
  2. In an external .bootstrap file

The .bootstap file contains binary data and the F4M file contains the same binary data that has been Base64 encoded. So, getting to the data is pretty trivial – either read in the .bootstrap file or un-encode the string in that is in the F4M. Getting to the data contained in the bootstrap binary data is the fun part.

Understanding the bootstrap data

To do so, check out the F4V file format specification. This PDF gives you the details for the entire F4V file format. If you read through the PDF, you’ll  see that it is built using what are called “boxes”. These boxes are given identifiers such as “abst”,  “adaf”,  “adkm”,  “aeib”,  “afra”,  & “afrt” to name a few. Each box contains a header, that header identifies the box by its identifier and lets you know how much data is contained in the box. These boxes are also arranged into a hierarchy, so each box has some data that is specific to some part of the data contained in the file.

It is all in the boxes

The boxes that we are concerned with are “abst” or the bootstrap information box, “asrt” or the segement run table box, and “afrt” or the fragment run table box.

The abst box

The bootstrap information box contains information needed to bootstrap playing of HDS content – specifically to construct the URLs necessary to retrieve the fragments for playback. This includes information about the server, media, & segment information.

The asrt box

The segment run table box contains data about the segments for the media item. There can be multiple ASRT boxes – each representing a different quality level. There are some rules that you’ll want to pay attention to for the data in the asrt box:

  • An asrt box can represent fragment runs for several quality levels.
  • Each entry gives the first segment number for a run of segments with the same count of fragments.
    • The count of segments having this same count of fragments can be calculated by subtracting the first segment number in this entry from the first segment number in the next entry.

The afrt box

The fragment run table box is used to find the fragment corresponding to a given time. Similar to the asrt box, there are some rules that you’ll want to pay attention to:

  • Fragments are individually identifiable by the URL scheme based on segment number and fragments number.
  • Fragments may vary both in duration and in number of samples.
  • Duration of the fragments are stored in the this box.
  • A Fragment Run Table may represent fragments for more than one quality level.
  • Each fragment run table entry gives the first fragment number for a run of fragments with the same duration.
    • The count of fragments having this same duration can be calculated by subtracting the first fragment number in this entry from the first fragment number in the next entry.

Parsing the bootstrap data using Node.js

Parsing binary data in Node.js can be done using “Buffer”. For the most part parsing the bootstrap data was pretty straight forward. There is one issue that I ran into with 64bit Integers which was solved easily enough (there are node modules for just about anything) using the node-int64 module to represent the 64Bit Integers. Once that was solved it was just a matter of parsing through the box header to figure out where you are in the dataset, and then creating the appropriate data structures to represent what you want and need in from the bootstrap data.

In our case we want to be able to monitor live events across multiple servers to make sure that they are all on the same segment and fragment. We’re building a services that in the case that something happens to a server and it goes haywire, will notify another service that can then restart or shut down that particular server or let caching servers know that they need to flush or refresh cache. We’re still dreaming up things we can use this type of data for.

Just want to get to that data?

If you have a .bootstrap file you can use the f4fpackager.exe that is part of the Adobe Media Server toolset to inspect the bootstrap data. All you need to do is run the tool with the argument “–inspect-bootstrap”. So the command looks something like the following if you have a bootstrap file named mydata.bootstrap:

[shell].f4fpackager.exe –input-file=mydata.bootstrap –inspect-bootstrap[/shell]

Anyways, if you have any questions or input let me know in the comments.

Configuring DVR HTTP Live Streaming

Previously I showed you how to configure the Adobe Media Server to deliver Live HTTP Dynamic Streaming (HDS). Let’s add on a feature that will allow your users to pause and rewind the live video – DVR.

DVR HTTP Live Streaming

DVR can be (and actually already is) configured for both HTTP Dynamic Streaming (HDS) as well as HTTP Live Streaming (HLS). The configuration for HLS is a bit more involved, so we’ll tackle the configuration for live HDS streams first. I’ll be walking through the configuration of Adobe Media Server (AMS) 5+.

Configuring DVR for HDS

HDS DVR is controlled using a set-level manifest file. A set-level manifest file is an XML file that provides information about where to play media back from as well as other configuration information for the stream. The Set level manifest file contains some or all of the following:

  1. A base URL.
  2. One or more <media> nodes that point to the media to be played.
  3. Information about DVR.

Below is an example of a set level manifest file for a single bitrate:

<manifest xmlns="http://ns.adobe.com/f4m/2.0"> 
    <baseURL>http://localhost/hds-live/livepkgr/_definst_/liveevent/</baseURL> 
    <media href="livestream1.f4m" bitrate="150"/> 
    <media href="livestream2.f4m" bitrate="500"/> 
    <media href="livestream3.f4m" bitrate="700"/> 
</manifest>

To specify dvr information for this live HDS stream, we add a <dvrInfo/> node that contains a windowDuration attribute.

<manifest xmlns="http://ns.adobe.com/f4m/2.0"> 
    <dvrInfo windowDuration="30"/>
    <baseURL>http://localhost/hds-live/livepkgr/_definst_/liveevent/</baseURL> 
    <media href="livestream1.f4m" bitrate="150"/> 
    <media href="livestream2.f4m" bitrate="500"/> 
    <media href="livestream3.f4m" bitrate="700"/> 
</manifest>

Setting the amount of recorded content

The value for windowDuration can be set to -1, meaning all of the recorded content is available, or it can be set to a number greater than zero (don’t set windowDuration to 0, it can cause problems). This number represents the number of minutes of recorded content, in minutes, that is available to the client to seek through. By Default Adobe Media Server keeps 3 hours of content. You can configure the amount of content the server keeps in the Application.xml or the Event.xml files.

To configure the disk management options in the Application.xml file specify a value in hours for the <DiskManagementDuration> node. You can also use decimal values to specify minutes as in the example below :

<Application> 
    <HDS> 
        <Recording>     
            <FragmentDuration>4000</FragmentDuration> 
            <SegmentDuration>16000</SegmentDuration> 
            <DiskManagementDuration>4.5</DiskManagementDuration> 
        </Recording> 
    </HDS> 
</Application>

To specify the disk management options in the Event.xml add the the <DiskManagementDuration> node with the same parameters similar to the example below:

<Event> 
    <EventID>liveevent</EventID> 
    <Recording>     
        ... 
        <DiskManagementDuration>4.5</DiskManagementDuration> 
    </Recording> 
</Events>

Creating the set-level manifest file

A tool to generate set-level manifest files is installed with AMS. Known as the F4M Configurator, you can find it in the {AMS_INSTALL}/tools/f4mconfig/configurator directory. I’ve written another article on using the F4M Configurator, so I won’t go into that here. But, feel free to review that article – Using Adobe’s F4M Configurator Tool. Using the set-level manifest above we now have a manifest file that we can work with.

<manifest xmlns="http://ns.adobe.com/f4m/2.0"> 
    <dvrInfo windowDuration="30"/>
    <baseURL>http://192.168.1.113/hds-live/livepkgr/_definst_/myliveevent/</baseURL> 
    <media href="mylivestream.f4m" bitrate="300"/>
</manifest>

Now that we have configured the amount of recorded media and created a set-level manifest file. We can test DVR for our live stream in a player.

  1. Upload the set-level manifest to a web server. I’ve uploaded mine to http://thekuroko.com/samples/hds/myliveevent.f4m
  2. Go ahead and start up your live stream.
  3. Then open the following URL: http://www.osmf.org/dev/2.0gm/setup.html
  4. Enter the path to your set-level manifest as the value for “src” in the “FlashVars” section.
  5. Set the “Stream Type” to “dvr”.
  6. Click the “Preview” button at the bottom of the form.

Your stream should play, but because we’ve added the <dvrInfo> node, the progress bar should reflect that you can now see back into the content.

Without the dvrInfo:

DVR HTTP Live Streaming Control Bar Without DVR Info

With the dvrInfo:

DVR HTTP Live Streaming Control Bar With DVR Info

Configuring DVR for HLS

DVR for HLS is configured in a similar way to HDS. Except you don’t need the set-level manifest.

The DVR or sliding window can be configured at an event level in the Event.xml file, at an application level in the Application.xml file, or at a server level in the Apache httpd.conf file.

Configuring at the event level

  1. Open the Event.xml file in the {AMS_INSTALL}/applications/livepkgr/_definst_/myliveevent to configure the sliding window for the event named myliveevent in the livepkgr application.
  2. Update the Event.xml with the following XML to create a 1 hour sliding window:
    <HLS> 
        <MediaFileDuration>8000</MediaFileDuration> 
        <SlidingWindowLength>450</SlidingWindowLength> 
    </HLS>
  3. The MediaFileDuration sets the length of each .ts segment in milliseconds and the SlidingWindowLength value configures the number of .ts segments to keep around.
  4. The resulting Event.xml file should look like:
    <Event>
      <EventID>myliveevent</EventID>
      <Recording>
        <FragmentDuration>4000</FragmentDuration>
        <SegmentDuration>400000</SegmentDuration>
        <DiskManagementDuration>1</DiskManagementDuration>
      </Recording>
      <HLS>
          <MediaFileDuration>8000</MediaFileDuration>
          <SlidingWindowLength>450</SlidingWindowLength>
      </HLS>
    </Event>

Configuring HLS at the application level

  1. Open the Application.xml file in {AMS_INSTALL}/applications/livepkgr
  2. Add the same XML node set as the event level configuration to the Application.xml file.
  3. The resulting file should resemble the following:
    <Application> 
        <StreamManager> 
            <Live> 
                <AssumeAbsoluteTime>true</AssumeAbsoluteTime> 
            </Live> 
        </StreamManager> 
        <HLS> 
            <MediaFileDuration>8000</MediaFileDuration> 
            <SlidingWindowLength>450</SlidingWindowLength> 
        </HLS> 
    </Application>

Configuring HLS at the server level

  1. Open the httpd.conf file in {AMS_INSTALL}/Apache2.2/conf (If you are using a non-default Apache install, your httpd.conf file will be in a different location)
  2. Find the Location directive for “hls-live” & add/update the value for HLSMediaFileDuration to be 8000 and HLSSlidingWindowLength to be 450. This will set a sliding window duration of 1 hour for all live HLS streams. By default the sliding window is set to 48 seconds (6 * 8 second .ts files).
    <Location /hls-live>
        HLSHttpStreamingEnabled true
        HttpStreamingLiveEventPath "../applications"
        HttpStreamingContentPath "../applications"
        HLSMediaFileDuration 8000
        HLSSlidingWindowLength 450
        HLSFmsDirPath ".."
        HttpStreamingUnavailableResponseCode 503
        ...
    </Location>

Playing back the HLS content

There are quite a few ways to test the HLS content:

  1. Safari on a Mac with Flash disabled.
  2. Quicktime Player
    1. From the main menu choose “File” -> “Open Location”
    2. Then type in the URL to the .M3U8 – Ex: http://192.168.1.114/hls-live/livepkgr/_definst_/myliveevent/mylivestream.m3u8
  3. VLC
    1. From the main menu choose “File” -> “Open Network”
    2. Then type in the URL to the .M3U8 – Ex: http://192.168.1.114/hls-live/livepkgr/_definst_/myliveevent/mylivestream.m3u8
  4. Use an iOS device – either setup an HTML player like VideoJS or MediaElementJS or open the .M3U8 UR directly. Currently this is the only way I was able to use the sliding window. The stream will playback in the other players, but the control bar will not reflect the available content to seek through.

Resources

  • Disk management: http://help.adobe.com/en_US/flashmediaserver/devguide/WSeb6b7485f9649bf23d103e5512e08f3a338-8000.html#WSec225f632fa00875-23954b6f1300b641158-8000

Creating Set-level Manifest Files Using the F4M Configurator Tool

Here is a quickie on how to use Adobe’s F4M configurator tool to create set level manifest files.

The configurator is installed with AMS 5.0 and can be found in the following directory: {AMS_INSTALL}/tools/f4mconfig/configurator/

  1. Open the f4mconfig.html file in a browser.
    Adobe Media Server - F4M Configurator Tool
  2. Enter the path to your server, application and event. For example for an event named “myliveevent” using the “livepkgr” application the Base URL would look like: http://192.168.1.113/hds-live/livepkgr/_definst_/myliveevent/
  3. If you are going to use DVR, enter a value for “DVR Window Duration”. A value of -1 configures the DVR window for all of the available content. A value greater than zero configures the amount of time in seconds available before the live point. We’ll set a 30 minute DVR window, so 1800 seconds.
  4. Enter the stream name and bit rate for each bit rate you are encoding. For this example lets say we have a single bit rate of 300 for a stream named “mylivestream”
    Adobe's F4M Configurator - Stream Name and DVR Window
  5. Click the “Save Manifest” button. A file will be created and you will prompted to save the file. Save the file and open it.
  6. The file should look similar to the following:
    <manifest xmlns="http://ns.adobe.com/f4m/2.0">
      <baseURL>http://192.168.1.113/hds-live/_definst_/myliveevent/</baseURL>
      <dvrInfo windowDuration="1800"/>
      <media href="mylivestream" bitrate="300"/>
    </manifest>
  7. This file can now be used to specify live DVR content. If you add an additional bitrate, you not have a set-level F4M file for multi-bitrate streaming.

Hope this helps and save a bit of time for you.