News

4 Aug 2017

[Release] Stable release 2.12 now available!

Hello everyone! Stable release 2.12 of MistServer is now available! The full change log is available here and downloads are here. Our Pro-edition customers will receive a new build notification in their e-mail. Here are some highlights: (Better) support for the PCM, Opus, MPEG2...

Hello everyone! Stable release 2.12 of MistServer is now available! The full change log is available here and downloads are here. Our Pro-edition customers will receive a new build notification in their e-mail.

Here are some highlights:

  • (Better) support for the PCM, Opus, MPEG2 (Pro-only) and MP2 (Pro-only) codecs.
  • Raw H.264 Annex B input and output support
  • exec-style inputs (autostarts/stops a given command to retrieve stream data) for TS (Pro-only) and raw H.264
  • Pro only: HEVC support in RTSP input and output
  • Pro only: TS over HTTP input support
  • Subnet mask support for push input whitelisting
  • Improved support for quickly stopping and restarting an incoming push
  • Many other small fixes and improvements!
read moreless
1 Aug 2017

[Blog] Raw H.264 from Raspberry Pi camera to MistServer

Hello everyone, Balder here. As you might know we have native ARMv7 and ARMv8 builds since MistServer 2.11, this allows for an easier install of MistServer on the Raspberry Pi. As a 3d printing enthusiast I use my Raspberry Pi...

Hello everyone, Balder here. As you might know we have native ARMv7 and ARMv8 builds since MistServer 2.11, this allows for an easier install of MistServer on the Raspberry Pi. As a 3d printing enthusiast I use my Raspberry Pi quite a lot to monitor my prints, but I was a bit let down by quality and stability of most solutions as many panics were luckily solved simply by pressing "f5" as it was the camera output, not the print that failed.

I needed a better solution and with the recent native MistServer ARM release we had the perfect excuse to try and do something directly with the Raspberry Pi cam. One of the new features we have in MistServer 2.12 is raw H264 support for all editions of MistServer. Below I will be describing just how I am using this new feature.

Ingesting Raspberry Pi Cam directly into MistServer

Ingesting the RaspiCam is a little different from other inputs, as instead of an address we will be filling in the command to generate video and dump the video to standard output. It will look something like this:

Image of stream configurations within MistServer to use Raspberry Pi camera as input

As you can see our new input is used by using: h264-exec: Which will run the rest of the line as a shell command and ingest its output as raw Annex B H.264. Do note that there is no support for shell escapes whatsoever so if you need to use spaces or other types of escapes inside your arguments run a script instead and put those in the script.

Raspivid is the most direct method of ingesting the Raspberry Pi camera footage in H264 format, powered by the hardware encoder. If your binary is not in your path, you might need to install the binaries or enter the full path. For example in ArchLinuxARM the path is /opt/vc/bin/raspivid instead of just plain raspivid

Recommended settings for ingesting RaspiCam

There’s a few settings we recommend or are downright required in order to ingest the camera correctly.

As you can see from our example we use the following:

h264-exec:raspivid -t 0 -pf high -lev 4.2 -g 10 -ih -qp 35 -o -

Required settings

  • -t 0

    This disables the timeout, to make sure the stream keeps going.

  • -ih

    This will insert the headers inline, which MistServer requires in order to ingest the stream correctly.

  • -o -

    This will output the video to standard output, which MistServer uses to ingest.

Recommended settings

  • -pf high

    This sets the H264 profile to high, we tend to see better qualities with this setting.

  • -lev 4.2

    This sets the H264 profile to 4.2 (our highest option), which tends to be better supported among protocols than our other options

  • -g 10

    This setting makes sure there is a keyframe every 10 seconds, which tends to make the camera more live, you can lower the number for an even more live stream, but bandwidth costs will be raised.

  • -qp 35

    This sets the quality of the stream, we tend to see the best results around 35, but it is a personal setting.

Watching your live stream

