source: providers/adaptive/src/com/longtailvideo/jwplayer/demux/PES.as @ 1456

Revision 1456, 7.6 KB checked in by jeroen, 3 years ago (diff)

added NALU length start bytes

Line 
1package com.longtailvideo.jwplayer.demux {
2
3
4        import com.longtailvideo.jwplayer.utils.Logger;
5
6        import flash.utils.ByteArray;
7
8
9        /** Translate a single Packetized Elementary Stream into FLV Tags. **/
10        public class PES {
11
12
13                /** The PES data (including headers). **/
14                private var _data:ByteArray;
15                /** Timestamp from the PTS header. **/
16                private var _timestamp:Number;
17                /** Is it AAC or AVC. **/
18                private var _audio:Boolean;
19                /** Is there a keyframe. **/
20                private var _keyframe:Boolean;
21                /** ByteArray with FLV tags for this stream. **/
22                private var _tags:ByteArray;
23                /** Composition time for video tag. **/
24                private var _compositionTime:Number;
25
26
27                /** Save the first chunk of data. **/
28                public function PES(data:ByteArray,audio:Boolean,keyframe:Boolean=false) {
29                        _data = data;
30                        _audio = audio;
31                        _keyframe = keyframe;
32                        _tags = new ByteArray();
33                };
34
35
36                /** Append data to the AVC stream. **/
37                public function append(data:ByteArray):void {
38                        _data.writeBytes(data);
39                };
40
41
42                /** Return the FLV tags. **/
43                public function get tags():ByteArray {
44                        return _tags;
45                }
46
47
48                /** Parse the Elementary Stream. **/
49                public function parse(): void {
50                        // Start code prefix and packet ID.
51                        _data.position = 0;
52                        if((_audio && _data.readUnsignedInt() != 448) ||
53                                (!_audio && _data.readUnsignedInt() != 480)) {
54                                throw new Error("PES start code not found or not AAC/AVC");
55                        }
56                        // Ignore packet length and marker bits.
57                        _data.position += 3;
58                        // Check for PTS
59                        var flags:uint = (_data.readUnsignedByte() & 192) >> 6;
60                        if((_audio && flags != 2) || (!_audio && flags != 3)) {
61                                throw new Error("No PTS/DTS in this PES packet");
62                        }
63                        // Check PES header length
64                        var length:uint = _data.readUnsignedByte();
65                        // Grab the timestamp from PTS data (spread out over 5 bytes):
66                        // XXXX---X -------- -------X -------- -------X
67                        var pts:Number = ((_data.readUnsignedByte() & 14) << 29) +
68                                ((_data.readUnsignedShort() & 65534) << 14) +
69                                ((_data.readUnsignedShort() & 65534) >> 1);
70                        // Convert 90kHz clock to milliseconds.
71                        _timestamp = Math.round(pts/90);
72                        length -= 5;
73                        if(!_audio) {
74                                // Grab the DTS and calculate compositionTime (PTS-DTS)
75                                var dts:Number = ((_data.readUnsignedByte() & 14) << 29) +
76                                        ((_data.readUnsignedShort() & 65534) << 14) +
77                                        ((_data.readUnsignedShort() & 65534) >> 1);
78                                _compositionTime = _timestamp - Math.round(dts/90);
79                                length -= 5;
80                        }
81                        // Skip other header data.
82                        _data.position += length;
83                        if(_audio) {
84                                parseAac();
85                        } else {
86                                parseAvc();
87                        }
88                };
89
90
91                /** Step through AAC to extract ADTS. **/
92                private function parseAac():void {
93                        var start:Number = 0;
94                        var rawstart:Number = 0;
95                        while(_data.bytesAvailable > 1) {
96                                // ADTS header: Syncword, ID, Layer and PA should be 0xFFF1.
97                                if(_data.readUnsignedShort() == 65521) {
98                                        // Inject raw AAC preceding this header.
99                                        if(rawstart) {
100                                                writeAudioTag(rawstart,_data.position-rawstart-2,false);
101                                        }
102                                        // Only write first ADTS header of file (7 bytes). Ignore the other.
103                                        if(!rawstart && _keyframe) {
104                                                writeAudioTag(_data.position-2,7,true);
105                                        }
106                                        rawstart = _data.position + 5;
107                                }
108                                _data.position--;
109                        }
110                        // Raw AAC after last ADTS header.
111                        writeAudioTag(rawstart,_data.length-rawstart,false);
112                };
113
114
115                /** Step through AVC to extract NAL units. **/
116                private function parseAvc():void {
117                        var units:Array = new Array();
118                        var window:uint = 0;
119                        var started:Number = 0;
120
121                        // Loop through data to find NAL startcode.
122                        while(_data.bytesAvailable > 3) {
123                                window = _data.readUnsignedInt();
124                                // Find startcodes
125                                if((window & 0xFFFFFF00) == 0x100) {
126                                        _data.position -= 5;
127                                        var four:uint = _data.readByte();
128                                        _data.position += 3;
129                                        var head:uint = _data.readByte();
130                                        // Now we know the length of the previous NAL
131                                        if(started) {
132                                                var size:Number = _data.position - started - 4;
133                                                if(!four) { size--; }
134                                                units.push(new ByteArray());
135                                                units[units.length-1].writeBytes(_data,started,size);
136                                                units[units.length-1].position = 0;
137                                        }
138                                        started = _data.position - 1;
139                                }
140                                _data.position -= 3;
141                        }
142
143                        // Save the last NAL to the array.
144                        if(started) {
145                                units.push(new ByteArray());
146                                units[units.length-1].writeBytes(_data, started);
147                                units[units.length-1].position = 0;
148                        } else {
149                                return;
150                                throw new Error('No NAL units found.');
151                        }
152
153                        // Separate the SPS/PPS and add size headers to other NALs
154                        var nalu:ByteArray = new ByteArray();
155                        var sps:ByteArray = new ByteArray();
156                        var pps:ByteArray = new ByteArray();
157                        for(var i:Number = 0; i < units.length; i++) {
158                                units[i].position = 0;
159                                var type:Number = units[i].readByte() & 0x1F;
160                                if(type == 7) {
161                                        sps.writeBytes(units[i],0);
162                                } else if (type == 8) {
163                                        pps.writeBytes(units[i],0);
164                                } else {
165                                        // Instead of a startcode, NAL units need a 4-byte length prefix.
166                                        nalu.writeUnsignedInt(units[i].length);
167                                        nalu.writeBytes(units[i],0);
168                                }
169                        }
170
171                        // Finally, write the tags
172                        if(sps.length && pps.length) {
173                                writeAvccTag(sps,pps);
174                        }
175                        writeVideoTag(nalu);
176                };
177
178
179                /** Write an FLV tag with audio data. **/
180                private function writeAudioTag(offset:uint,length:uint,adts:Boolean=false):void {
181                        // Header
182                        writeTagHeader(8,length + 2);
183
184                        // SoundFormat, -Rate, -Size, Type.
185                        _tags.writeByte(0xAF);
186                        // ADTS or raw AAC.
187                        if(adts) {
188                                _tags.writeByte(0x00);
189                        } else {
190                                _tags.writeByte(0x01);
191                        }
192                        // Write actual data.
193                        _tags.writeBytes(_data,offset,length);
194                        // PreviousTagSize
195                        _tags.writeUnsignedInt(length + 13);
196                };
197
198
199                /** Write an FLV tag with SPS/PPS data parsed into avcC. **/
200                private function writeAvccTag(sps:ByteArray,pps:ByteArray):void {
201                        // Header
202                        writeTagHeader(9,sps.length + pps.length + 19);
203
204                        // Keyframe + AVC
205                        _tags.writeByte(0x17);
206                        // CompositionTime
207                        _tags.writeByte(0);
208                        _tags.writeByte(0);
209                        _tags.writeByte(0);
210                        // Sequence header
211                        _tags.writeUnsignedInt(0x00);
212
213                        // avcC version.
214                        _tags.writeByte(0x01);
215                        // profile, compatibility and level.
216                        _tags.writeBytes(sps, 1, 3);
217                        // 111111 + 2 bit NAL size - 11
218                        _tags.writeByte(0xFF);
219                        // Number of SPS.
220                        _tags.writeByte(0xE1);
221                        // SPS bytesize (UI16)
222                        _tags.writeByte(sps.length >> 8);
223                        _tags.writeByte(sps.length);
224                        // SPS data block.
225                        _tags.writeBytes(sps,0);
226                        // Number of PPS
227                        _tags.writeByte(0x01);
228                        // PPS bytesize
229                        _tags.writeByte(pps.length >> 8);
230                        _tags.writeByte(pps.length);
231                        // PPS data block.
232                        _tags.writeBytes(pps,0);
233
234                        // PreviousTagSize
235                        _tags.writeUnsignedInt(sps.length + pps.length + 30);
236                };
237
238
239                /** Write an FLV tag with video data. **/
240                private function writeVideoTag(nalu:ByteArray):void {
241                        // Header
242                        writeTagHeader(9,nalu.length + 5);
243
244                        // Keyframe or interframe + AVC
245                        if(_keyframe) {
246                                _tags.writeByte(0x17);
247                        } else {
248                                _tags.writeByte(0x27);
249                        }
250                        // AVC NALU
251                        _tags.writeByte(0x01);
252                        // CompositionTime
253                        _tags.writeByte(_compositionTime >> 16);
254                        _tags.writeByte(_compositionTime >> 8);
255                        _tags.writeByte(_compositionTime);
256                        // Write actual data.
257                        _tags.writeBytes(nalu,0);
258                        // Write PreviousTagSize
259                        _tags.writeUnsignedInt(nalu.length + 16);
260                };
261
262
263                /** Write the tag header. **/
264                private function writeTagHeader(type:uint,length:uint):void {
265                        _tags.writeByte(type);
266                        // Size of the tag in bytes after StreamID.
267                        _tags.writeByte(length >> 16);
268                        _tags.writeByte(length >> 8);
269                        _tags.writeByte(length);
270                        // Timestamp (lower 24 plus upper 8)
271                        _tags.writeByte(_timestamp >> 16);
272                        _tags.writeByte(_timestamp >> 8);
273                        _tags.writeByte(_timestamp);
274                        _tags.writeByte(_timestamp >> 24);
275                        // StreamID (3 empty bytes)
276                        _tags.writeByte(0);
277                        _tags.writeByte(0);
278                        _tags.writeByte(0);
279                };
280
281        }
282
283
284}
Note: See TracBrowser for help on using the repository browser.