Changeset 1484


Ignore:
Timestamp:
12/09/10 09:31:59 (2 years ago)
Author:
jeroen
Message:

moved AAC/H264 parsing to separate files.

Location:
providers/adaptive
Files:
4 added
1 deleted
6 edited
1 moved

Legend:

Unmodified
Added
Removed
  • providers/adaptive/doc/index.rst

    r1449 r1484  
    1 HTTP Live Streaming provider 
    2 ============================ 
     1Adaptive provider 
     2================= 
    33 
    4 This custom media provider leverages the HTTP Live Streaming protocol: 
     4This custom media provider intends to add suppport to: 
    55 
    6 * Parsing of M3U8 manifests 
    7 * Demuxing of MPPEG-TS streams 
     6* Apple HTTP Live Streaming (by parsing M3U8 files and transmuxing MPEG TS files) 
     7* Microsoft Smooth Streaming (by parsing ISMC files and transmuxing ISMV files) 
     8 
     9Flash player 10.1 is required for the transmuxed data to play back. 
     10 
     11Only Smooth streams using H264 video can be supported. VC-1 streams cannot be decoded in Flash. 
     12 
     13http://learn.iis.net/page.aspx/792/adaptive-streaming-comparison/ 
     14 
     15 
     16References 
     17========== 
     18 
     19MPEG TS 
     20------- 
     21 
     22TS, PAT, PMT and PES: 
     23 
     24http://en.wikipedia.org/wiki/MPEG_transport_stream 
     25 
     26 
     27 
     28H264 
     29---- 
     30 
     31H264, NAL, SPS/PPS and avcC: 
     32 
     33http://en.wikipedia.org/wiki/H.264/MPEG-4_AVC 
     34http://en.wikipedia.org/wiki/Network_Abstraction_Layer 
     35http://www.javvin.com/protocolH264.html 
     36http://wiki.multimedia.cx/index.php?title=H.264 
     37 
     38AAC 
     39--- 
     40 
     41AAC, ADTS and ADIF: 
     42 
     43http://en.wikipedia.org/wiki/Advanced_Audio_Coding 
     44http://wiki.multimedia.cx/index.php?title=ADTS 
     45http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio 
     46 
     47 
     48M3U8 
     49---- 
     50 
     51M3U8 manifests: 
     52 
     53http://tools.ietf.org/html/draft-pantos-http-live-streaming-04 
    854 
    955 
    1056 
    1157 
    12 MPEG-TS Parsing Tree 
    13 -------------------- 
     58ISMV 
     59---- 
    1460 
    15 Excerpt from the draft: 
     61ISMC manifests and ISMV MP4 fragments: 
    1662 
    17    Each media file URI in a Playlist file MUST identify a media file 
    18    which is a segment of the overall presentation.  Each media file MUST 
    19    be formatted as an MPEG-2 Transport Stream or an MPEG-2 audio 
    20    elementary stream [ISO_13818]. 
    21  
    22    Transport Stream files MUST contain a single MPEG-2 Program.  There 
    23    SHOULD be a Program Association Table and a Program Map Table at the 
    24    start of each file.  A file that contains video SHOULD have at least 
    25    one key frame and enough information to completely initialize a video 
    26    decoder. 
     63http://wiki.multimedia.cx/index.php?title=MP4 
     64http://learn.iis.net/page.aspx/626/smooth-streaming-technical-overview/ 
    2765 
    2866 
    29 Format specs 
    30 ------------ 
     67FLV 
     68--- 
    3169 
    32 http://en.wikipedia.org/wiki/MPEG_transport_stream 
    33  
    34 http://tools.ietf.org/html/draft-pantos-http-live-streaming-04 
     70FLV files, FLV tags and loading into actionscript: 
    3571 
    3672http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf 
    37  
    38 http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/flash/net/NetStream.html 
  • providers/adaptive/src/com/longtailvideo/jwplayer/media/AdaptiveProvider.as

    r1476 r1484  
    22 
    33 
    4         import com.longtailvideo.jwplayer.demux.*; 
    54        import com.longtailvideo.jwplayer.events.MediaEvent; 
    65        import com.longtailvideo.jwplayer.media.MediaProvider; 
     
    87        import com.longtailvideo.jwplayer.model.PlaylistItem; 
    98        import com.longtailvideo.jwplayer.player.PlayerState; 
     9        import com.longtailvideo.jwplayer.muxing.*; 
    1010        import com.longtailvideo.jwplayer.utils.*; 
    1111 
  • providers/adaptive/src/com/longtailvideo/jwplayer/muxing/FLV.as

    r1476 r1484  
    1 package com.longtailvideo.jwplayer.demux { 
     1package com.longtailvideo.jwplayer.muxing { 
    22 
    33 
    44        import com.longtailvideo.jwplayer.utils.Logger; 
    5         import com.longtailvideo.jwplayer.demux.*; 
     5        import com.longtailvideo.jwplayer.muxing.*; 
    66        import flash.utils.ByteArray; 
    77 
     
    2525                        var flv:ByteArray = new ByteArray(); 
    2626                        _writeHeader(flv); 
    27                         // Sort the tags by timestamp. 
     27                        // Sort the tags by timestamp; private data first. 
    2828                        _tags.sortOn( 
    29                                 ['isPrivate','timestamp','isAudio'], 
    30                                 [Array.NUMERIC | Array.DESCENDING, Array.NUMERIC, Array.NUMERIC] 
     29                                ['timestamp','isPrivate'], 
     30                                [Array.NUMERIC, Array.NUMERIC | Array.DESCENDING] 
    3131                        ); 
    32                         // var stt:Number = _tags[0].timestamp; 
     32                        var stt:Number = _tags[0].timestamp; 
    3333                        // Append all tags 
    3434                        var pos:Number = flv.position; 
    3535                        for(var i:Number=0; i<_tags.length; i++) { 
    3636                                // Set the timestamps zero-indexed. 
    37                                 // _tags[i].timestamp -= stt; 
     37                                _tags[i].timestamp -= stt; 
    3838                                // The Tag itself. 
    3939                                flv.writeBytes(_tags[i].data); 
     
    4949 
    5050                /** Push an FLV tag into the FLV file. **/ 
    51                 public function push(tag:Tag):void { 
     51                public function push(tag:FLVTag):void { 
    5252                        _tags.push(tag); 
    5353                }; 
     
    5656                /** Write end-of-sequence FLV tag. **/ 
    5757                private function _writeEOSTag(tag:ByteArray):void {  
    58                         var tsp:Number = _tags[_tags.length-1].timestamp; 
    59                         tag.writeByte(9); 
    6058                        // Size of the tag in bytes after StreamID. 
    61                         tag.writeByte(5 >> 16); 
    62                         tag.writeByte(5 >> 8); 
    63                         tag.writeByte(5); 
     59                        tag.writeUnsignedInt(0x09000005); 
    6460                        // Timestamp (lower 24 plus upper 8) 
     61                        var tsp:Number = _tags[_tags.length-1].timestamp + 1; 
    6562                        tag.writeByte(tsp >> 16); 
    6663                        tag.writeByte(tsp >> 8); 
    6764                        tag.writeByte(tsp); 
    6865                        tag.writeByte(tsp >> 24); 
    69                         // StreamID (3 empty bytes) 
    70                         tag.writeByte(0); 
    71                         tag.writeByte(0); 
    72                         tag.writeByte(0); 
    73                         // Keyframe + AVC 
    74                         tag.writeByte(0x17); 
    75                         //Sequence end 
    76                         tag.writeByte(0x02); 
    77                         // Composition time. 
    78                         tag.writeByte(0); 
    79                         tag.writeByte(0); 
    80                         tag.writeByte(0); 
     66                        // StreamID (3 empty bytes), Keyframe, AVC. 
     67                        tag.writeUnsignedInt(0x00000017); 
     68                        //Sequence end, composition time 
     69                        tag.writeUnsignedInt(0x02000000); 
    8170                        // PreviousTagSize 
    82                         tag.writeUnsignedInt(16); 
     71                        tag.writeUnsignedInt(0x00000016); 
    8372                }; 
    8473 
  • providers/adaptive/src/com/longtailvideo/jwplayer/muxing/PES.as

    r1476 r1484  
    1 package com.longtailvideo.jwplayer.demux { 
     1package com.longtailvideo.jwplayer.muxing { 
    22 
    33 
    4         import com.longtailvideo.jwplayer.demux.*; 
     4        import com.longtailvideo.jwplayer.muxing.*; 
    55        import com.longtailvideo.jwplayer.utils.Logger; 
    66        import flash.utils.ByteArray; 
     
    4343                        _flv = flv; 
    4444                        _data.position = 0; 
    45                         _parseHeader(); 
    46                 }; 
    47  
    48  
    49                 /** Parse the ADTS header and store FLV tag. **/ 
    50                 private function _parseADTS():void { 
    51                         var dat:ByteArray = new ByteArray(); 
    52                         // Syncword, ID, Layer and PA should be 0xFFF1. 
    53                         if(_data.readUnsignedShort() == 65521) { 
    54                                 // ADIF zero index is null; ADTS not. 
    55                                 var profile:uint = (_data.readByte() >> 6) + 1; 
    56                                 _data.position--; 
    57                                 var samplerate:uint = (_data.readByte() & 0x3C) >> 2; 
    58                                 _data.position--; 
    59                                 var channels:uint = (_data.readShort() & 0x01C0) >> 6; 
    60                                 _data.position -= 4; 
    61                                 // 5 bits profile + 4 bits samplerate + 4 bits channels. 
    62                                 dat.writeByte((profile << 3) + (samplerate >> 1)); 
    63                                 dat.writeByte((samplerate << 7) + (channels << 3)); 
    64                                 _flv.push(new Tag(dat,Math.round(_pts/90), true, true)); 
    65                         } else { 
    66                                 throw new Error("ADTS data not found in PES."); 
    67                         } 
    68                 }; 
    69  
    70  
    71                 /** Filter out ADTS headers and store FLV tag. **/ 
    72                 private function _parseAudio():void { 
    73                         var pos:Number = 0; 
    74                         var dat:ByteArray; 
    75                         while(_data.bytesAvailable > 1) { 
    76                                 // ADTS header: Syncword, ID, Layer and PA should be 0xFFF1. 
    77                                 if(_data.readUnsignedShort() == 65521) { 
    78                                         // Write raw AAC preceding this header into FLVTag. 
    79                                         if(pos) { 
    80                                                 dat = new ByteArray(); 
    81                                                 dat.writeBytes(_data, pos , _data.position - pos - 1); 
    82                                                 // Saving raw AAC in one tag by just slicing ADTS caused speed-ups. 
    83                                                 _flv.push(new Tag(dat, Math.round(_pts/90), true)); 
    84                                         } 
    85                                         // ADTS header is 7 bytes. 
    86                                         _data.position += 5; 
    87                                         pos = _data.position; 
    88                                 } else {  
    89                                         _data.position--; 
    90                                 } 
    91                         } 
    92                         // Write raw AAC after last header. 
    93                         dat = new ByteArray(); 
    94                         dat.writeBytes(_data, pos); 
    95                         _flv.push(new Tag(dat, Math.round(_pts/90), true)); 
    96                 }; 
    97  
    98  
    99                 /** Parse the header of a PES. **/ 
    100                 private function _parseHeader():void { 
    10145                        // Start code prefix and packet ID. 
    10246                        if((_isAudio && _data.readUnsignedInt() != 448) ||  
     
    13074                        _data.position += len; 
    13175                        if(_isAudio) { 
    132                                 if(_isFirst) { 
    133                                         _parseADTS(); 
    134                                 } 
    13576                                _parseAudio(); 
    13677                        } else { 
     
    14081 
    14182 
    142                 /** Step through AVC to extract NAL units. **/ 
    143                 private function _parseVideo():void { 
    144                         var units:Array = new Array(); 
    145                         var window:uint = 0; 
    146                         var started:Number = 0; 
    147  
    148                         // Loop through data to find NAL startcode. 
    149                         while(_data.bytesAvailable > 3) { 
    150                                 window = _data.readUnsignedInt(); 
    151                                 // Find startcodes 
    152                                 if((window & 0xFFFFFF00) == 0x100) { 
    153                                         _data.position -= 5; 
    154                                         var four:uint = _data.readByte(); 
    155                                         _data.position += 3; 
    156                                         var head:uint = _data.readByte(); 
    157                                         // Now we know the length of the previous NAL 
    158                                         if(started) { 
    159                                                 var size:Number = _data.position - started - 4; 
    160                                                 if(!four) { size--; } 
    161                                                 units.push(new ByteArray()); 
    162                                                 units[units.length-1].writeBytes(_data,started,size); 
    163                                                 units[units.length-1].position = 0; 
    164                                         } 
    165                                         started = _data.position - 1; 
    166                                 } 
    167                                 _data.position -= 3; 
    168                         } 
    169  
    170                         // Save the last NAL to the array. 
    171                         if(started) { 
    172                                 units.push(new ByteArray()); 
    173                                 units[units.length-1].writeBytes(_data, started); 
    174                                 units[units.length-1].position = 0; 
    175                         } else { 
    176                                 return; 
    177                                 throw new Error('No NAL units found.'); 
    178                         } 
    179  
    180                         // Separate the SPS/PPS and add size headers to other NALs 
    181                         var nalu:ByteArray = new ByteArray(); 
    182                         var sps:ByteArray = new ByteArray(); 
    183                         var pps:ByteArray = new ByteArray(); 
    184                         for(var i:Number = 0; i < units.length; i++) { 
    185                                 units[i].position = 0; 
    186                                 var type:Number = units[i].readByte() & 0x1F; 
    187                                 if(type == 7) {  
    188                                         sps.writeBytes(units[i],0); 
    189                                 } else if (type == 8) { 
    190                                         pps.writeBytes(units[i],0); 
    191                                 } else { 
    192                                         // Instead of a startcode, NAL units need a 4-byte length prefix. 
    193                                         nalu.writeUnsignedInt(units[i].length); 
    194                                         nalu.writeBytes(units[i],0); 
    195                                 } 
    196                         } 
    197  
    198                         // Finally, write the tags 
     83                /** Parse ADTS streams for audio tags. **/ 
     84                private function _parseAudio():void { 
     85                        // Get ADIF header if this is the first audio stream. 
    19986                        if(_isFirst) { 
    200                                 if(sps.length && pps.length) {  
    201                                         _parseSpsPps(sps,pps); 
    202                                 } 
    203                                 _flv.push(new Tag( 
    204                                         nalu, 
    205                                         Math.round(_pts/90),  
    206                                         false,  
    207                                         false,  
     87                                _flv.push(new FLVTag( 
     88                                        AAC.getADIF(_data), 
     89                                        Math.round(_pts/90), 
     90                                        true,  
    20891                                        true 
    20992                                )); 
    210                         } else {  
    211                                 _flv.push(new Tag( 
    212                                         nalu, 
    213                                         Math.round(_pts/90),  
    214                                         false,  
    215                                         false,  
    216                                         false 
    217                                 )); 
     93                        } 
     94                        // Get AAC frames. 
     95                        var samplerate:uint = AAC.getSamplerate(_data); 
     96                        var frames:Array = AAC.getFrames(_data); 
     97                        for(var i:Number = 0; i<frames.length; i++) { 
     98                                // Increment the timestamp of subsequent frames. 
     99                                var tsp:uint = Math.round(_pts/90 + i*1024*1000/samplerate); 
     100                                _flv.push(new FLVTag(frames[i], Math.round(_pts/90), true)); 
    218101                        } 
    219102                }; 
    220103 
    221104 
    222                 /** Parse the SPS and PPS data into avcC. **/ 
    223                 private function _parseSpsPps(sps:ByteArray,pps:ByteArray):void { 
    224                         var dat:ByteArray = new ByteArray(); 
    225                         // avcC version. 
    226                         dat.writeByte(0x01); 
    227                         // profile, compatibility and level. 
    228                         dat.writeBytes(sps, 1, 3); 
    229                         // 111111 + 2 bit NAL size - 11 
    230                         dat.writeByte(0xFF); 
    231                         // Number of SPS. 
    232                         dat.writeByte(0xE1); 
    233                         // SPS bytesize (UI16) 
    234                         dat.writeByte(sps.length >> 8); 
    235                         dat.writeByte(sps.length); 
    236                         // SPS data block. 
    237                         dat.writeBytes(sps,0); 
    238                         // Number of PPS 
    239                         dat.writeByte(0x01); 
    240                         // PPS bytesize 
    241                         dat.writeByte(pps.length >> 8); 
    242                         dat.writeByte(pps.length); 
    243                         // PPS data block. 
    244                         dat.writeBytes(pps,0); 
    245                         _flv.push(new Tag( 
    246                                 dat, 
    247                                 Math.round(_pts/90),  
    248                                 false,  
    249                                 true, 
    250                                 true 
     105                /** Step through AVC to extract NAL units. **/ 
     106                private function _parseVideo():void { 
     107                        // Get AVCC header if this is the first video stream. 
     108                        if(_isFirst) { 
     109                                _flv.push(new FLVTag( 
     110                                        H264.getAVCC(_data), 
     111                                        Math.round(_pts/90), 
     112                                        false, 
     113                                        true, 
     114                                        true, 
     115                                        Math.round((_pts-_dts)/90) 
     116                                )); 
     117                        } 
     118                        // Get raw NALU data. 
     119                        var nalu:ByteArray = new ByteArray(); 
     120                        var units:Array = H264.getNALU(_data); 
     121                        var unit_type:Number; 
     122                        var is_keyframe:Boolean; 
     123                        // Only push NAL units 1 to 6 into stream (5 is keyframe). 
     124                        for(var i:Number = 0; i < units.length; i++) { 
     125                                unit_type = units[i].readByte() & 0x1F; 
     126                                if (unit_type < 6) { 
     127                                        // NAL's are separated by a 4 byte NAL length header. 
     128                                        nalu.writeUnsignedInt(units[i].length); 
     129                                        nalu.writeBytes(units[i]); 
     130                                        if(unit_type == 5) { 
     131                                                is_keyframe = true; 
     132                                        } 
     133                                } 
     134                        } 
     135                        _flv.push(new FLVTag( 
     136                                nalu, 
     137                                Math.round(_pts/90), 
     138                                false, 
     139                                false, 
     140                                is_keyframe, 
     141                                Math.round((_pts-_dts)/90) 
    251142                        )); 
    252143                }; 
    253  
    254144 
    255145 
  • providers/adaptive/src/com/longtailvideo/jwplayer/muxing/TS.as

    r1476 r1484  
    1 package com.longtailvideo.jwplayer.demux { 
     1package com.longtailvideo.jwplayer.muxing { 
    22 
    33 
    44        import com.longtailvideo.jwplayer.utils.Logger; 
    5         import com.longtailvideo.jwplayer.demux.*; 
     5        import com.longtailvideo.jwplayer.muxing.*; 
    66        import flash.utils.ByteArray; 
    77 
     
    99        /** Representation of an MPEG transport stream. **/ 
    1010        public class TS { 
     11 
     12 
     13                /** TS Sync byte. **/ 
     14                public static const SYNC_BYTE:uint = 0x47; 
     15 
     16 
     17                /** TS Packet size in byte. **/ 
     18                public static const PACKET_SIZE:uint = 188; 
    1119 
    1220 
     
    5058                private function _readPacket(dat:ByteArray):void { 
    5159                        // Each packet is 188 bytes. 
    52                         var todo:uint = 188; 
     60                        var todo:uint = TS.PACKET_SIZE; 
    5361                        // Sync byte. 
    54                         if(dat.readByte() != 0x47) { 
     62                        if(dat.readByte() != TS.SYNC_BYTE) { 
    5563                                throw new Error("Could not parse TS file: sync byte not found."); 
    5664                        } 
     
    6573                        var atf:uint = (dat.readByte() & 48) >> 4; 
    6674                        todo --; 
    67  
    6875                        // Read adaptation field if available. 
    6976                        if(atf > 1) { 
     
    7683                                todo -= len; 
    7784                                // Return if there's only adaptation field. 
    78                                 if(atf == 2) {  
     85                                if(atf == 2) { 
    7986                                        dat.position += todo; 
    80                                         return;  
    81                                 }  
     87                                        return; 
     88                                } 
    8289                        } 
    8390 
     
    111118                                                if(_lastAvc == -1) { 
    112119                                                        _pesArray.push(new PES(pes,false,true)); 
    113                                                 } else {  
    114                                                         _pesArray.push(new PES(pes,false,Boolean(rai))); 
     120                                                } else { 
     121                                                        _pesArray.push(new PES(pes,false)); 
    115122                                                } 
    116123                                                _lastAvc = _pesArray.length-1; 
     
    123130                                        break; 
    124131                        } 
    125  
    126132                        // Jump to the next packet. 
    127133                        dat.position += todo; 
     
    143149 
    144150 
    145                 /** Read the Program Map Table. **/  
    146                 private function _readPMT(dat:ByteArray):Number {  
     151                /** Read the Program Map Table. **/ 
     152                private function _readPMT(dat:ByteArray):Number { 
    147153                        // Check the section length for a single PMT. 
    148154                        dat.position += 3; 
    149                         var len:uint = dat.readUnsignedByte(); 
     155                        var len:uint = dat.readByte(); 
    150156                        dat.position += 9; 
    151157                        // Loop through the streams in the PMT. 
    152                         for(var i:Number = 0; i < (len - 13) / 5; i++) { 
    153                                 var typ:uint = dat.readUnsignedByte(); 
    154                                 if(typ == 0x0F) {  
     158                        for(var i:Number=0; i<2; i++) { 
     159                                var typ:uint = dat.readByte(); 
     160                                if(typ == 0x0F) { 
    155161                                        _aacId = dat.readUnsignedShort() & 8191; 
    156                                 } else if (typ == 0x1B) {  
     162                                } else if (typ == 0x1B) { 
    157163                                        _avcId = dat.readUnsignedShort() & 8191; 
    158164                                } else { 
    159165                                        throw new Error("Only AAC audio and AVC video are supported."); 
    160166                                } 
    161                                 dat.position += 2; 
     167                                dat.position ++; 
     168                                // Possible section length. 
     169                                var sel:uint = dat.readByte(); 
     170                                dat.position += sel; 
    162171                        } 
    163172                        return len; 
Note: See TracChangeset for help on using the changeset viewer.