If filled in correctly, you should now see your stream show up when you try to watch it. The neat thing about this method is that it will only start ingesting the stream if you try to watch it, so you are not wasting resources for a stream that is not viewed, attempts a restart in the event raspivid crashes and closes automatically when no one is watching it anymore.

Image of Raspberry Pi camera live footage playing

I am pretty happy with my new method to monitor my prints, as it has been more reliable than my older set up of using mjpeg-streamer and using the media server functionality of MistServer allows me to monitor my prints more easily from outside my home network as this method is quite a lot better in terms of bandwidth and quality.

Well that was all for this post, see you all next time!

read moreless
18 Jul 2017

[Blog] Load balancing especially for media servers

Hello streaming media enthusiasts! Jaron here, with a new background article. In this blog post, I'll detail the why and how of our load balancing solution, which is currently being beta-tested in some of our clients' production deployments. Quick primer on load...

Hello streaming media enthusiasts! Jaron here, with a new background article. In this blog post, I'll detail the why and how of our load balancing solution, which is currently being beta-tested in some of our clients' production deployments.

Quick primer on load balancing and how it relates to media

The concept of load balancing is not new; not by a long shot. It is probably one of the most researched and experimented-upon topics in computing. So, obviously, the load balancing problem has already been thoroughly solved. There are many different techniques and algorithms, both generic and specific ones.

Media is a very particular type of load, however: it highly benefits from clustering. This means that if you have a media stream, it's beneficial to serve all users for this stream from the same server. Serving the same stream from multiple servers increases the load much more than serving multiple users from a single server. Of course that changes when there are so many users connected that a single server cannot handle the load anymore, as you will be forced to spread those users over multiple servers. Even then, you will want to keep the amount of servers per stream the lowest possible, while spreading the users more or less equally over those servers.

Why is this important?

Most traditional load balancing methods will either spread randomly, evenly, or to whatever server is least loaded. This works, of course. However, it suffers greatly when there is a sudden surge of new viewers coming in. These viewers will either all be sent to the same server, or spread over all servers unnecessarily. The result of this is sub-optimal use of the available servers... and that means higher costs for a lesser experience for your end-users.

Load balancing, MistServer-style

MistServer's load balancing technique is media-aware. The load balancer maintains constant contact with all servers, receiving information on bandwidth use, CPU use, memory use, active streams, viewer counts per stream and bit rates per stream. It uses these numbers to preemptively cluster incoming requests on servers while making predictions on bandwidth use after these users will connect. This last trick in particular allows MistServer to handle surges of new users correctly without overloading any single server. Meanwhile, it constantly adjusts its predictions with new data received from the servers, accounting for dropped users and changes in bandwidth patterns over time.

A little extra

Our load balancer also doubles as an information source for the servers: they can make queries as to what server to best pull a feed from for the lowest latency and highest efficiency. As an extra trick, the load balancer (just like MistServer itself) is fully capable of making any changes to its configuration without needing to be restarted, allowing you to add and remove servers from the list dynamically without affecting your users. Last but not least, the load balancer also provides merged statistics on viewer counts and server health for use in your own monitoring and usage tracking systems.

Want to try it?

As mentioned in the introduction, our load balancer is still in beta. It's fully stable and usable; we just want to collect a little bit more data to further improve its behavior before we release it to everyone. If you are interested in helping us make these final improvements and/or in testing cool new technology, contact us and let us know!

Wrapping up

That was it for this post. Next time Balder will be back with a new subject!

read moreless
3 Jul 2017

[Blog] Building an access control system with triggers and PHP

Hello everyone, Carina here. In this blog I will discuss how PHP can be used to inform MistServer that a certain user has access to streams through the use of triggers. The Pro version is required for this. The process Before I...

Hello everyone, Carina here. In this blog I will discuss how PHP can be used to inform MistServer that a certain user has access to streams through the use of triggers. The Pro version is required for this.

The process

