Modifying MP4 segments Bitmovin Player Client-Side


You might notice your Stream plays on 80% of devices but fails on 20% of them. i.e typical 80-20 rule. Particularly on Live Streams.

In Bitmovin we’ve seen older devices making the bulk of this 20% devices.

Fixing the Stream

After investigating, if the issue’s with the in-stream mp4 boxes you’re likely to update your encoder/packager. This is COSTLY as regression testing is needed and RISKS breaking a device in your 80%.

At Bitmovin, you’ve ANOTHER alternative. CLIENT-SIDE FIXING of the mp4 box in the client!

A Real-Life Usecase


  1. Customer’s stream does not have PSSH boxes due to legacy reason
  2. Tizen 2016 Tv MUST have PSSH box in init segment for DRM decoding


Bitmovin + codem-isoboxer in THREE steps :slight_smile:

  1. Include the codem-isoboxer js script in your HTML file.
    <script type="text/javascript"
  1. Update Bitmovin Player network configuration to preprocess media segments before passing it to decoder.
        var conf = {
            key: 'YOUR-PLAYER-KEY',
            playback: {
                muted: true,
                autoplay: true,
            network: {
                preprocessHttpResponse: function (type, response) {
                    if (type.includes('media')) {
                        response.body = prepareSegment(response.body);
                    return Promise.resolve(response);
  1. Code to modify MP4 box in the segment. In this case add the PSSH box in the init segment
        function prepareSegment(segmentData) {
            var isoFile = ISOBoxer.parseBuffer(segmentData);

           /****** An init segment MUST contain `ftyp` and `moov` box *****/
            var ftyp = isoFile.fetch('ftyp');
            if (!ftyp) {
                return segmentData;

           var moov = isoFile.fetch('moov');
           if(!moov) {
               return segmentData;

            console.log('---------edited moov box---------');

            return isoFile.write();

        function appendPsshBox(parent) {
            var psshWidevine = "--YOUR--BASE64--WIDEVINE--PSSH--VALUE--FROM--MANIFEST--";
            var psshPlayready = "--YOUR--BASE64--PLAYREADY--PSSH--VALUE--FROM--MANIFEST--"

            /******************* Widevine ***************************/
            let wvPsshBox = ISOBoxer.createFullBox('pssh');
            // Widevine PSSH
            wvPsshBox['SystemID'] = [0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed];

            // PSSH in binary form
            wvPsshBox['Data'] = new Uint8Array(atob(psshWidevine).split("").map(function (c) {
                return c.charCodeAt(0);
            wvPsshBox['DataSize'] = wvPsshBox['Data'].length;

            ISOBoxer.Utils.appendBox(parent, wvPsshBox, 0);

            /******************* Playready ***************************/
            let prPsshBox = ISOBoxer.createFullBox('pssh');
            // Widevine PSSH
            prPsshBox['SystemID'] = [0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95];

            // PSSH in binary form
            prPsshBox['Data'] = new Uint8Array(atob(psshPlayready).split("").map(function (c) {
                return c.charCodeAt(0);
            prPsshBox['DataSize'] = prPsshBox['Data'].length;

            ISOBoxer.Utils.appendBox(parent, prPsshBox, 0);