@gkatsev ensure segments without an initial IDR are not displayed in 4:3 initially.
Closes #272. Squashed commit of the following: commit de597c3ee10075cece7036aeaacedb74f33d9ef5 Author: David LaPalomento <dlapalomento@gmail.com> Date: Fri May 22 14:02:27 2015 -0400 Remove vim swap file Ignore them in version control in the future. commit fb189ba16330311371bb6b9cf1bb6248005e10b7 Author: Gary Katsevman <git@gkatsev.com> Date: Wed May 20 18:30:07 2015 -0400 Default 'nextFrameKeyFrame' to false. commit 3788ad0607fabb2cde1a1d5eea4163ceafb5fab5 Author: Gary Katsevman <git@gkatsev.com> Date: Wed May 20 18:24:16 2015 -0400 setNextFrameKeyFrame commit 6944234afd99285a448a9cf3adbcaf1eb26cfe33 Author: Gary Katsevman <git@gkatsev.com> Date: Wed May 20 18:06:26 2015 -0400 Another camelcase usage commit da6e32a4d9024d859ca58bc1d4bc3fcbe1de7a33 Author: Gary Katsevman <git@gkatsev.com> Date: Wed May 20 18:05:30 2015 -0400 Dont parse unused fields. Use camel case names commit fb7990b6606501a549d7448de6eec38784695896 Author: Gary Katsevman <git@gkatsev.com> Date: Wed May 20 16:28:28 2015 -0400 ifs should have curly braces commit 92e40c642270e4ce4652a874073a66876312072f Author: Gary Katsevman <git@gkatsev.com> Date: Wed May 20 15:58:18 2015 -0400 Adaptation Field vars. Use Random Access Indicator The Random Access Indicator tells us whether something is a keyframe. Set the stream's frame's keyFrame property to true if the Random Access Indicator is set. commit 047a6d7771cd2d9c324b6da9bd76d4b6aeeadaf0 Author: Gary Katsevman <git@gkatsev.com> Date: Mon May 18 16:56:23 2015 -0400 Set up the test with exact conditions commit e2f8b18656d4cdb830cd23013fe7e5fcfa599ca4 Author: Gary Katsevman <git@gkatsev.com> Date: Mon May 18 16:56:01 2015 -0400 Restore stubbed out methods commit ff5f3b5fdb9dbe5e34447f8a4680c294163e0c46 Author: Gary Katsevman <git@gkatsev.com> Date: Mon May 18 16:50:22 2015 -0400 prototypeify h264-stream commit 7ee359d582550474a33730547742ce3d656dad52 Author: Gary Katsevman <git@gkatsev.com> Date: Mon May 18 16:28:08 2015 -0400 Initial test for metadata commit 0e3a961c7594d67280958cf21330fb347e1dda0a Author: Gary Katsevman <git@gkatsev.com> Date: Mon May 18 16:21:55 2015 -0400 Fix test properly commit 7e0de308227a4373c6abd71769ed474e59c7edeb Author: Gary Katsevman <git@gkatsev.com> Date: Mon May 18 16:14:57 2015 -0400 Move h264-extradata into separate file commit 66a676c234e6e087694611d710cad6414cbc984c Author: Gary Katsevman <git@gkatsev.com> Date: Mon May 18 15:52:08 2015 -0400 prototypeify H264ExtraData
Showing
11 changed files
with
510 additions
and
410 deletions
... | @@ -5,6 +5,7 @@ CHANGELOG | ... | @@ -5,6 +5,7 @@ CHANGELOG |
5 | * @dmlap use contribflow to manage contributions ([view](https://github.com/videojs/videojs-contrib-hls/pull/275)) | 5 | * @dmlap use contribflow to manage contributions ([view](https://github.com/videojs/videojs-contrib-hls/pull/275)) |
6 | * @dmlap add a contribflow configuration ([view](https://github.com/videojs/videojs-contrib-hls/pull/276)) | 6 | * @dmlap add a contribflow configuration ([view](https://github.com/videojs/videojs-contrib-hls/pull/276)) |
7 | * @ntadej Do not unnecessarily reset to the live point when refreshing playlists. Clean up playlist loader timeouts. ([view](https://github.com/videojs/videojs-contrib-hls/pull/274)) | 7 | * @ntadej Do not unnecessarily reset to the live point when refreshing playlists. Clean up playlist loader timeouts. ([view](https://github.com/videojs/videojs-contrib-hls/pull/274)) |
8 | * @gkatsev ensure segments without an initial IDR are not displayed in 4 | ||
8 | 9 | ||
9 | -------------------- | 10 | -------------------- |
10 | 11 | ... | ... |
... | @@ -27,6 +27,7 @@ module.exports = function(grunt) { | ... | @@ -27,6 +27,7 @@ module.exports = function(grunt) { |
27 | 'src/stream.js', | 27 | 'src/stream.js', |
28 | 'src/flv-tag.js', | 28 | 'src/flv-tag.js', |
29 | 'src/exp-golomb.js', | 29 | 'src/exp-golomb.js', |
30 | 'src/h264-extradata.js', | ||
30 | 'src/h264-stream.js', | 31 | 'src/h264-stream.js', |
31 | 'src/aac-stream.js', | 32 | 'src/aac-stream.js', |
32 | 'src/metadata-stream.js', | 33 | 'src/metadata-stream.js', | ... | ... |
... | @@ -20,6 +20,7 @@ | ... | @@ -20,6 +20,7 @@ |
20 | <script src="src/flv-tag.js"></script> | 20 | <script src="src/flv-tag.js"></script> |
21 | <script src="src/stream.js"></script> | 21 | <script src="src/stream.js"></script> |
22 | <script src="src/exp-golomb.js"></script> | 22 | <script src="src/exp-golomb.js"></script> |
23 | <script src="src/h264-extradata.js"></script> | ||
23 | <script src="src/h264-stream.js"></script> | 24 | <script src="src/h264-stream.js"></script> |
24 | <script src="src/aac-stream.js"></script> | 25 | <script src="src/aac-stream.js"></script> |
25 | <script src="src/metadata-stream.js"></script> | 26 | <script src="src/metadata-stream.js"></script> | ... | ... |
src/h264-extradata.js
0 → 100644
1 | (function() { | ||
2 | var | ||
3 | H264ExtraData, | ||
4 | ExpGolomb = window.videojs.Hls.ExpGolomb, | ||
5 | FlvTag = window.videojs.Hls.FlvTag; | ||
6 | |||
7 | window.videojs.Hls.H264ExtraData = H264ExtraData = function() { | ||
8 | this.sps = []; // :Array | ||
9 | this.pps = []; // :Array | ||
10 | }; | ||
11 | |||
12 | H264ExtraData.prototype.extraDataExists = function() { // :Boolean | ||
13 | return this.sps.length > 0; | ||
14 | }; | ||
15 | |||
16 | // (sizeOfScalingList:int, expGolomb:ExpGolomb):void | ||
17 | H264ExtraData.prototype.scaling_list = function(sizeOfScalingList, expGolomb) { | ||
18 | var | ||
19 | lastScale = 8, // :int | ||
20 | nextScale = 8, // :int | ||
21 | j, | ||
22 | delta_scale; // :int | ||
23 | |||
24 | for (j = 0; j < sizeOfScalingList; ++j) { | ||
25 | if (0 !== nextScale) { | ||
26 | delta_scale = expGolomb.readExpGolomb(); | ||
27 | nextScale = (lastScale + delta_scale + 256) % 256; | ||
28 | //useDefaultScalingMatrixFlag = ( j = = 0 && nextScale = = 0 ) | ||
29 | } | ||
30 | |||
31 | lastScale = (nextScale === 0) ? lastScale : nextScale; | ||
32 | // scalingList[ j ] = ( nextScale == 0 ) ? lastScale : nextScale; | ||
33 | // lastScale = scalingList[ j ] | ||
34 | } | ||
35 | }; | ||
36 | |||
37 | /** | ||
38 | * RBSP: raw bit-stream payload. The actual encoded video data. | ||
39 | * | ||
40 | * SPS: sequence parameter set. Part of the RBSP. Metadata to be applied | ||
41 | * to a complete video sequence, like width and height. | ||
42 | */ | ||
43 | H264ExtraData.prototype.getSps0Rbsp = function() { // :ByteArray | ||
44 | var | ||
45 | sps = this.sps[0], | ||
46 | offset = 1, | ||
47 | start = 1, | ||
48 | written = 0, | ||
49 | end = sps.byteLength - 2, | ||
50 | result = new Uint8Array(sps.byteLength); | ||
51 | |||
52 | // In order to prevent 0x0000 01 from being interpreted as a | ||
53 | // NAL start code, occurences of that byte sequence in the | ||
54 | // RBSP are escaped with an "emulation byte". That turns | ||
55 | // sequences of 0x0000 01 into 0x0000 0301. When interpreting | ||
56 | // a NAL payload, they must be filtered back out. | ||
57 | while (offset < end) { | ||
58 | if (sps[offset] === 0x00 && | ||
59 | sps[offset + 1] === 0x00 && | ||
60 | sps[offset + 2] === 0x03) { | ||
61 | result.set(sps.subarray(start, offset + 1), written); | ||
62 | written += offset + 1 - start; | ||
63 | start = offset + 3; | ||
64 | } | ||
65 | offset++; | ||
66 | } | ||
67 | result.set(sps.subarray(start), written); | ||
68 | return result.subarray(0, written + (sps.byteLength - start)); | ||
69 | }; | ||
70 | |||
71 | // (pts:uint):FlvTag | ||
72 | H264ExtraData.prototype.metaDataTag = function(pts) { | ||
73 | var | ||
74 | tag = new FlvTag(FlvTag.METADATA_TAG), // :FlvTag | ||
75 | expGolomb, // :ExpGolomb | ||
76 | profile_idc, // :int | ||
77 | chroma_format_idc, // :int | ||
78 | imax, // :int | ||
79 | i, // :int | ||
80 | |||
81 | pic_order_cnt_type, // :int | ||
82 | num_ref_frames_in_pic_order_cnt_cycle, // :uint | ||
83 | |||
84 | pic_width_in_mbs_minus1, // :int | ||
85 | pic_height_in_map_units_minus1, // :int | ||
86 | |||
87 | frame_mbs_only_flag, // :int | ||
88 | frame_cropping_flag, // :Boolean | ||
89 | |||
90 | frame_crop_left_offset = 0, // :int | ||
91 | frame_crop_right_offset = 0, // :int | ||
92 | frame_crop_top_offset = 0, // :int | ||
93 | frame_crop_bottom_offset = 0, // :int | ||
94 | |||
95 | width, | ||
96 | height; | ||
97 | |||
98 | tag.dts = pts; | ||
99 | tag.pts = pts; | ||
100 | expGolomb = new ExpGolomb(this.getSps0Rbsp()); | ||
101 | |||
102 | // :int = expGolomb.readUnsignedByte(); // profile_idc u(8) | ||
103 | profile_idc = expGolomb.readUnsignedByte(); | ||
104 | |||
105 | // constraint_set[0-5]_flag, u(1), reserved_zero_2bits u(2), level_idc u(8) | ||
106 | expGolomb.skipBits(16); | ||
107 | |||
108 | // seq_parameter_set_id | ||
109 | expGolomb.skipUnsignedExpGolomb(); | ||
110 | |||
111 | if (profile_idc === 100 || | ||
112 | profile_idc === 110 || | ||
113 | profile_idc === 122 || | ||
114 | profile_idc === 244 || | ||
115 | profile_idc === 44 || | ||
116 | profile_idc === 83 || | ||
117 | profile_idc === 86 || | ||
118 | profile_idc === 118 || | ||
119 | profile_idc === 128) { | ||
120 | chroma_format_idc = expGolomb.readUnsignedExpGolomb(); | ||
121 | if (3 === chroma_format_idc) { | ||
122 | expGolomb.skipBits(1); // separate_colour_plane_flag | ||
123 | } | ||
124 | expGolomb.skipUnsignedExpGolomb(); // bit_depth_luma_minus8 | ||
125 | expGolomb.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8 | ||
126 | expGolomb.skipBits(1); // qpprime_y_zero_transform_bypass_flag | ||
127 | if (expGolomb.readBoolean()) { // seq_scaling_matrix_present_flag | ||
128 | imax = (chroma_format_idc !== 3) ? 8 : 12; | ||
129 | for (i = 0 ; i < imax ; ++i) { | ||
130 | if (expGolomb.readBoolean()) { // seq_scaling_list_present_flag[ i ] | ||
131 | if (i < 6) { | ||
132 | this.scaling_list(16, expGolomb); | ||
133 | } else { | ||
134 | this.scaling_list(64, expGolomb); | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | |||
141 | expGolomb.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4 | ||
142 | pic_order_cnt_type = expGolomb.readUnsignedExpGolomb(); | ||
143 | |||
144 | if ( 0 === pic_order_cnt_type ) { | ||
145 | expGolomb.readUnsignedExpGolomb(); //log2_max_pic_order_cnt_lsb_minus4 | ||
146 | } else if ( 1 === pic_order_cnt_type ) { | ||
147 | expGolomb.skipBits(1); // delta_pic_order_always_zero_flag | ||
148 | expGolomb.skipExpGolomb(); // offset_for_non_ref_pic | ||
149 | expGolomb.skipExpGolomb(); // offset_for_top_to_bottom_field | ||
150 | num_ref_frames_in_pic_order_cnt_cycle = expGolomb.readUnsignedExpGolomb(); | ||
151 | for(i = 0 ; i < num_ref_frames_in_pic_order_cnt_cycle ; ++i) { | ||
152 | expGolomb.skipExpGolomb(); // offset_for_ref_frame[ i ] | ||
153 | } | ||
154 | } | ||
155 | |||
156 | expGolomb.skipUnsignedExpGolomb(); // max_num_ref_frames | ||
157 | expGolomb.skipBits(1); // gaps_in_frame_num_value_allowed_flag | ||
158 | pic_width_in_mbs_minus1 = expGolomb.readUnsignedExpGolomb(); | ||
159 | pic_height_in_map_units_minus1 = expGolomb.readUnsignedExpGolomb(); | ||
160 | |||
161 | frame_mbs_only_flag = expGolomb.readBits(1); | ||
162 | if (0 === frame_mbs_only_flag) { | ||
163 | expGolomb.skipBits(1); // mb_adaptive_frame_field_flag | ||
164 | } | ||
165 | |||
166 | expGolomb.skipBits(1); // direct_8x8_inference_flag | ||
167 | frame_cropping_flag = expGolomb.readBoolean(); | ||
168 | if (frame_cropping_flag) { | ||
169 | frame_crop_left_offset = expGolomb.readUnsignedExpGolomb(); | ||
170 | frame_crop_right_offset = expGolomb.readUnsignedExpGolomb(); | ||
171 | frame_crop_top_offset = expGolomb.readUnsignedExpGolomb(); | ||
172 | frame_crop_bottom_offset = expGolomb.readUnsignedExpGolomb(); | ||
173 | } | ||
174 | |||
175 | width = ((pic_width_in_mbs_minus1 + 1) * 16) - frame_crop_left_offset * 2 - frame_crop_right_offset * 2; | ||
176 | height = ((2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16) - (frame_crop_top_offset * 2) - (frame_crop_bottom_offset * 2); | ||
177 | |||
178 | tag.writeMetaDataDouble("videocodecid", 7); | ||
179 | tag.writeMetaDataDouble("width", width); | ||
180 | tag.writeMetaDataDouble("height", height); | ||
181 | // tag.writeMetaDataDouble("videodatarate", 0 ); | ||
182 | // tag.writeMetaDataDouble("framerate", 0); | ||
183 | |||
184 | return tag; | ||
185 | }; | ||
186 | |||
187 | // (pts:uint):FlvTag | ||
188 | H264ExtraData.prototype.extraDataTag = function(pts) { | ||
189 | var | ||
190 | i, | ||
191 | tag = new FlvTag(FlvTag.VIDEO_TAG, true); | ||
192 | |||
193 | tag.dts = pts; | ||
194 | tag.pts = pts; | ||
195 | |||
196 | tag.writeByte(0x01);// version | ||
197 | tag.writeByte(this.sps[0][1]);// profile | ||
198 | tag.writeByte(this.sps[0][2]);// compatibility | ||
199 | tag.writeByte(this.sps[0][3]);// level | ||
200 | tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits) | ||
201 | tag.writeByte(0xE0 | 0x01 ); // reserved (3 bits), num of SPS (5 bits) | ||
202 | tag.writeShort( this.sps[0].length ); // data of SPS | ||
203 | tag.writeBytes( this.sps[0] ); // SPS | ||
204 | |||
205 | tag.writeByte( this.pps.length ); // num of PPS (will there ever be more that 1 PPS?) | ||
206 | for (i = 0 ; i < this.pps.length ; ++i) { | ||
207 | tag.writeShort(this.pps[i].length); // 2 bytes for length of PPS | ||
208 | tag.writeBytes(this.pps[i]); // data of PPS | ||
209 | } | ||
210 | |||
211 | return tag; | ||
212 | }; | ||
213 | })(); |
1 | (function(window) { | 1 | (function(window) { |
2 | var | 2 | var |
3 | ExpGolomb = window.videojs.Hls.ExpGolomb, | ||
4 | FlvTag = window.videojs.Hls.FlvTag, | 3 | FlvTag = window.videojs.Hls.FlvTag, |
5 | 4 | H264ExtraData = window.videojs.Hls.H264ExtraData, | |
6 | H264ExtraData = function() { | 5 | H264Stream, |
7 | this.sps = []; // :Array | ||
8 | this.pps = []; // :Array | ||
9 | |||
10 | this.extraDataExists = function() { // :Boolean | ||
11 | return this.sps.length > 0; | ||
12 | }; | ||
13 | |||
14 | // (sizeOfScalingList:int, expGolomb:ExpGolomb):void | ||
15 | this.scaling_list = function(sizeOfScalingList, expGolomb) { | ||
16 | var | ||
17 | lastScale = 8, // :int | ||
18 | nextScale = 8, // :int | ||
19 | j, | ||
20 | delta_scale; // :int | ||
21 | |||
22 | for (j = 0; j < sizeOfScalingList; ++j) { | ||
23 | if (0 !== nextScale) { | ||
24 | delta_scale = expGolomb.readExpGolomb(); | ||
25 | nextScale = (lastScale + delta_scale + 256) % 256; | ||
26 | //useDefaultScalingMatrixFlag = ( j = = 0 && nextScale = = 0 ) | ||
27 | } | ||
28 | |||
29 | lastScale = (nextScale === 0) ? lastScale : nextScale; | ||
30 | // scalingList[ j ] = ( nextScale == 0 ) ? lastScale : nextScale; | ||
31 | // lastScale = scalingList[ j ] | ||
32 | } | ||
33 | }; | ||
34 | |||
35 | /** | ||
36 | * RBSP: raw bit-stream payload. The actual encoded video data. | ||
37 | * | ||
38 | * SPS: sequence parameter set. Part of the RBSP. Metadata to be applied | ||
39 | * to a complete video sequence, like width and height. | ||
40 | */ | ||
41 | this.getSps0Rbsp = function() { // :ByteArray | ||
42 | var | ||
43 | sps = this.sps[0], | ||
44 | offset = 1, | ||
45 | start = 1, | ||
46 | written = 0, | ||
47 | end = sps.byteLength - 2, | ||
48 | result = new Uint8Array(sps.byteLength); | ||
49 | |||
50 | // In order to prevent 0x0000 01 from being interpreted as a | ||
51 | // NAL start code, occurences of that byte sequence in the | ||
52 | // RBSP are escaped with an "emulation byte". That turns | ||
53 | // sequences of 0x0000 01 into 0x0000 0301. When interpreting | ||
54 | // a NAL payload, they must be filtered back out. | ||
55 | while (offset < end) { | ||
56 | if (sps[offset] === 0x00 && | ||
57 | sps[offset + 1] === 0x00 && | ||
58 | sps[offset + 2] === 0x03) { | ||
59 | result.set(sps.subarray(start, offset + 1), written); | ||
60 | written += offset + 1 - start; | ||
61 | start = offset + 3; | ||
62 | } | ||
63 | offset++; | ||
64 | } | ||
65 | result.set(sps.subarray(start), written); | ||
66 | return result.subarray(0, written + (sps.byteLength - start)); | ||
67 | }; | ||
68 | |||
69 | // (pts:uint):FlvTag | ||
70 | this.metaDataTag = function(pts) { | ||
71 | var | ||
72 | tag = new FlvTag(FlvTag.METADATA_TAG), // :FlvTag | ||
73 | expGolomb, // :ExpGolomb | ||
74 | profile_idc, // :int | ||
75 | chroma_format_idc, // :int | ||
76 | imax, // :int | ||
77 | i, // :int | ||
78 | |||
79 | pic_order_cnt_type, // :int | ||
80 | num_ref_frames_in_pic_order_cnt_cycle, // :uint | ||
81 | |||
82 | pic_width_in_mbs_minus1, // :int | ||
83 | pic_height_in_map_units_minus1, // :int | ||
84 | |||
85 | frame_mbs_only_flag, // :int | ||
86 | frame_cropping_flag, // :Boolean | ||
87 | |||
88 | frame_crop_left_offset = 0, // :int | ||
89 | frame_crop_right_offset = 0, // :int | ||
90 | frame_crop_top_offset = 0, // :int | ||
91 | frame_crop_bottom_offset = 0, // :int | ||
92 | |||
93 | width, | ||
94 | height; | ||
95 | |||
96 | tag.dts = pts; | ||
97 | tag.pts = pts; | ||
98 | expGolomb = new ExpGolomb(this.getSps0Rbsp()); | ||
99 | |||
100 | // :int = expGolomb.readUnsignedByte(); // profile_idc u(8) | ||
101 | profile_idc = expGolomb.readUnsignedByte(); | ||
102 | |||
103 | // constraint_set[0-5]_flag, u(1), reserved_zero_2bits u(2), level_idc u(8) | ||
104 | expGolomb.skipBits(16); | ||
105 | |||
106 | // seq_parameter_set_id | ||
107 | expGolomb.skipUnsignedExpGolomb(); | ||
108 | |||
109 | if (profile_idc === 100 || | ||
110 | profile_idc === 110 || | ||
111 | profile_idc === 122 || | ||
112 | profile_idc === 244 || | ||
113 | profile_idc === 44 || | ||
114 | profile_idc === 83 || | ||
115 | profile_idc === 86 || | ||
116 | profile_idc === 118 || | ||
117 | profile_idc === 128) { | ||
118 | chroma_format_idc = expGolomb.readUnsignedExpGolomb(); | ||
119 | if (3 === chroma_format_idc) { | ||
120 | expGolomb.skipBits(1); // separate_colour_plane_flag | ||
121 | } | ||
122 | expGolomb.skipUnsignedExpGolomb(); // bit_depth_luma_minus8 | ||
123 | expGolomb.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8 | ||
124 | expGolomb.skipBits(1); // qpprime_y_zero_transform_bypass_flag | ||
125 | if (expGolomb.readBoolean()) { // seq_scaling_matrix_present_flag | ||
126 | imax = (chroma_format_idc !== 3) ? 8 : 12; | ||
127 | for (i = 0 ; i < imax ; ++i) { | ||
128 | if (expGolomb.readBoolean()) { // seq_scaling_list_present_flag[ i ] | ||
129 | if (i < 6) { | ||
130 | this.scaling_list(16, expGolomb); | ||
131 | } else { | ||
132 | this.scaling_list(64, expGolomb); | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | |||
139 | expGolomb.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4 | ||
140 | pic_order_cnt_type = expGolomb.readUnsignedExpGolomb(); | ||
141 | |||
142 | if ( 0 === pic_order_cnt_type ) { | ||
143 | expGolomb.readUnsignedExpGolomb(); //log2_max_pic_order_cnt_lsb_minus4 | ||
144 | } else if ( 1 === pic_order_cnt_type ) { | ||
145 | expGolomb.skipBits(1); // delta_pic_order_always_zero_flag | ||
146 | expGolomb.skipExpGolomb(); // offset_for_non_ref_pic | ||
147 | expGolomb.skipExpGolomb(); // offset_for_top_to_bottom_field | ||
148 | num_ref_frames_in_pic_order_cnt_cycle = expGolomb.readUnsignedExpGolomb(); | ||
149 | for(i = 0 ; i < num_ref_frames_in_pic_order_cnt_cycle ; ++i) { | ||
150 | expGolomb.skipExpGolomb(); // offset_for_ref_frame[ i ] | ||
151 | } | ||
152 | } | ||
153 | |||
154 | expGolomb.skipUnsignedExpGolomb(); // max_num_ref_frames | ||
155 | expGolomb.skipBits(1); // gaps_in_frame_num_value_allowed_flag | ||
156 | pic_width_in_mbs_minus1 = expGolomb.readUnsignedExpGolomb(); | ||
157 | pic_height_in_map_units_minus1 = expGolomb.readUnsignedExpGolomb(); | ||
158 | |||
159 | frame_mbs_only_flag = expGolomb.readBits(1); | ||
160 | if (0 === frame_mbs_only_flag) { | ||
161 | expGolomb.skipBits(1); // mb_adaptive_frame_field_flag | ||
162 | } | ||
163 | |||
164 | expGolomb.skipBits(1); // direct_8x8_inference_flag | ||
165 | frame_cropping_flag = expGolomb.readBoolean(); | ||
166 | if (frame_cropping_flag) { | ||
167 | frame_crop_left_offset = expGolomb.readUnsignedExpGolomb(); | ||
168 | frame_crop_right_offset = expGolomb.readUnsignedExpGolomb(); | ||
169 | frame_crop_top_offset = expGolomb.readUnsignedExpGolomb(); | ||
170 | frame_crop_bottom_offset = expGolomb.readUnsignedExpGolomb(); | ||
171 | } | ||
172 | |||
173 | width = ((pic_width_in_mbs_minus1 + 1) * 16) - frame_crop_left_offset * 2 - frame_crop_right_offset * 2; | ||
174 | height = ((2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16) - (frame_crop_top_offset * 2) - (frame_crop_bottom_offset * 2); | ||
175 | |||
176 | tag.writeMetaDataDouble("videocodecid", 7); | ||
177 | tag.writeMetaDataDouble("width", width); | ||
178 | tag.writeMetaDataDouble("height", height); | ||
179 | // tag.writeMetaDataDouble("videodatarate", 0 ); | ||
180 | // tag.writeMetaDataDouble("framerate", 0); | ||
181 | |||
182 | return tag; | ||
183 | }; | ||
184 | |||
185 | // (pts:uint):FlvTag | ||
186 | this.extraDataTag = function(pts) { | ||
187 | var | ||
188 | i, | ||
189 | tag = new FlvTag(FlvTag.VIDEO_TAG, true); | ||
190 | |||
191 | tag.dts = pts; | ||
192 | tag.pts = pts; | ||
193 | |||
194 | tag.writeByte(0x01);// version | ||
195 | tag.writeByte(this.sps[0][1]);// profile | ||
196 | tag.writeByte(this.sps[0][2]);// compatibility | ||
197 | tag.writeByte(this.sps[0][3]);// level | ||
198 | tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits) | ||
199 | tag.writeByte(0xE0 | 0x01 ); // reserved (3 bits), num of SPS (5 bits) | ||
200 | tag.writeShort( this.sps[0].length ); // data of SPS | ||
201 | tag.writeBytes( this.sps[0] ); // SPS | ||
202 | |||
203 | tag.writeByte( this.pps.length ); // num of PPS (will there ever be more that 1 PPS?) | ||
204 | for (i = 0 ; i < this.pps.length ; ++i) { | ||
205 | tag.writeShort(this.pps[i].length); // 2 bytes for length of PPS | ||
206 | tag.writeBytes(this.pps[i]); // data of PPS | ||
207 | } | ||
208 | |||
209 | return tag; | ||
210 | }; | ||
211 | }, | ||
212 | |||
213 | NALUnitType; | 6 | NALUnitType; |
214 | 7 | ||
215 | /** | 8 | /** |
... | @@ -241,235 +34,246 @@ | ... | @@ -241,235 +34,246 @@ |
241 | end_of_stream_rbsp: 11 | 34 | end_of_stream_rbsp: 11 |
242 | }; | 35 | }; |
243 | 36 | ||
244 | window.videojs.Hls.H264Stream = function() { | 37 | window.videojs.Hls.H264Stream = H264Stream = function() { |
245 | var | 38 | this._next_pts = 0; // :uint; |
246 | next_pts, // :uint; | 39 | this._next_dts = 0; // :uint; |
247 | next_dts, // :uint; | 40 | this._pts_offset = 0; // :int |
248 | pts_offset, // :int | 41 | |
42 | this._h264Frame = null; // :FlvTag | ||
249 | 43 | ||
250 | h264Frame, // :FlvTag | 44 | this._oldExtraData = new H264ExtraData(); // :H264ExtraData |
45 | this._newExtraData = new H264ExtraData(); // :H264ExtraData | ||
251 | 46 | ||
252 | oldExtraData = new H264ExtraData(), // :H264ExtraData | 47 | this._nalUnitType = -1; // :int |
253 | newExtraData = new H264ExtraData(), // :H264ExtraData | ||
254 | 48 | ||
255 | nalUnitType = -1, // :int | 49 | this._state = 0; // :uint; |
256 | 50 | ||
257 | state; // :uint; | 51 | this._nextFrameKeyFrame = false; |
258 | 52 | ||
259 | this.tags = []; | 53 | this.tags = []; |
260 | 54 | ||
261 | //(pts:uint):void | 55 | }; |
262 | this.setTimeStampOffset = function(pts) { | 56 | //(pts:uint):void |
263 | pts_offset = pts; | 57 | H264Stream.prototype.setTimeStampOffset = function(pts) { |
264 | }; | 58 | this._pts_offset = pts; |
59 | }; | ||
265 | 60 | ||
266 | //(pts:uint, dts:uint, dataAligned:Boolean):void | 61 | //(pts:uint, dts:uint, dataAligned:Boolean):void |
267 | this.setNextTimeStamp = function(pts, dts, dataAligned) { | 62 | H264Stream.prototype.setNextTimeStamp = function(pts, dts, dataAligned) { |
268 | // We could end up with a DTS less than 0 here. We need to deal with that! | 63 | // We could end up with a DTS less than 0 here. We need to deal with that! |
269 | next_pts = pts - pts_offset; | 64 | this._next_pts = pts - this._pts_offset; |
270 | next_dts = dts - pts_offset; | 65 | this._next_dts = dts - this._pts_offset; |
271 | 66 | ||
272 | // If data is aligned, flush all internal buffers | 67 | // If data is aligned, flush all internal buffers |
273 | if (dataAligned) { | 68 | if (dataAligned) { |
274 | this.finishFrame(); | 69 | this.finishFrame(); |
70 | } | ||
71 | }; | ||
72 | |||
73 | H264Stream.prototype.finishFrame = function() { | ||
74 | if (this._h264Frame) { | ||
75 | // Push SPS before EVERY IDR frame for seeking | ||
76 | if (this._newExtraData.extraDataExists()) { | ||
77 | this._oldExtraData = this._newExtraData; | ||
78 | this._newExtraData = new H264ExtraData(); | ||
275 | } | 79 | } |
276 | }; | ||
277 | |||
278 | this.finishFrame = function() { | ||
279 | if (h264Frame) { | ||
280 | // Push SPS before EVERY IDR frame for seeking | ||
281 | if (newExtraData.extraDataExists()) { | ||
282 | oldExtraData = newExtraData; | ||
283 | newExtraData = new H264ExtraData(); | ||
284 | } | ||
285 | 80 | ||
286 | // Check if keyframe and the length of tags. | 81 | // Check if keyframe and the length of tags. |
287 | // This makes sure we write metadata on the first frame of a segment. | 82 | // This makes sure we write metadata on the first frame of a segment. |
288 | if (h264Frame.keyFrame || this.tags.length === 0) { | 83 | if (this._h264Frame.keyFrame || this.tags.length === 0) { |
289 | // Push extra data on every IDR frame in case we did a stream change + seek | 84 | // Push extra data on every IDR frame in case we did a stream change + seek |
290 | this.tags.push(oldExtraData.metaDataTag(h264Frame.pts)); | 85 | this.tags.push(this._oldExtraData.metaDataTag(this._h264Frame.pts)); |
291 | this.tags.push(oldExtraData.extraDataTag(h264Frame.pts)); | 86 | this.tags.push(this._oldExtraData.extraDataTag(this._h264Frame.pts)); |
292 | } | 87 | } |
293 | 88 | ||
294 | h264Frame.endNalUnit(); | 89 | this._h264Frame.endNalUnit(); |
295 | this.tags.push(h264Frame); | 90 | this.tags.push(this._h264Frame); |
296 | 91 | ||
297 | } | 92 | } |
298 | 93 | ||
299 | h264Frame = null; | 94 | this._h264Frame = null; |
300 | nalUnitType = -1; | 95 | this._nalUnitType = -1; |
301 | state = 0; | 96 | this._state = 0; |
302 | }; | 97 | }; |
303 | |||
304 | // (data:ByteArray, o:int, l:int):void | ||
305 | this.writeBytes = function(data, offset, length) { | ||
306 | var | ||
307 | nalUnitSize, // :uint | ||
308 | start, // :uint | ||
309 | end, // :uint | ||
310 | t; // :int | ||
311 | |||
312 | // default argument values | ||
313 | offset = offset || 0; | ||
314 | length = length || 0; | ||
315 | |||
316 | if (length <= 0) { | ||
317 | // data is empty so there's nothing to write | ||
318 | return; | ||
319 | } | ||
320 | 98 | ||
321 | // scan through the bytes until we find the start code (0x000001) for a | ||
322 | // NAL unit and then begin writing it out | ||
323 | // strip NAL start codes as we go | ||
324 | switch (state) { | ||
325 | default: | ||
326 | /* falls through */ | ||
327 | case 0: | ||
328 | state = 1; | ||
329 | /* falls through */ | ||
330 | case 1: | ||
331 | // A NAL unit may be split across two TS packets. Look back a bit to | ||
332 | // make sure the prefix of the start code wasn't already written out. | ||
333 | if (data[offset] <= 1) { | ||
334 | nalUnitSize = h264Frame ? h264Frame.nalUnitSize() : 0; | ||
335 | if (nalUnitSize >= 1 && h264Frame.negIndex(1) === 0) { | ||
336 | // ?? ?? 00 | O[01] ?? ?? | ||
337 | if (data[offset] === 1 && | ||
338 | nalUnitSize >= 2 && | ||
339 | h264Frame.negIndex(2) === 0) { | ||
340 | // ?? 00 00 : 01 | ||
341 | if (3 <= nalUnitSize && 0 === h264Frame.negIndex(3)) { | ||
342 | h264Frame.length -= 3; // 00 00 00 : 01 | ||
343 | } else { | ||
344 | h264Frame.length -= 2; // 00 00 : 01 | ||
345 | } | ||
346 | |||
347 | state = 3; | ||
348 | return this.writeBytes(data, offset + 1, length - 1); | ||
349 | } | ||
350 | 99 | ||
351 | if (length > 1 && data[offset] === 0 && data[offset + 1] === 1) { | 100 | H264Stream.prototype.setNextFrameKeyFrame = function() { |
352 | // ?? 00 | 00 01 | 101 | this._nextFrameKeyFrame = true; |
353 | if (nalUnitSize >= 2 && h264Frame.negIndex(2) === 0) { | 102 | }; |
354 | h264Frame.length -= 2; // 00 00 : 00 01 | ||
355 | } else { | ||
356 | h264Frame.length -= 1; // 00 : 00 01 | ||
357 | } | ||
358 | 103 | ||
359 | state = 3; | 104 | // (data:ByteArray, o:int, l:int):void |
360 | return this.writeBytes(data, offset + 2, length - 2); | 105 | H264Stream.prototype.writeBytes = function(data, offset, length) { |
106 | var | ||
107 | nalUnitSize, // :uint | ||
108 | start, // :uint | ||
109 | end, // :uint | ||
110 | t; // :int | ||
111 | |||
112 | // default argument values | ||
113 | offset = offset || 0; | ||
114 | length = length || 0; | ||
115 | |||
116 | if (length <= 0) { | ||
117 | // data is empty so there's nothing to write | ||
118 | return; | ||
119 | } | ||
120 | |||
121 | // scan through the bytes until we find the start code (0x000001) for a | ||
122 | // NAL unit and then begin writing it out | ||
123 | // strip NAL start codes as we go | ||
124 | switch (this._state) { | ||
125 | default: | ||
126 | /* falls through */ | ||
127 | case 0: | ||
128 | this._state = 1; | ||
129 | /* falls through */ | ||
130 | case 1: | ||
131 | // A NAL unit may be split across two TS packets. Look back a bit to | ||
132 | // make sure the prefix of the start code wasn't already written out. | ||
133 | if (data[offset] <= 1) { | ||
134 | nalUnitSize = this._h264Frame ? this._h264Frame.nalUnitSize() : 0; | ||
135 | if (nalUnitSize >= 1 && this._h264Frame.negIndex(1) === 0) { | ||
136 | // ?? ?? 00 | O[01] ?? ?? | ||
137 | if (data[offset] === 1 && | ||
138 | nalUnitSize >= 2 && | ||
139 | this._h264Frame.negIndex(2) === 0) { | ||
140 | // ?? 00 00 : 01 | ||
141 | if (3 <= nalUnitSize && 0 === this._h264Frame.negIndex(3)) { | ||
142 | this._h264Frame.length -= 3; // 00 00 00 : 01 | ||
143 | } else { | ||
144 | this._h264Frame.length -= 2; // 00 00 : 01 | ||
361 | } | 145 | } |
362 | 146 | ||
363 | if (length > 2 && | 147 | this._state = 3; |
364 | data[offset] === 0 && | 148 | return this.writeBytes(data, offset + 1, length - 1); |
365 | data[offset + 1] === 0 && | 149 | } |
366 | data[offset + 2] === 1) { | 150 | |
367 | // 00 : 00 00 01 | 151 | if (length > 1 && data[offset] === 0 && data[offset + 1] === 1) { |
368 | // h264Frame.length -= 1; | 152 | // ?? 00 | 00 01 |
369 | state = 3; | 153 | if (nalUnitSize >= 2 && this._h264Frame.negIndex(2) === 0) { |
370 | return this.writeBytes(data, offset + 3, length - 3); | 154 | this._h264Frame.length -= 2; // 00 00 : 00 01 |
155 | } else { | ||
156 | this._h264Frame.length -= 1; // 00 : 00 01 | ||
371 | } | 157 | } |
158 | |||
159 | this._state = 3; | ||
160 | return this.writeBytes(data, offset + 2, length - 2); | ||
161 | } | ||
162 | |||
163 | if (length > 2 && | ||
164 | data[offset] === 0 && | ||
165 | data[offset + 1] === 0 && | ||
166 | data[offset + 2] === 1) { | ||
167 | // 00 : 00 00 01 | ||
168 | // this._h264Frame.length -= 1; | ||
169 | this._state = 3; | ||
170 | return this.writeBytes(data, offset + 3, length - 3); | ||
372 | } | 171 | } |
373 | } | 172 | } |
374 | // allow fall through if the above fails, we may end up checking a few | 173 | } |
375 | // bytes a second time. But that case will be VERY rare | 174 | // allow fall through if the above fails, we may end up checking a few |
376 | state = 2; | 175 | // bytes a second time. But that case will be VERY rare |
377 | /* falls through */ | 176 | this._state = 2; |
378 | case 2: | 177 | /* falls through */ |
379 | // Look for start codes in the data from the current offset forward | 178 | case 2: |
380 | start = offset; | 179 | // Look for start codes in the data from the current offset forward |
381 | end = start + length; | 180 | start = offset; |
382 | for (t = end - 3; offset < t;) { | 181 | end = start + length; |
383 | if (data[offset + 2] > 1) { | 182 | for (t = end - 3; offset < t;) { |
384 | // if data[offset + 2] is greater than 1, there is no way a start | 183 | if (data[offset + 2] > 1) { |
385 | // code can begin before offset + 3 | 184 | // if data[offset + 2] is greater than 1, there is no way a start |
386 | offset += 3; | 185 | // code can begin before offset + 3 |
387 | } else if (data[offset + 1] !== 0) { | 186 | offset += 3; |
388 | offset += 2; | 187 | } else if (data[offset + 1] !== 0) { |
389 | } else if (data[offset] !== 0) { | 188 | offset += 2; |
390 | offset += 1; | 189 | } else if (data[offset] !== 0) { |
391 | } else { | 190 | offset += 1; |
392 | // If we get here we have 00 00 00 or 00 00 01 | 191 | } else { |
393 | if (data[offset + 2] === 1) { | 192 | // If we get here we have 00 00 00 or 00 00 01 |
394 | if (offset > start) { | 193 | if (data[offset + 2] === 1) { |
395 | h264Frame.writeBytes(data, start, offset - start); | 194 | if (offset > start) { |
396 | } | 195 | this._h264Frame.writeBytes(data, start, offset - start); |
397 | state = 3; | ||
398 | offset += 3; | ||
399 | return this.writeBytes(data, offset, end - offset); | ||
400 | } | 196 | } |
197 | this._state = 3; | ||
198 | offset += 3; | ||
199 | return this.writeBytes(data, offset, end - offset); | ||
200 | } | ||
401 | 201 | ||
402 | if (end - offset >= 4 && | 202 | if (end - offset >= 4 && |
403 | data[offset + 2] === 0 && | 203 | data[offset + 2] === 0 && |
404 | data[offset + 3] === 1) { | 204 | data[offset + 3] === 1) { |
405 | if (offset > start) { | 205 | if (offset > start) { |
406 | h264Frame.writeBytes(data, start, offset - start); | 206 | this._h264Frame.writeBytes(data, start, offset - start); |
407 | } | ||
408 | state = 3; | ||
409 | offset += 4; | ||
410 | return this.writeBytes(data, offset, end - offset); | ||
411 | } | 207 | } |
412 | 208 | this._state = 3; | |
413 | // We are at the end of the buffer, or we have 3 NULLS followed by | 209 | offset += 4; |
414 | // something that is not a 1, either way we can step forward by at | 210 | return this.writeBytes(data, offset, end - offset); |
415 | // least 3 | ||
416 | offset += 3; | ||
417 | } | 211 | } |
212 | |||
213 | // We are at the end of the buffer, or we have 3 NULLS followed by | ||
214 | // something that is not a 1, either way we can step forward by at | ||
215 | // least 3 | ||
216 | offset += 3; | ||
418 | } | 217 | } |
218 | } | ||
419 | 219 | ||
420 | // We did not find any start codes. Try again next packet | 220 | // We did not find any start codes. Try again next packet |
421 | state = 1; | 221 | this._state = 1; |
422 | if (h264Frame) { | 222 | if (this._h264Frame) { |
423 | h264Frame.writeBytes(data, start, length); | 223 | this._h264Frame.writeBytes(data, start, length); |
224 | } | ||
225 | return; | ||
226 | case 3: | ||
227 | // The next byte is the first byte of a NAL Unit | ||
228 | |||
229 | if (this._h264Frame) { | ||
230 | // we've come to a new NAL unit so finish up the one we've been | ||
231 | // working on | ||
232 | |||
233 | switch (this._nalUnitType) { | ||
234 | case NALUnitType.seq_parameter_set_rbsp: | ||
235 | this._h264Frame.endNalUnit(this._newExtraData.sps); | ||
236 | break; | ||
237 | case NALUnitType.pic_parameter_set_rbsp: | ||
238 | this._h264Frame.endNalUnit(this._newExtraData.pps); | ||
239 | break; | ||
240 | case NALUnitType.slice_layer_without_partitioning_rbsp_idr: | ||
241 | this._h264Frame.endNalUnit(); | ||
242 | break; | ||
243 | default: | ||
244 | this._h264Frame.endNalUnit(); | ||
245 | break; | ||
424 | } | 246 | } |
425 | return; | 247 | } |
426 | case 3: | 248 | |
427 | // The next byte is the first byte of a NAL Unit | 249 | // setup to begin processing the new NAL unit |
428 | 250 | this._nalUnitType = data[offset] & 0x1F; | |
429 | if (h264Frame) { | 251 | if (this._h264Frame) { |
430 | // we've come to a new NAL unit so finish up the one we've been | 252 | if (this._nalUnitType === NALUnitType.access_unit_delimiter_rbsp) { |
431 | // working on | 253 | // starting a new access unit, flush the previous one |
432 | 254 | this.finishFrame(); | |
433 | switch (nalUnitType) { | 255 | } else if (this._nalUnitType === NALUnitType.slice_layer_without_partitioning_rbsp_idr) { |
434 | case NALUnitType.seq_parameter_set_rbsp: | 256 | this._h264Frame.keyFrame = true; |
435 | h264Frame.endNalUnit(newExtraData.sps); | ||
436 | break; | ||
437 | case NALUnitType.pic_parameter_set_rbsp: | ||
438 | h264Frame.endNalUnit(newExtraData.pps); | ||
439 | break; | ||
440 | case NALUnitType.slice_layer_without_partitioning_rbsp_idr: | ||
441 | h264Frame.endNalUnit(); | ||
442 | break; | ||
443 | default: | ||
444 | h264Frame.endNalUnit(); | ||
445 | break; | ||
446 | } | 257 | } |
447 | } | 258 | } |
448 | 259 | ||
449 | // setup to begin processing the new NAL unit | 260 | // finishFrame may render this._h264Frame null, so we must test again |
450 | nalUnitType = data[offset] & 0x1F; | 261 | if (!this._h264Frame) { |
451 | if (h264Frame) { | 262 | this._h264Frame = new FlvTag(FlvTag.VIDEO_TAG); |
452 | if (nalUnitType === NALUnitType.access_unit_delimiter_rbsp) { | 263 | this._h264Frame.pts = this._next_pts; |
453 | // starting a new access unit, flush the previous one | 264 | this._h264Frame.dts = this._next_dts; |
454 | this.finishFrame(); | ||
455 | } else if (nalUnitType === NALUnitType.slice_layer_without_partitioning_rbsp_idr) { | ||
456 | h264Frame.keyFrame = true; | ||
457 | } | ||
458 | } | ||
459 | 265 | ||
460 | // finishFrame may render h264Frame null, so we must test again | 266 | if (this._nextFrameKeyFrame) { |
461 | if (!h264Frame) { | 267 | this._h264Frame.keyFrame = true; |
462 | h264Frame = new FlvTag(FlvTag.VIDEO_TAG); | 268 | this._nextFrameKeyFrame = false; |
463 | h264Frame.pts = next_pts; | ||
464 | h264Frame.dts = next_dts; | ||
465 | } | 269 | } |
270 | } | ||
466 | 271 | ||
467 | h264Frame.startNalUnit(); | 272 | this._h264Frame.startNalUnit(); |
468 | // We know there will not be an overlapping start code, so we can skip | 273 | // We know there will not be an overlapping start code, so we can skip |
469 | // that test | 274 | // that test |
470 | state = 2; | 275 | this._state = 2; |
471 | return this.writeBytes(data, offset, length); | 276 | return this.writeBytes(data, offset, length); |
472 | } // switch | 277 | } // switch |
473 | }; | ||
474 | }; | 278 | }; |
475 | })(this); | 279 | })(this); | ... | ... |
... | @@ -214,6 +214,9 @@ | ... | @@ -214,6 +214,9 @@ |
214 | // adaptation_field_control, whether this header is followed by an | 214 | // adaptation_field_control, whether this header is followed by an |
215 | // adaptation field, a payload, or both | 215 | // adaptation field, a payload, or both |
216 | afflag = (data[offset + 3] & 0x30 ) >>> 4, | 216 | afflag = (data[offset + 3] & 0x30 ) >>> 4, |
217 | adaptationFieldLength, | ||
218 | afftemp, | ||
219 | randomAccessIndicator, | ||
217 | 220 | ||
218 | patTableId, // :int | 221 | patTableId, // :int |
219 | patCurrentNextIndicator, // Boolean | 222 | patCurrentNextIndicator, // Boolean |
... | @@ -247,7 +250,19 @@ | ... | @@ -247,7 +250,19 @@ |
247 | // used to specify some forms of timing and control data that we | 250 | // used to specify some forms of timing and control data that we |
248 | // do not currently use. | 251 | // do not currently use. |
249 | if (afflag > 0x01) { | 252 | if (afflag > 0x01) { |
250 | offset += data[offset] + 1; | 253 | adaptationFieldLength = data[offset]; |
254 | |||
255 | if (adaptationFieldLength > 0) { | ||
256 | afftemp = data[offset + 1]; | ||
257 | |||
258 | randomAccessIndicator = (afftemp & 0x40) >>> 6; | ||
259 | |||
260 | if (randomAccessIndicator === 1) { | ||
261 | h264Stream.setNextFrameKeyFrame(); | ||
262 | } | ||
263 | } | ||
264 | |||
265 | offset += adaptationFieldLength + 1; | ||
251 | } | 266 | } |
252 | 267 | ||
253 | // Handle a Program Association Table (PAT). PATs map PIDs to | 268 | // Handle a Program Association Table (PAT). PATs map PIDs to | ... | ... |
... | @@ -62,7 +62,10 @@ test('metadata is generated for IDRs after a full NAL unit is written', function | ... | @@ -62,7 +62,10 @@ test('metadata is generated for IDRs after a full NAL unit is written', function |
62 | 62 | ||
63 | test('starting PTS values can be negative', function() { | 63 | test('starting PTS values can be negative', function() { |
64 | var | 64 | var |
65 | h264Stream = new videojs.Hls.H264Stream(), | 65 | H264ExtraData = videojs.Hls.H264ExtraData, |
66 | oldExtraData = H264ExtraData.prototype.extraDataTag, | ||
67 | oldMetadata = H264ExtraData.prototype.metaDataTag, | ||
68 | h264Stream, | ||
66 | accessUnitDelimiter = new Uint8Array([ | 69 | accessUnitDelimiter = new Uint8Array([ |
67 | 0x00, | 70 | 0x00, |
68 | 0x00, | 71 | 0x00, |
... | @@ -70,8 +73,14 @@ test('starting PTS values can be negative', function() { | ... | @@ -70,8 +73,14 @@ test('starting PTS values can be negative', function() { |
70 | nalUnitTypes.access_unit_delimiter_rbsp | 73 | nalUnitTypes.access_unit_delimiter_rbsp |
71 | ]); | 74 | ]); |
72 | 75 | ||
73 | // add a "tag" to the stream so that it doesn't try and parse metadata | 76 | H264ExtraData.prototype.extraDataTag = function() { |
74 | h264Stream.tags.push('spacer tag'); | 77 | return 'extraDataTag'; |
78 | }; | ||
79 | H264ExtraData.prototype.metaDataTag = function() { | ||
80 | return 'metaDataTag'; | ||
81 | }; | ||
82 | |||
83 | h264Stream = new videojs.Hls.H264Stream(); | ||
75 | 84 | ||
76 | h264Stream.setTimeStampOffset(-100); | 85 | h264Stream.setTimeStampOffset(-100); |
77 | h264Stream.setNextTimeStamp(-100, -100, true); | 86 | h264Stream.setNextTimeStamp(-100, -100, true); |
... | @@ -83,6 +92,8 @@ test('starting PTS values can be negative', function() { | ... | @@ -83,6 +92,8 @@ test('starting PTS values can be negative', function() { |
83 | // flush out the last tag | 92 | // flush out the last tag |
84 | h264Stream.writeBytes(accessUnitDelimiter, 0, accessUnitDelimiter.byteLength); | 93 | h264Stream.writeBytes(accessUnitDelimiter, 0, accessUnitDelimiter.byteLength); |
85 | 94 | ||
95 | // shift the metadata and extradata tags out, since we don't care about them here | ||
96 | h264Stream.tags.shift(); | ||
86 | h264Stream.tags.shift(); | 97 | h264Stream.tags.shift(); |
87 | 98 | ||
88 | strictEqual(h264Stream.tags.length, 3, 'three tags are ready'); | 99 | strictEqual(h264Stream.tags.length, 3, 'three tags are ready'); |
... | @@ -93,6 +104,57 @@ test('starting PTS values can be negative', function() { | ... | @@ -93,6 +104,57 @@ test('starting PTS values can be negative', function() { |
93 | 104 | ||
94 | strictEqual(h264Stream.tags[2].pts, 100, 'the third PTS is 100'); | 105 | strictEqual(h264Stream.tags[2].pts, 100, 'the third PTS is 100'); |
95 | strictEqual(h264Stream.tags[2].dts, 100, 'the third DTS is 100'); | 106 | strictEqual(h264Stream.tags[2].dts, 100, 'the third DTS is 100'); |
107 | |||
108 | H264ExtraData.prototype.extraDataTag = oldExtraData; | ||
109 | H264ExtraData.prototype.metaDataTag = oldMetadata; | ||
110 | }); | ||
111 | |||
112 | test('make sure we add metadata and extra data at the beginning of a stream', function() { | ||
113 | var | ||
114 | H264ExtraData = videojs.Hls.H264ExtraData, | ||
115 | oldExtraData = H264ExtraData.prototype.extraDataTag, | ||
116 | oldMetadata = H264ExtraData.prototype.metaDataTag, | ||
117 | h264Stream, | ||
118 | accessUnitDelimiter = new Uint8Array([ | ||
119 | 0x00, | ||
120 | 0x00, | ||
121 | 0x01, | ||
122 | nalUnitTypes.access_unit_delimiter_rbsp | ||
123 | ]); | ||
124 | |||
125 | H264ExtraData.prototype.extraDataTag = function() { | ||
126 | return 'extraDataTag'; | ||
127 | }; | ||
128 | H264ExtraData.prototype.metaDataTag = function() { | ||
129 | return 'metaDataTag'; | ||
130 | }; | ||
131 | |||
132 | h264Stream = new videojs.Hls.H264Stream(); | ||
133 | |||
134 | h264Stream.setTimeStampOffset(0); | ||
135 | h264Stream.setNextTimeStamp(0, 0, true); | ||
136 | h264Stream.writeBytes(accessUnitDelimiter, 0, accessUnitDelimiter.byteLength); | ||
137 | |||
138 | // make sure that keyFrame is set to false but that we don't have any tags currently written out | ||
139 | h264Stream._h264Frame.keyFrame = false; | ||
140 | h264Stream.tags = []; | ||
141 | |||
142 | h264Stream.setNextTimeStamp(5, 5, true); | ||
143 | h264Stream.writeBytes(accessUnitDelimiter, 0, accessUnitDelimiter.byteLength); | ||
144 | // flush out the last tag | ||
145 | h264Stream.writeBytes(accessUnitDelimiter, 0, accessUnitDelimiter.byteLength); | ||
146 | |||
147 | strictEqual(h264Stream.tags.length, 4, 'three tags are ready'); | ||
148 | strictEqual(h264Stream.tags[0], 'metaDataTag', 'the first tag is the metaDataTag'); | ||
149 | strictEqual(h264Stream.tags[1], 'extraDataTag', 'the second tag is the extraDataTag'); | ||
150 | |||
151 | strictEqual(h264Stream.tags[2].pts, 0, 'the first PTS is 0'); | ||
152 | strictEqual(h264Stream.tags[2].dts, 0, 'the first DTS is 0'); | ||
153 | strictEqual(h264Stream.tags[3].pts, 5, 'the second PTS is 5'); | ||
154 | strictEqual(h264Stream.tags[3].dts, 5, 'the second DTS is 5'); | ||
155 | |||
156 | H264ExtraData.prototype.extraDataTag = oldExtraData; | ||
157 | H264ExtraData.prototype.metaDataTag = oldMetadata; | ||
96 | }); | 158 | }); |
97 | 159 | ||
98 | })(window.videojs); | 160 | })(window.videojs); | ... | ... |
... | @@ -83,6 +83,7 @@ module.exports = function(config) { | ... | @@ -83,6 +83,7 @@ module.exports = function(config) { |
83 | '../src/stream.js', | 83 | '../src/stream.js', |
84 | '../src/flv-tag.js', | 84 | '../src/flv-tag.js', |
85 | '../src/exp-golomb.js', | 85 | '../src/exp-golomb.js', |
86 | '../src/h264-extradata.js', | ||
86 | '../src/h264-stream.js', | 87 | '../src/h264-stream.js', |
87 | '../src/aac-stream.js', | 88 | '../src/aac-stream.js', |
88 | '../src/metadata-stream.js', | 89 | '../src/metadata-stream.js', | ... | ... |
... | @@ -47,6 +47,7 @@ module.exports = function(config) { | ... | @@ -47,6 +47,7 @@ module.exports = function(config) { |
47 | '../src/stream.js', | 47 | '../src/stream.js', |
48 | '../src/flv-tag.js', | 48 | '../src/flv-tag.js', |
49 | '../src/exp-golomb.js', | 49 | '../src/exp-golomb.js', |
50 | '../src/h264-extradata.js', | ||
50 | '../src/h264-stream.js', | 51 | '../src/h264-stream.js', |
51 | '../src/aac-stream.js', | 52 | '../src/aac-stream.js', |
52 | '../src/metadata-stream.js', | 53 | '../src/metadata-stream.js', | ... | ... |
... | @@ -24,6 +24,7 @@ | ... | @@ -24,6 +24,7 @@ |
24 | <script src="../src/stream.js"></script> | 24 | <script src="../src/stream.js"></script> |
25 | <script src="../src/flv-tag.js"></script> | 25 | <script src="../src/flv-tag.js"></script> |
26 | <script src="../src/exp-golomb.js"></script> | 26 | <script src="../src/exp-golomb.js"></script> |
27 | <script src="../src/h264-extradata.js"></script> | ||
27 | <script src="../src/h264-stream.js"></script> | 28 | <script src="../src/h264-stream.js"></script> |
28 | <script src="../src/aac-stream.js"></script> | 29 | <script src="../src/aac-stream.js"></script> |
29 | <script src="../src/metadata-stream.js"></script> | 30 | <script src="../src/metadata-stream.js"></script> | ... | ... |
-
Please register or sign in to post a comment