Before I go into the implementation details, let's take a look at what the user validation process is going to be like.

  1. The process starts when a user opens a PHP page, which we shall call the video page
  2. The website knows who this user is through a login system, and uses their userid and their ip to generate a hash, which is added to video urls on the video page
  3. The video is requested from MistServer, which fires the USER_NEW trigger
  4. The trigger requests another PHP page, the validation page, which checks the hash and returns whether or not this user is allowed to watch streams
  5. If the user has permission to watch, MistServer sends the video data

Alright, on to implementation.

The video page

Please see the sample code below.

First, the user id should be retrieved from an existing login system. In the sample code, it is set to 0, or the value provided through the user parameter. Then, the user id and ip are hashed, so that the validation script can check if the user id hasn't been tampered with. Both the user id and the hash will be appended to the video url, so that MistServer can pass them along to the validation script later.
Next, the video is embedded into the page.

<?PHP

  //some general configuration
  //where MistServer's HTTP output can be found
  $mist_http_host = "http://<your website>:8080";
  //the name of the stream that should be shown
  $stream = "<stream name>";

  //set the userid
  //in a 'real' situation, this would be provided through a login system
  if (isset($_REQUEST["user"])) { $user_id = intval($_REQUEST["user"]); }
  else { $user_id = 0; }

  //create a hash containing the remote IP and the user id, that the user id can be validated against
  $hash = md5($_SERVER["REMOTE_ADDR"].$user_id."something very very secret");

  //prepare a string that can pass the user id and validation hash to the trigger
  $urlappend = "?user=".$user_id."&hash=".$hash;

  //print the embed code with $urlappend where appropriate
?>
<div class="mistvideo" id="tmp_H0FVLdwHeg4j">
  <noscript>
    <video controls autoplay loop>
      <source
        src="<?PHP echo $mist_http_host."/hls/".$stream."/index.m3u8".$urlappend; ?>"
        type="application/vnd.apple.mpegurl">
      </source>
      <source
        src="<?PHP echo $mist_http_host."/".$stream.".mp4".$urlappend; ?>"
        type="video/mp4">
      </source>
    </video>
  </noscript>
  <script>
    var a = function(){
      mistPlay("<?PHP echo $stream; ?>",{
        target: document.getElementById("tmp_H0FVLdwHeg4j"),
        loop: true,
        urlappend: "<?PHP echo $urlappend; ?>"
      });
    };
    if (!window.mistplayers) {
      var p = document.createElement("script");
      p.src = "<?PHP echo $mist_http_host; ?>/player.js"
      document.head.appendChild(p);
      p.onload = a;
    }
    else { a(); }
  </script>
</div>

Trigger configuration

Sample configuration of the USER_NEW trigger

In the MistServer Management Interface, a new trigger must be added, using the following settings:

  • Trigger on: USER_NEW
  • Applies to: Check the streams for which user validation will be required, or don't check any streams to require validation for all streams
  • Handler: The url to your validation script
  • Blocking: Check.
    This means that MistServer will use the scripts output to decide whether or not to send data to the user.
  • Default response: 1 or 0.
    If for some reason the script can't be executed, this value will be used by MistServer. When this is set to 1, everyone will be able to watch the stream in case of a script error. When set to 0, no one will.

The validation page

Please see the sample code below.
It starts off by defining what the script should return in case something unexpected happens. Here I've chosen for this to be 0 (don't play the video), as this probably occurs when someone is trying to tamper with the system.
Next, some functions are defined that will make the rest of the script more readable.

The first actual action is to check whether the script was called by one of the supported triggers. The trigger type is sent by MistServer in the X_TRIGGER request header.

Next, the payload that MistServer has sent in the request body is retrieved. The payload contains variables like the stream name, ip, protocol, etc. These will be used shortly, starting with the protocol.
When the protocol is HTTP or HTTPS, the user is probably trying to request javascript or CSS files that are required for the meta player. There is no reason to deny someone access to these, and thus the script informs MistServer it is allowed.

If the protocol is something else, the user id and hash are retrieved from the request url, which is passed to the validation script in the payload.
The hash is compared to what the hash should be, which is recalculated with the provided payload variables. If the user id has been tampered with or if the request is from another ip, this check should fail and MistServer is told not to send video data.

Otherwise, the now validated user id can be used to evaluate if this user is allowed to watch streams.

<?PHP
  //what the trigger should return in case of errors
  $defaultresponse = 0;

  ///\function get_payload
  /// the trigger request contains a payload in the post body, with various information separated by newlines
  /// this function returns the payload variables in an array
  function get_payload() {

    //translation array for the USER_NEW (and CONN_OPEN, CONN_CLOSE, CONN_PLAY) trigger
    $types = Array("stream","ip","connection_id","protocol","request_url","session_id");

    //retrieve the post body
    $post_body = file_get_contents("php://input");
    //convert to an array
    $post_body = explode("\n",$post_body);

    //combine the keys and values, and return
    return array_combine(array_slice($types,0,count($post_body)),$post_body);
  }

  ///\function no_ffffs
  /// removes ::ffff: from the beginning of an IPv6 that is actually an IPv6, so that it gives the same result
  function no_ffffs($str) {
    if (substr($str,0,7) == "::ffff:") {
      $str = substr($str,7);
    }
    return $str;
  }

  ///\function user_can_watch
  /// check whether a user can watch streams
  ///\TODO replace this with something sensible ;)
  function user_can_watch ($userid) {
    $can_watch = Array(1,2,3,6);
    if (in_array($userid,$can_watch)) { return 1; }
    return 0;
  }


  //as we're counting on the payload to contain certain values, this code doesn't work with other triggers (and it wouldn't make sense either)
  if (!in_array($_SERVER["HTTP_X_TRIGGER"],Array("USER_NEW","CONN_OPEN","CONN_CLOSE","CONN_PLAY"))) {
    error_log("This script is not compatible with triggers other than USER_NEW, CONN_OPEN, CONN_CLOSE and CONN_PLAY");
    die($defaultresponse);
  }




  $payload = get_payload();

  //always allow HTTP(S) requests
  if (($payload["protocol"] == "HTTP") || ($payload["protocol"] == "HTTPS")) { echo 1; }
  else {

    //error handling
    if (!isset($payload["request_url"])) {
      error_log("Payload did not include request_url.");
      die($defaultresponse);
    }

    //retrieve the request parameters
    $request_url_params = explode("?",$payload["request_url"]);
    parse_str($request_url_params[1],$request_url_params);

    //more error handling
    if (!isset($payload["ip"])) {
      error_log("Payload did not include ip.");
      die($defaultresponse);
    }
    if (!isset($request_url_params["hash"])) {
      error_log("Request_url parameters did not include hash.");
      die($defaultresponse);
    }
    if (!isset($request_url_params["user"])) {
      error_log("Request_url parameters did not include user.");
      die($defaultresponse);
    }

    //validate the hash/ip/userid combo
    if ($request_url_params["hash"] != md5(no_ffffs($payload["ip"]).$request_url_params["user"]."something very very secret")) {
      echo 0;
    }
    else {

      //the userid is valid, let's check if this user is allowed to watch the stream
      echo user_can_watch($request_url_params["user"]);

    }
  }

That's all folks: the validation process has been implemented.

Further information

The example described above is written to allow a user access to any streams that are served through MistServer. If access should be limited to certain streams, there are two ways to achieve this.
The simplest option is to configure the USER_NEW trigger in MistServer to only apply to the restricted streams. With this method it is not possible to differentiate between users (UserA can watch stream1 and 3, UserB can watch stream2).
If differentiation is required, the user_can_watch function in the validation script should be modified to take into account the stream that is being requested (which is included in the trigger payload).

The USER_NEW trigger is only fired when a user is not yet cached by MistServer. If this is not desired, for example when the validation script also has a statistical purpose, the CONN_PLAY trigger can be used instead. It should be noted however, that for segmented protocols such as HLS, CONN_PLAY will usually fire for every segment.

More information about the trigger system can be found in chapter 4.4 of the manual.
If any further questions remain, feel free to contact us.

Next time, Jaron will be discussing our load balancer.

read moreless
26 Jun 2017

[Blog] AV1

Hello, this is Erik with a post about the up and coming AV1 video codec. While none of the elements that will eventually be accepted into the final design are currently fixed (and the specification of the codec might not...

Hello, this is Erik with a post about the up and coming AV1 video codec. While none of the elements that will eventually be accepted into the final design are currently fixed (and the specification of the codec might not even be finalized this year), more and more companies have started working on support already. This post is meant to give a more in-depth view of what is currently happening, and how everything will most likely eventually fall into place.

NetVC

Internet Video Codec (NetVC) is a working group within the Internet Engineering Task Force (IETF). The goal of the group is to develop and monitor the requirements for the standardization of a new royalty-free video codec.

The current version of their draft specifies a variety of requirements of the next generation royalty-free codec. These range from low-latency real-time collaboration to 4k IPTV, and from adaptive bit rate to the existence of a real-time software encoder.

While these requirements are not bound to a specific codec, it determines what would be needed for a codec to be deemed acceptable. Techniques from the major contenders have been taken into account whilst setting up this list in 2015, with the Alliance for Open Media (AOMedia) forming shortly after to develop the AV1 codec to comply to these requirements in a joint effort between the Daala, Thor and VP10 teams.

AV1

The AOMedia has been formed to streamline development of a new open codec, as multiple groups were working simultaneously on different new codecs. AV1 is the first codec to be released by the alliance. With many of the influential groups within the internet video area participating in its development, it will be set-up to compete with HEVC in regard to compression and visual quality while remaining completely royalty free.

Because steering completely clear of the use of patents when designing a codec is a daunting task, the AOMedia group has decided to provide a license to use their IP in any project using AV1, as long as the user does not sue for patent infringement. This, in combination with a thorough Intellectual Property Rights review, means that AV1 will be free of royalties. This should give it a big edge over the main competitor HEVC, for which there are multiple patent pools one needs to pay license fees to, with an unknown amount of patents not contained in these pools.

Decided is to take the development of the VP10 codec as developed by Google as the base for AV1. In addition to this, AV1 will contain techniques from both Daala (by Xiph and Mozilla) and Thor (by Cisco).

The AV1 codec is developed to be used in combination with the opus audio codec, and wrapped in WebM for HTML5 media and WebRTC for real time uses. As most browsers already support both the WebM format and opus codec, this immediately generates a large potential user base.

Experiments

Development of AV1 is based mainly around the efforts of Google's VP10. Built upon VP9, VP10 was geared towards better compression while optimizing visual qualities in the encoded bitstream. With the formation of AOMedia, Google decided to drop VP10 development, and instead focus on solely the development of AV1.

Building upon a VP9 core, contributors can submit so called experiments to the repository, which can then be enabled and tested by the community. Based on whether the experiment is considered worthwhile, it enters IPR review, and after a successful pass there, it will be enabled by default and added to the specification. Most experiments have come from the respective developments from VP10, Daala and Thor, but other ideas are welcomed as well. As of yet, no experiments have been finalized.

Performance

Multiple tests have been run to compare AV1 to both H.264 and HEVC, with varying results over time. However, with no final selection of experiments, these performance measures have been made not only with different settings, but with completely different experiments enabled.

While it is good to see how the codec is developing over time, a real comparison between AV1 and any other codec can not be reliably made until the bitstream is frozen.

What's next?

With the end of the year targeted for finalizing the bitstream, the amount of people interested in the codec will probably only grow. With the variety of companies that AOMedia consists of, the general assumption is that the adoption of the codec and hardware support for encoding/decoding will be made available in a matter of months after the bitstream is finalized, rather than years.

While VP10 has been completely replaced by the AV1 codec, the same does not seem to hold for Thor and Daala. Both projects still see respective development, which does not seem limited to the features that will eventually be incorporated into AV1.

And that concludes it for now. Our next post will be by Carina, who will show how to create an access control system in PHP with our triggers.

read moreless
Latest 2017 2016 2015 2014 2013 2012