正在显示
1 个修改的文件
包含
737 行增加
和
741 行删除
trunk/src/core/srs_core_hls.cpp
100755 → 100644
1 | -/* | ||
2 | -The MIT License (MIT) | ||
3 | - | ||
4 | -Copyright (c) 2013 winlin | ||
5 | - | ||
6 | -Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
7 | -this software and associated documentation files (the "Software"), to deal in | ||
8 | -the Software without restriction, including without limitation the rights to | ||
9 | -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
10 | -the Software, and to permit persons to whom the Software is furnished to do so, | ||
11 | -subject to the following conditions: | ||
12 | - | ||
13 | -The above copyright notice and this permission notice shall be included in all | ||
14 | -copies or substantial portions of the Software. | ||
15 | - | ||
16 | -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
18 | -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
19 | -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
20 | -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
21 | -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
22 | -*/ | ||
23 | - | ||
24 | -#include <srs_core_hls.hpp> | ||
25 | - | ||
26 | -#include <sys/types.h> | ||
27 | -#include <sys/stat.h> | ||
28 | -#include <fcntl.h> | ||
29 | -#include <stdlib.h> | ||
30 | -#include <string.h> | ||
31 | - | ||
32 | -#include <srs_core_error.hpp> | ||
33 | -#include <srs_core_codec.hpp> | ||
34 | -#include <srs_core_amf0.hpp> | ||
35 | -#include <srs_core_protocol.hpp> | ||
36 | -#include <srs_core_config.hpp> | ||
37 | - | ||
38 | -SrsHLS::SrsHLS() | ||
39 | -{ | ||
40 | - hls_enabled = false; | ||
41 | - codec = new SrsCodec(); | ||
42 | - sample = new SrsCodecSample(); | ||
43 | - muxer = NULL; | ||
44 | -} | ||
45 | - | ||
46 | -SrsHLS::~SrsHLS() | ||
47 | -{ | ||
48 | - srs_freep(codec); | ||
49 | - srs_freep(sample); | ||
50 | - srs_freep(muxer); | ||
51 | -} | ||
52 | - | ||
53 | -int SrsHLS::on_publish(std::string _vhost) | ||
54 | -{ | ||
55 | - int ret = ERROR_SUCCESS; | ||
56 | - | ||
57 | - if (muxer) { | ||
58 | - ret = ERROR_HLS_BUSY; | ||
59 | - srs_error("hls is busy, something error, " | ||
60 | - "vhost=%s, ret=%d", _vhost.c_str(), ret); | ||
61 | - return ret; | ||
62 | - } | ||
63 | - | ||
64 | - vhost = _vhost; | ||
65 | - muxer = new SrsTSMuxer(); | ||
66 | - | ||
67 | - // try to open the HLS muxer | ||
68 | - SrsConfDirective* conf = config->get_hls(vhost); | ||
69 | - if (!conf && conf->arg0() == "off") { | ||
70 | - return ret; | ||
71 | - } | ||
72 | - | ||
73 | - // TODO: check the audio and video, ensure both exsists. | ||
74 | - // for use fixed mpegts header specifeid the audio and video pid. | ||
75 | - | ||
76 | - hls_enabled = true; | ||
77 | - | ||
78 | - std::string path = SRS_CONF_DEFAULT_HLS_PATH; | ||
79 | - if ((conf = config->get_hls_path(vhost)) != NULL) { | ||
80 | - path = conf->arg0(); | ||
81 | - } | ||
82 | - | ||
83 | - // TODO: generate by m3u8 muxer. | ||
84 | - path += "/1.ts"; | ||
85 | - | ||
86 | - if ((ret = muxer->open(path)) != ERROR_SUCCESS) { | ||
87 | - srs_error("open hls muxer failed. ret=%d", ret); | ||
88 | - return ret; | ||
89 | - } | ||
90 | - | ||
91 | - return ret; | ||
92 | -} | ||
93 | - | ||
94 | -void SrsHLS::on_unpublish() | ||
95 | -{ | ||
96 | - hls_enabled = false; | ||
97 | - muxer->close(); | ||
98 | - srs_freep(muxer); | ||
99 | -} | ||
100 | - | ||
101 | -int SrsHLS::on_meta_data(SrsOnMetaDataPacket* metadata) | ||
102 | -{ | ||
103 | - int ret = ERROR_SUCCESS; | ||
104 | - | ||
105 | - if (!metadata || !metadata->metadata) { | ||
106 | - srs_trace("no metadata persent, hls ignored it."); | ||
107 | - return ret; | ||
108 | - } | ||
109 | - | ||
110 | - SrsAmf0Object* obj = metadata->metadata; | ||
111 | - if (obj->size() <= 0) { | ||
112 | - srs_trace("no metadata persent, hls ignored it."); | ||
113 | - return ret; | ||
114 | - } | ||
115 | - | ||
116 | - // finger out the codec info from metadata if possible. | ||
117 | - SrsAmf0Any* prop = NULL; | ||
118 | - | ||
119 | - if ((prop = obj->get_property("duration")) != NULL && prop->is_number()) { | ||
120 | - codec->duration = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
121 | - } | ||
122 | - if ((prop = obj->get_property("width")) != NULL && prop->is_number()) { | ||
123 | - codec->width = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
124 | - } | ||
125 | - if ((prop = obj->get_property("height")) != NULL && prop->is_number()) { | ||
126 | - codec->height = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
127 | - } | ||
128 | - if ((prop = obj->get_property("framerate")) != NULL && prop->is_number()) { | ||
129 | - codec->frame_rate = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
130 | - } | ||
131 | - if ((prop = obj->get_property("videocodecid")) != NULL && prop->is_number()) { | ||
132 | - codec->video_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
133 | - } | ||
134 | - if ((prop = obj->get_property("videodatarate")) != NULL && prop->is_number()) { | ||
135 | - codec->video_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
136 | - } | ||
137 | - | ||
138 | - if ((prop = obj->get_property("audiocodecid")) != NULL && prop->is_number()) { | ||
139 | - codec->audio_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
140 | - } | ||
141 | - if ((prop = obj->get_property("audiodatarate")) != NULL && prop->is_number()) { | ||
142 | - codec->audio_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
143 | - } | ||
144 | - | ||
145 | - // ignore the following, for each flv/rtmp packet contains them: | ||
146 | - // audiosamplerate, sample->sound_rate | ||
147 | - // audiosamplesize, sample->sound_size | ||
148 | - // stereo, sample->sound_type | ||
149 | - | ||
150 | - return ret; | ||
151 | -} | ||
152 | - | ||
153 | -int SrsHLS::on_audio(SrsCommonMessage* audio) | ||
154 | -{ | ||
155 | - int ret = ERROR_SUCCESS; | ||
156 | - | ||
157 | - sample->clear(); | ||
158 | - if ((ret = codec->audio_aac_demux(audio->payload, audio->size, sample)) != ERROR_SUCCESS) { | ||
159 | - return ret; | ||
160 | - } | ||
161 | - | ||
162 | - if (codec->audio_codec_id != SrsCodecAudioAAC) { | ||
163 | - return ret; | ||
164 | - } | ||
165 | - | ||
166 | - // TODO: maybe donot need to demux the aac? | ||
167 | - if (!hls_enabled) { | ||
168 | - return ret; | ||
169 | - } | ||
170 | - | ||
171 | - // ignore sequence header | ||
172 | - if (sample->aac_packet_type == SrsCodecAudioTypeSequenceHeader) { | ||
173 | - return ret; | ||
174 | - } | ||
175 | - | ||
176 | - u_int32_t timestamp = audio->header.timestamp; | ||
177 | - // TODO: correct the timestamp. | ||
178 | - | ||
179 | - if ((ret = muxer->write_audio(timestamp, codec, sample)) != ERROR_SUCCESS) { | ||
180 | - return ret; | ||
181 | - } | ||
182 | - | ||
183 | - return ret; | ||
184 | -} | ||
185 | - | ||
186 | -int SrsHLS::on_video(SrsCommonMessage* video) | ||
187 | -{ | ||
188 | - int ret = ERROR_SUCCESS; | ||
189 | - | ||
190 | - sample->clear(); | ||
191 | - if ((ret = codec->video_avc_demux(video->payload, video->size, sample)) != ERROR_SUCCESS) { | ||
192 | - return ret; | ||
193 | - } | ||
194 | - | ||
195 | - if (codec->video_codec_id != SrsCodecVideoAVC) { | ||
196 | - return ret; | ||
197 | - } | ||
198 | - | ||
199 | - // TODO: maybe donot need to demux the avc? | ||
200 | - if (!hls_enabled) { | ||
201 | - return ret; | ||
202 | - } | ||
203 | - | ||
204 | - // ignore sequence header | ||
205 | - if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) { | ||
206 | - return ret; | ||
207 | - } | ||
208 | - | ||
209 | - u_int32_t timestamp = video->header.timestamp; | ||
210 | - // TODO: correct the timestamp. | ||
211 | - | ||
212 | - if ((ret = muxer->write_video(timestamp, codec, sample)) != ERROR_SUCCESS) { | ||
213 | - return ret; | ||
214 | - } | ||
215 | - | ||
216 | - return ret; | ||
217 | -} | ||
218 | - | ||
219 | -// @see: ngx_rtmp_mpegts_header | ||
220 | -u_int8_t mpegts_header[] = { | ||
221 | - /* TS */ | ||
222 | - 0x47, 0x40, 0x00, 0x10, 0x00, | ||
223 | - /* PSI */ | ||
224 | - 0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00, | ||
225 | - /* PAT */ | ||
226 | - 0x00, 0x01, 0xf0, 0x01, | ||
227 | - /* CRC */ | ||
228 | - 0x2e, 0x70, 0x19, 0x05, | ||
229 | - /* stuffing 167 bytes */ | ||
230 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
231 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
232 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
233 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
234 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
235 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
236 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
237 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
238 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
239 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
240 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
241 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
242 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
243 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
244 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
245 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
246 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
247 | - | ||
248 | - /* TS */ | ||
249 | - 0x47, 0x50, 0x01, 0x10, 0x00, | ||
250 | - /* PSI */ | ||
251 | - 0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00, | ||
252 | - /* PMT */ | ||
253 | - 0xe1, 0x00, | ||
254 | - 0xf0, 0x00, | ||
255 | - 0x1b, 0xe1, 0x00, 0xf0, 0x00, /* h264, pid=0x100=256 */ | ||
256 | - 0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac, pid=0x101=257 */ | ||
257 | - /*0x03, 0xe1, 0x01, 0xf0, 0x00,*/ /* mp3 */ | ||
258 | - /* CRC */ | ||
259 | - 0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */ | ||
260 | - /*0x4e, 0x59, 0x3d, 0x1e,*/ /* crc for mp3 */ | ||
261 | - /* stuffing 157 bytes */ | ||
262 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
263 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
264 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
265 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
266 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
267 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
268 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
269 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
270 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
271 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
272 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
273 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
274 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
275 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
276 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
277 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff | ||
278 | -}; | ||
279 | - | ||
280 | -// @see: NGX_RTMP_HLS_DELAY, | ||
281 | -// 63000: 700ms, ts_tbn=90000 | ||
282 | -#define SRS_HLS_DELAY 63000 | ||
283 | - | ||
284 | -// @see: ngx_rtmp_SrsMpegtsFrame_t | ||
285 | -struct SrsMpegtsFrame | ||
286 | -{ | ||
287 | - int64_t pts; | ||
288 | - int64_t dts; | ||
289 | - int pid; | ||
290 | - int sid; | ||
291 | - int cc; | ||
292 | - bool key; | ||
293 | - | ||
294 | - SrsMpegtsFrame() | ||
295 | - { | ||
296 | - pts = dts = 0; | ||
297 | - pid = sid = cc = 0; | ||
298 | - key = false; | ||
299 | - } | ||
300 | -}; | ||
301 | - | ||
302 | -// @see: ngx_rtmp_mpegts.c | ||
303 | -// TODO: support full mpegts feature in future. | ||
304 | -class SrsMpegtsWriter | ||
305 | -{ | ||
306 | -public: | ||
307 | - static int write_header(int fd) | ||
308 | - { | ||
309 | - int ret = ERROR_SUCCESS; | ||
310 | - | ||
311 | - if (::write(fd, mpegts_header, sizeof(mpegts_header)) != sizeof(mpegts_header)) { | ||
312 | - ret = ERROR_HLS_WRITE_FAILED; | ||
313 | - srs_error("write ts file header failed. ret=%d", ret); | ||
314 | - return ret; | ||
315 | - } | ||
316 | - | ||
317 | - return ret; | ||
318 | - } | ||
319 | - static int write_frame(int fd, SrsMpegtsFrame* frame, SrsCodecBuffer* buffer) | ||
320 | - { | ||
321 | - int ret = ERROR_SUCCESS; | ||
322 | - | ||
323 | - if (!buffer->bytes || buffer->size <= 0) { | ||
324 | - return ret; | ||
325 | - } | ||
326 | - | ||
327 | - char* last = buffer->bytes + buffer->size; | ||
328 | - char* pos = buffer->bytes; | ||
329 | - | ||
330 | - bool first = true; | ||
331 | - while (pos < last) { | ||
332 | - static char packet[188]; | ||
333 | - char* p = packet; | ||
334 | - | ||
335 | - frame->cc++; | ||
336 | - | ||
337 | - // sync_byte; //8bits | ||
338 | - *p++ = 0x47; | ||
339 | - // pid; //13bits | ||
340 | - *p++ = (frame->pid >> 8) & 0x1f; | ||
341 | - // payload_unit_start_indicator; //1bit | ||
342 | - if (first) { | ||
343 | - p[-1] |= 0x40; | ||
344 | - } | ||
345 | - *p++ = frame->pid; | ||
346 | - | ||
347 | - // transport_scrambling_control; //2bits | ||
348 | - // adaption_field_control; //2bits, 0x01: PayloadOnly | ||
349 | - // continuity_counter; //4bits | ||
350 | - *p++ = 0x10 | (frame->cc & 0x0f); | ||
351 | - | ||
352 | - if (first) { | ||
353 | - first = false; | ||
354 | - if (frame->key) { | ||
355 | - p[-1] |= 0x20; // Both Adaption and Payload | ||
356 | - *p++ = 7; // size | ||
357 | - *p++ = 0x50; // random access + PCR | ||
358 | - p = write_pcr(p, frame->dts - SRS_HLS_DELAY); | ||
359 | - } | ||
360 | - | ||
361 | - // PES header | ||
362 | - // packet_start_code_prefix; //24bits, '00 00 01' | ||
363 | - *p++ = 0x00; | ||
364 | - *p++ = 0x00; | ||
365 | - *p++ = 0x01; | ||
366 | - //8bits | ||
367 | - *p++ = frame->sid; | ||
368 | - | ||
369 | - // pts(33bits) need 5bytes. | ||
370 | - u_int8_t header_size = 5; | ||
371 | - u_int8_t flags = 0x80; // pts | ||
372 | - | ||
373 | - // dts(33bits) need 5bytes also | ||
374 | - if (frame->dts != frame->pts) { | ||
375 | - header_size += 5; | ||
376 | - flags |= 0x40; // dts | ||
377 | - } | ||
378 | - | ||
379 | - // 3bytes: flag fields from PES_packet_length to PES_header_data_length | ||
380 | - int pes_size = (last - pos) + header_size + 3; | ||
381 | - if (pes_size > 0xffff) { | ||
382 | - /** | ||
383 | - * when actual packet length > 0xffff(65535), | ||
384 | - * which exceed the max u_int16_t packet length, | ||
385 | - * use 0 packet length, the next unit start indicates the end of packet. | ||
386 | - */ | ||
387 | - pes_size = 0; | ||
388 | - } | ||
389 | - | ||
390 | - // PES_packet_length; //16bits | ||
391 | - *p++ = (pes_size >> 8); | ||
392 | - *p++ = pes_size; | ||
393 | - | ||
394 | - // PES_scrambling_control; //2bits, '10' | ||
395 | - // PES_priority; //1bit | ||
396 | - // data_alignment_indicator; //1bit | ||
397 | - // copyright; //1bit | ||
398 | - // original_or_copy; //1bit | ||
399 | - *p++ = 0x80; /* H222 */ | ||
400 | - | ||
401 | - // PTS_DTS_flags; //2bits | ||
402 | - // ESCR_flag; //1bit | ||
403 | - // ES_rate_flag; //1bit | ||
404 | - // DSM_trick_mode_flag; //1bit | ||
405 | - // additional_copy_info_flag; //1bit | ||
406 | - // PES_CRC_flag; //1bit | ||
407 | - // PES_extension_flag; //1bit | ||
408 | - *p++ = flags; | ||
409 | - | ||
410 | - // PES_header_data_length; //8bits | ||
411 | - *p++ = header_size; | ||
412 | - | ||
413 | - // pts; // 33bits | ||
414 | - p = write_pts(p, flags >> 6, frame->pts + SRS_HLS_DELAY); | ||
415 | - | ||
416 | - // dts; // 33bits | ||
417 | - if (frame->dts != frame->pts) { | ||
418 | - p = write_pts(p, 1, frame->dts + SRS_HLS_DELAY); | ||
419 | - } | ||
420 | - } | ||
421 | - | ||
422 | - int body_size = sizeof(packet) - (p - packet); | ||
423 | - int in_size = last - pos; | ||
424 | - | ||
425 | - if (body_size <= in_size) { | ||
426 | - memcpy(p, pos, body_size); | ||
427 | - pos += body_size; | ||
428 | - } else { | ||
429 | - p = fill_stuff(p, packet, body_size, in_size); | ||
430 | - memcpy(p, pos, in_size); | ||
431 | - pos = last; | ||
432 | - } | ||
433 | - | ||
434 | - // write ts packet | ||
435 | - if (::write(fd, packet, sizeof(packet)) != sizeof(packet)) { | ||
436 | - ret = ERROR_HLS_WRITE_FAILED; | ||
437 | - srs_error("write ts file failed. ret=%d", ret); | ||
438 | - return ret; | ||
439 | - } | ||
440 | - } | ||
441 | - | ||
442 | - // write success, clear and free the buffer | ||
443 | - buffer->free(); | ||
444 | - | ||
445 | - return ret; | ||
446 | - } | ||
447 | -private: | ||
448 | - static char* fill_stuff(char* pes_body_end, char* packet, int body_size, int in_size) | ||
449 | - { | ||
450 | - char* p = pes_body_end; | ||
451 | - | ||
452 | - // insert the stuff bytes before PES body | ||
453 | - int stuff_size = (body_size - in_size); | ||
454 | - | ||
455 | - // adaption_field_control; //2bits | ||
456 | - if (packet[3] & 0x20) { | ||
457 | - // has adaptation | ||
458 | - // packet[4]: adaption_field_length | ||
459 | - // packet[5]: adaption field data | ||
460 | - // base: start of PES body | ||
461 | - char* base = &packet[5] + packet[4]; | ||
462 | - int len = p - base; | ||
463 | - p = (char*)memmove(base + stuff_size, base, len) + len; | ||
464 | - // increase the adaption field size. | ||
465 | - packet[4] += stuff_size; | ||
466 | - | ||
467 | - return p; | ||
468 | - } | ||
469 | - | ||
470 | - // create adaption field. | ||
471 | - // adaption_field_control; //2bits | ||
472 | - packet[3] |= 0x20; | ||
473 | - // base: start of PES body | ||
474 | - char* base = &packet[4]; | ||
475 | - int len = p - base; | ||
476 | - p = (char*)memmove(base + stuff_size, base, len) + len; | ||
477 | - // adaption_field_length; //8bits | ||
478 | - packet[4] = (stuff_size - 1); | ||
479 | - if (stuff_size >= 2) { | ||
480 | - // adaption field flags. | ||
481 | - packet[5] = 0; | ||
482 | - // adaption data. | ||
483 | - if (stuff_size > 2) { | ||
484 | - memset(&packet[6], 0xff, stuff_size - 2); | ||
485 | - } | ||
486 | - } | ||
487 | - | ||
488 | - return p; | ||
489 | - } | ||
490 | - static char* write_pcr(char* p, int64_t pcr) | ||
491 | - { | ||
492 | - *p++ = (char) (pcr >> 25); | ||
493 | - *p++ = (char) (pcr >> 17); | ||
494 | - *p++ = (char) (pcr >> 9); | ||
495 | - *p++ = (char) (pcr >> 1); | ||
496 | - *p++ = (char) (pcr << 7 | 0x7e); | ||
497 | - *p++ = 0; | ||
498 | - | ||
499 | - return p; | ||
500 | - } | ||
501 | - static char* write_pts(char* p, u_int8_t fb, int64_t pts) | ||
502 | - { | ||
503 | - int32_t val; | ||
504 | - | ||
505 | - val = fb << 4 | (((pts >> 30) & 0x07) << 1) | 1; | ||
506 | - *p++ = val; | ||
507 | - | ||
508 | - val = (((pts >> 15) & 0x7fff) << 1) | 1; | ||
509 | - *p++ = (val >> 8); | ||
510 | - *p++ = val; | ||
511 | - | ||
512 | - val = (((pts) & 0x7fff) << 1) | 1; | ||
513 | - *p++ = (val >> 8); | ||
514 | - *p++ = val; | ||
515 | - | ||
516 | - return p; | ||
517 | - } | ||
518 | -}; | ||
519 | - | ||
520 | -// the mpegts header specifed the video/audio pid. | ||
521 | -#define TS_VIDEO_PID 256 | ||
522 | -#define TS_AUDIO_PID 257 | ||
523 | - | ||
524 | -// ts aac stream id. | ||
525 | -#define TS_AUDIO_AAC 0xc0 | ||
526 | -// ts avc stream id. | ||
527 | -#define TS_VIDEO_AVC 0xe0 | ||
528 | - | ||
529 | -SrsTSMuxer::SrsTSMuxer() | ||
530 | -{ | ||
531 | - fd = -1; | ||
532 | - | ||
533 | - audio_buffer = new SrsCodecBuffer(); | ||
534 | - video_buffer = new SrsCodecBuffer(); | ||
535 | - | ||
536 | - audio_frame = new SrsMpegtsFrame(); | ||
537 | - video_frame = new SrsMpegtsFrame(); | ||
538 | -} | ||
539 | - | ||
540 | -SrsTSMuxer::~SrsTSMuxer() | ||
541 | -{ | ||
542 | - close(); | ||
543 | - | ||
544 | - audio_buffer->free(); | ||
545 | - video_buffer->free(); | ||
546 | - | ||
547 | - srs_freep(audio_buffer); | ||
548 | - srs_freep(video_buffer); | ||
549 | - | ||
550 | - srs_freep(audio_frame); | ||
551 | - srs_freep(video_frame); | ||
552 | -} | ||
553 | - | ||
554 | -int SrsTSMuxer::open(std::string _path) | ||
555 | -{ | ||
556 | - int ret = ERROR_SUCCESS; | ||
557 | - | ||
558 | - path = _path; | ||
559 | - | ||
560 | - close(); | ||
561 | - | ||
562 | - int flags = O_CREAT|O_WRONLY|O_TRUNC; | ||
563 | - mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH; | ||
564 | - if ((fd = ::open(path.c_str(), flags, mode)) < 0) { | ||
565 | - ret = ERROR_HLS_OPEN_FAILED; | ||
566 | - srs_error("open ts file %s failed. ret=%d", path.c_str(), ret); | ||
567 | - return ret; | ||
568 | - } | ||
569 | - | ||
570 | - // write mpegts header | ||
571 | - if ((ret = SrsMpegtsWriter::write_header(fd)) != ERROR_SUCCESS) { | ||
572 | - return ret; | ||
573 | - } | ||
574 | - | ||
575 | - return ret; | ||
576 | -} | ||
577 | - | ||
578 | -int SrsTSMuxer::write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample) | ||
579 | -{ | ||
580 | - int ret = ERROR_SUCCESS; | ||
581 | - | ||
582 | - if (!audio_frame) { | ||
583 | - audio_frame = new SrsMpegtsFrame(); | ||
584 | - audio_frame->dts = audio_frame->pts = time * 90; | ||
585 | - audio_frame->pid = TS_AUDIO_PID; | ||
586 | - audio_frame->sid = TS_AUDIO_AAC; | ||
587 | - } | ||
588 | - | ||
589 | - for (int i = 0; i < sample->nb_buffers; i++) { | ||
590 | - SrsCodecBuffer* buf = &sample->buffers[i]; | ||
591 | - int32_t size = buf->size; | ||
592 | - | ||
593 | - if (!buf->bytes || size <= 0 || size > 0x1fff) { | ||
594 | - ret = ERROR_HLS_AAC_FRAME_LENGTH; | ||
595 | - srs_error("invalid aac frame length=%d, ret=%d", size, ret); | ||
596 | - return ret; | ||
597 | - } | ||
598 | - | ||
599 | - // the frame length is the AAC raw data plus the adts header size. | ||
600 | - int32_t frame_length = size + 7; | ||
601 | - | ||
602 | - // AAC-ADTS | ||
603 | - // 6.2 Audio Data Transport Stream, ADTS | ||
604 | - // in aac-iso-13818-7.pdf, page 26. | ||
605 | - // fixed 7bytes header | ||
606 | - static u_int8_t adts_header[7] = {0xff, 0xf1, 0x00, 0x00, 0x00, 0x0f, 0xfc}; | ||
607 | - /* | ||
608 | - // adts_fixed_header | ||
609 | - // 2B, 16bits | ||
610 | - int16_t syncword; //12bits, '1111 1111 1111' | ||
611 | - int8_t ID; //1bit, '0' | ||
612 | - int8_t layer; //2bits, '00' | ||
613 | - int8_t protection_absent; //1bit, can be '1' | ||
614 | - // 12bits | ||
615 | - int8_t profile; //2bit, 7.1 Profiles, page 40 | ||
616 | - TSAacSampleFrequency sampling_frequency_index; //4bits, Table 35, page 46 | ||
617 | - int8_t private_bit; //1bit, can be '0' | ||
618 | - int8_t channel_configuration; //3bits, Table 8 | ||
619 | - int8_t original_or_copy; //1bit, can be '0' | ||
620 | - int8_t home; //1bit, can be '0' | ||
621 | - | ||
622 | - // adts_variable_header | ||
623 | - // 28bits | ||
624 | - int8_t copyright_identification_bit; //1bit, can be '0' | ||
625 | - int8_t copyright_identification_start; //1bit, can be '0' | ||
626 | - int16_t frame_length; //13bits | ||
627 | - int16_t adts_buffer_fullness; //11bits, 7FF signals that the bitstream is a variable rate bitstream. | ||
628 | - int8_t number_of_raw_data_blocks_in_frame; //2bits, 0 indicating 1 raw_data_block() | ||
629 | - */ | ||
630 | - // profile, 2bits | ||
631 | - adts_header[2] = (codec->aac_profile << 6) & 0xc0; | ||
632 | - // sampling_frequency_index 4bits | ||
633 | - adts_header[2] |= (codec->aac_sample_rate << 2) & 0x3c; | ||
634 | - // channel_configuration 3bits | ||
635 | - adts_header[2] |= (codec->aac_channels >> 2) & 0x01; | ||
636 | - adts_header[3] = (codec->aac_channels << 6) & 0xc0; | ||
637 | - // frame_length 13bits | ||
638 | - adts_header[3] |= (frame_length >> 11) & 0x03; | ||
639 | - adts_header[4] = (frame_length >> 3) & 0xff; | ||
640 | - adts_header[5] = ((frame_length << 5) & 0xe0); | ||
641 | - // adts_buffer_fullness; //11bits | ||
642 | - adts_header[5] |= 0x1f; | ||
643 | - | ||
644 | - // copy to audio buffer | ||
645 | - audio_buffer->append(adts_header, sizeof(adts_header)); | ||
646 | - audio_buffer->append(buf->bytes, buf->size); | ||
647 | - } | ||
648 | - | ||
649 | - if ((ret = SrsMpegtsWriter::write_frame(fd, audio_frame, audio_buffer)) != ERROR_SUCCESS) { | ||
650 | - return ret; | ||
651 | - } | ||
652 | - srs_freep(audio_frame); | ||
653 | - | ||
654 | - return ret; | ||
655 | -} | ||
656 | - | ||
657 | -int SrsTSMuxer::write_video(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample) | ||
658 | -{ | ||
659 | - int ret = ERROR_SUCCESS; | ||
660 | - | ||
661 | - video_frame->dts = time * 90; | ||
662 | - video_frame->pts = video_frame->dts + sample->cts * 90; | ||
663 | - video_frame->pid = TS_VIDEO_PID; | ||
664 | - video_frame->sid = TS_VIDEO_AVC; | ||
665 | - video_frame->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame; | ||
666 | - | ||
667 | - static u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 }; | ||
668 | - video_buffer->append(aud_nal, sizeof(aud_nal)); | ||
669 | - | ||
670 | - bool sps_pps_sent = false; | ||
671 | - for (int i = 0; i < sample->nb_buffers; i++) { | ||
672 | - SrsCodecBuffer* buf = &sample->buffers[i]; | ||
673 | - int32_t size = buf->size; | ||
674 | - | ||
675 | - if (!buf->bytes || size <= 0) { | ||
676 | - ret = ERROR_HLS_AVC_SAMPLE_SIZE; | ||
677 | - srs_error("invalid avc sample length=%d, ret=%d", size, ret); | ||
678 | - return ret; | ||
679 | - } | ||
680 | - | ||
681 | - // 5bits, 7.3.1 NAL unit syntax, | ||
682 | - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
683 | - u_int8_t nal_unit_type; | ||
684 | - nal_unit_type = *buf->bytes; | ||
685 | - nal_unit_type &= 0x1f; | ||
686 | - | ||
687 | - // Table 7-1 – NAL unit type codes, page 61 | ||
688 | - // 1: Coded slice | ||
689 | - if (nal_unit_type == 1) { | ||
690 | - sps_pps_sent = false; | ||
691 | - } | ||
692 | - // 5: Coded slice of an IDR picture. | ||
693 | - // insert sps/pps before IDR or key frame is ok. | ||
694 | - if (nal_unit_type == 5 && !sps_pps_sent) { | ||
695 | - //if (video_frame->key && !sps_pps_sent) { | ||
696 | - sps_pps_sent = true; | ||
697 | - | ||
698 | - // ngx_rtmp_hls_append_sps_pps | ||
699 | - if (codec->sequenceParameterSetLength > 0) { | ||
700 | - // AnnexB prefix | ||
701 | - video_buffer->append(aud_nal, 4); | ||
702 | - // sps | ||
703 | - video_buffer->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength); | ||
704 | - } | ||
705 | - if (codec->pictureParameterSetLength > 0) { | ||
706 | - // AnnexB prefix | ||
707 | - video_buffer->append(aud_nal, 4); | ||
708 | - // pps | ||
709 | - video_buffer->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength); | ||
710 | - } | ||
711 | - } | ||
712 | - | ||
713 | - // sample start prefix, '00 00 00 01' or '00 00 01' | ||
714 | - u_int8_t* p = aud_nal + 1; | ||
715 | - u_int8_t* end = p + 3; | ||
716 | - | ||
717 | - // first AnnexB prefix is long (4 bytes) | ||
718 | - if (i == 0) { | ||
719 | - p = aud_nal; | ||
720 | - } | ||
721 | - video_buffer->append(p, end - p); | ||
722 | - | ||
723 | - // sample data | ||
724 | - video_buffer->append(buf->bytes, buf->size); | ||
725 | - } | ||
726 | - | ||
727 | - if ((ret = SrsMpegtsWriter::write_frame(fd, video_frame, video_buffer)) != ERROR_SUCCESS) { | ||
728 | - return ret; | ||
729 | - } | ||
730 | - | ||
731 | - return ret; | ||
732 | -} | ||
733 | - | ||
734 | -void SrsTSMuxer::close() | ||
735 | -{ | ||
736 | - if (fd > 0) { | ||
737 | - ::close(fd); | ||
738 | - fd = -1; | ||
739 | - } | ||
740 | -} | ||
741 | - | 1 | +/* |
2 | +The MIT License (MIT) | ||
3 | + | ||
4 | +Copyright (c) 2013 winlin | ||
5 | + | ||
6 | +Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
7 | +this software and associated documentation files (the "Software"), to deal in | ||
8 | +the Software without restriction, including without limitation the rights to | ||
9 | +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
10 | +the Software, and to permit persons to whom the Software is furnished to do so, | ||
11 | +subject to the following conditions: | ||
12 | + | ||
13 | +The above copyright notice and this permission notice shall be included in all | ||
14 | +copies or substantial portions of the Software. | ||
15 | + | ||
16 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
18 | +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
19 | +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
20 | +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
21 | +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
22 | +*/ | ||
23 | + | ||
24 | +#include <srs_core_hls.hpp> | ||
25 | + | ||
26 | +#include <sys/types.h> | ||
27 | +#include <sys/stat.h> | ||
28 | +#include <fcntl.h> | ||
29 | +#include <stdlib.h> | ||
30 | +#include <string.h> | ||
31 | + | ||
32 | +#include <srs_core_error.hpp> | ||
33 | +#include <srs_core_codec.hpp> | ||
34 | +#include <srs_core_amf0.hpp> | ||
35 | +#include <srs_core_protocol.hpp> | ||
36 | +#include <srs_core_config.hpp> | ||
37 | + | ||
38 | +SrsHLS::SrsHLS() | ||
39 | +{ | ||
40 | + hls_enabled = false; | ||
41 | + codec = new SrsCodec(); | ||
42 | + sample = new SrsCodecSample(); | ||
43 | + muxer = NULL; | ||
44 | +} | ||
45 | + | ||
46 | +SrsHLS::~SrsHLS() | ||
47 | +{ | ||
48 | + srs_freep(codec); | ||
49 | + srs_freep(sample); | ||
50 | + srs_freep(muxer); | ||
51 | +} | ||
52 | + | ||
53 | +int SrsHLS::on_publish(std::string _vhost) | ||
54 | +{ | ||
55 | + int ret = ERROR_SUCCESS; | ||
56 | + | ||
57 | + if (muxer) { | ||
58 | + ret = ERROR_HLS_BUSY; | ||
59 | + srs_error("hls is busy, something error, " | ||
60 | + "vhost=%s, ret=%d", _vhost.c_str(), ret); | ||
61 | + return ret; | ||
62 | + } | ||
63 | + | ||
64 | + vhost = _vhost; | ||
65 | + muxer = new SrsTSMuxer(); | ||
66 | + | ||
67 | + // try to open the HLS muxer | ||
68 | + SrsConfDirective* conf = config->get_hls(vhost); | ||
69 | + if (!conf && conf->arg0() == "off") { | ||
70 | + return ret; | ||
71 | + } | ||
72 | + | ||
73 | + // TODO: check the audio and video, ensure both exsists. | ||
74 | + // for use fixed mpegts header specifeid the audio and video pid. | ||
75 | + | ||
76 | + hls_enabled = true; | ||
77 | + | ||
78 | + std::string path = SRS_CONF_DEFAULT_HLS_PATH; | ||
79 | + if ((conf = config->get_hls_path(vhost)) != NULL) { | ||
80 | + path = conf->arg0(); | ||
81 | + } | ||
82 | + | ||
83 | + // TODO: generate by m3u8 muxer. | ||
84 | + path += "/1.ts"; | ||
85 | + | ||
86 | + if ((ret = muxer->open(path)) != ERROR_SUCCESS) { | ||
87 | + srs_error("open hls muxer failed. ret=%d", ret); | ||
88 | + return ret; | ||
89 | + } | ||
90 | + | ||
91 | + return ret; | ||
92 | +} | ||
93 | + | ||
94 | +void SrsHLS::on_unpublish() | ||
95 | +{ | ||
96 | + hls_enabled = false; | ||
97 | + muxer->close(); | ||
98 | + srs_freep(muxer); | ||
99 | +} | ||
100 | + | ||
101 | +int SrsHLS::on_meta_data(SrsOnMetaDataPacket* metadata) | ||
102 | +{ | ||
103 | + int ret = ERROR_SUCCESS; | ||
104 | + | ||
105 | + if (!metadata || !metadata->metadata) { | ||
106 | + srs_trace("no metadata persent, hls ignored it."); | ||
107 | + return ret; | ||
108 | + } | ||
109 | + | ||
110 | + SrsAmf0Object* obj = metadata->metadata; | ||
111 | + if (obj->size() <= 0) { | ||
112 | + srs_trace("no metadata persent, hls ignored it."); | ||
113 | + return ret; | ||
114 | + } | ||
115 | + | ||
116 | + // finger out the codec info from metadata if possible. | ||
117 | + SrsAmf0Any* prop = NULL; | ||
118 | + | ||
119 | + if ((prop = obj->get_property("duration")) != NULL && prop->is_number()) { | ||
120 | + codec->duration = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
121 | + } | ||
122 | + if ((prop = obj->get_property("width")) != NULL && prop->is_number()) { | ||
123 | + codec->width = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
124 | + } | ||
125 | + if ((prop = obj->get_property("height")) != NULL && prop->is_number()) { | ||
126 | + codec->height = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
127 | + } | ||
128 | + if ((prop = obj->get_property("framerate")) != NULL && prop->is_number()) { | ||
129 | + codec->frame_rate = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
130 | + } | ||
131 | + if ((prop = obj->get_property("videocodecid")) != NULL && prop->is_number()) { | ||
132 | + codec->video_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
133 | + } | ||
134 | + if ((prop = obj->get_property("videodatarate")) != NULL && prop->is_number()) { | ||
135 | + codec->video_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
136 | + } | ||
137 | + | ||
138 | + if ((prop = obj->get_property("audiocodecid")) != NULL && prop->is_number()) { | ||
139 | + codec->audio_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
140 | + } | ||
141 | + if ((prop = obj->get_property("audiodatarate")) != NULL && prop->is_number()) { | ||
142 | + codec->audio_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
143 | + } | ||
144 | + | ||
145 | + // ignore the following, for each flv/rtmp packet contains them: | ||
146 | + // audiosamplerate, sample->sound_rate | ||
147 | + // audiosamplesize, sample->sound_size | ||
148 | + // stereo, sample->sound_type | ||
149 | + | ||
150 | + return ret; | ||
151 | +} | ||
152 | + | ||
153 | +int SrsHLS::on_audio(SrsCommonMessage* audio) | ||
154 | +{ | ||
155 | + int ret = ERROR_SUCCESS; | ||
156 | + | ||
157 | + sample->clear(); | ||
158 | + if ((ret = codec->audio_aac_demux(audio->payload, audio->size, sample)) != ERROR_SUCCESS) { | ||
159 | + return ret; | ||
160 | + } | ||
161 | + | ||
162 | + if (codec->audio_codec_id != SrsCodecAudioAAC) { | ||
163 | + return ret; | ||
164 | + } | ||
165 | + | ||
166 | + // TODO: maybe donot need to demux the aac? | ||
167 | + if (!hls_enabled) { | ||
168 | + return ret; | ||
169 | + } | ||
170 | + | ||
171 | + // ignore sequence header | ||
172 | + if (sample->aac_packet_type == SrsCodecAudioTypeSequenceHeader) { | ||
173 | + return ret; | ||
174 | + } | ||
175 | + | ||
176 | + u_int32_t timestamp = audio->header.timestamp; | ||
177 | + // TODO: correct the timestamp. | ||
178 | + | ||
179 | + if ((ret = muxer->write_audio(timestamp, codec, sample)) != ERROR_SUCCESS) { | ||
180 | + return ret; | ||
181 | + } | ||
182 | + | ||
183 | + return ret; | ||
184 | +} | ||
185 | + | ||
186 | +int SrsHLS::on_video(SrsCommonMessage* video) | ||
187 | +{ | ||
188 | + int ret = ERROR_SUCCESS; | ||
189 | + | ||
190 | + sample->clear(); | ||
191 | + if ((ret = codec->video_avc_demux(video->payload, video->size, sample)) != ERROR_SUCCESS) { | ||
192 | + return ret; | ||
193 | + } | ||
194 | + | ||
195 | + if (codec->video_codec_id != SrsCodecVideoAVC) { | ||
196 | + return ret; | ||
197 | + } | ||
198 | + | ||
199 | + // TODO: maybe donot need to demux the avc? | ||
200 | + if (!hls_enabled) { | ||
201 | + return ret; | ||
202 | + } | ||
203 | + | ||
204 | + // ignore sequence header | ||
205 | + if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) { | ||
206 | + return ret; | ||
207 | + } | ||
208 | + | ||
209 | + u_int32_t timestamp = video->header.timestamp; | ||
210 | + // TODO: correct the timestamp. | ||
211 | + | ||
212 | + if ((ret = muxer->write_video(timestamp, codec, sample)) != ERROR_SUCCESS) { | ||
213 | + return ret; | ||
214 | + } | ||
215 | + | ||
216 | + return ret; | ||
217 | +} | ||
218 | + | ||
219 | +// @see: ngx_rtmp_mpegts_header | ||
220 | +u_int8_t mpegts_header[] = { | ||
221 | + /* TS */ | ||
222 | + 0x47, 0x40, 0x00, 0x10, 0x00, | ||
223 | + /* PSI */ | ||
224 | + 0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00, | ||
225 | + /* PAT */ | ||
226 | + 0x00, 0x01, 0xf0, 0x01, | ||
227 | + /* CRC */ | ||
228 | + 0x2e, 0x70, 0x19, 0x05, | ||
229 | + /* stuffing 167 bytes */ | ||
230 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
231 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
232 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
233 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
234 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
235 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
236 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
237 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
238 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
239 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
240 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
241 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
242 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
243 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
244 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
245 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
246 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
247 | + | ||
248 | + /* TS */ | ||
249 | + 0x47, 0x50, 0x01, 0x10, 0x00, | ||
250 | + /* PSI */ | ||
251 | + 0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00, | ||
252 | + /* PMT */ | ||
253 | + 0xe1, 0x00, | ||
254 | + 0xf0, 0x00, | ||
255 | + 0x1b, 0xe1, 0x00, 0xf0, 0x00, /* h264, pid=0x100=256 */ | ||
256 | + 0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac, pid=0x101=257 */ | ||
257 | + /*0x03, 0xe1, 0x01, 0xf0, 0x00,*/ /* mp3 */ | ||
258 | + /* CRC */ | ||
259 | + 0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */ | ||
260 | + /*0x4e, 0x59, 0x3d, 0x1e,*/ /* crc for mp3 */ | ||
261 | + /* stuffing 157 bytes */ | ||
262 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
263 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
264 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
265 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
266 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
267 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
268 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
269 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
270 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
271 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
272 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
273 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
274 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
275 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
276 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
277 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff | ||
278 | +}; | ||
279 | + | ||
280 | +// @see: NGX_RTMP_HLS_DELAY, | ||
281 | +// 63000: 700ms, ts_tbn=90000 | ||
282 | +#define SRS_HLS_DELAY 63000 | ||
283 | + | ||
284 | +// @see: ngx_rtmp_SrsMpegtsFrame_t | ||
285 | +struct SrsMpegtsFrame | ||
286 | +{ | ||
287 | + int64_t pts; | ||
288 | + int64_t dts; | ||
289 | + int pid; | ||
290 | + int sid; | ||
291 | + int cc; | ||
292 | + bool key; | ||
293 | + | ||
294 | + SrsMpegtsFrame() | ||
295 | + { | ||
296 | + pts = dts = 0; | ||
297 | + pid = sid = cc = 0; | ||
298 | + key = false; | ||
299 | + } | ||
300 | +}; | ||
301 | + | ||
302 | +// @see: ngx_rtmp_mpegts.c | ||
303 | +// TODO: support full mpegts feature in future. | ||
304 | +class SrsMpegtsWriter | ||
305 | +{ | ||
306 | +public: | ||
307 | + static int write_header(int fd) | ||
308 | + { | ||
309 | + int ret = ERROR_SUCCESS; | ||
310 | + | ||
311 | + if (::write(fd, mpegts_header, sizeof(mpegts_header)) != sizeof(mpegts_header)) { | ||
312 | + ret = ERROR_HLS_WRITE_FAILED; | ||
313 | + srs_error("write ts file header failed. ret=%d", ret); | ||
314 | + return ret; | ||
315 | + } | ||
316 | + | ||
317 | + return ret; | ||
318 | + } | ||
319 | + static int write_frame(int fd, SrsMpegtsFrame* frame, SrsCodecBuffer* buffer) | ||
320 | + { | ||
321 | + int ret = ERROR_SUCCESS; | ||
322 | + | ||
323 | + if (!buffer->bytes || buffer->size <= 0) { | ||
324 | + return ret; | ||
325 | + } | ||
326 | + | ||
327 | + char* last = buffer->bytes + buffer->size; | ||
328 | + char* pos = buffer->bytes; | ||
329 | + | ||
330 | + bool first = true; | ||
331 | + while (pos < last) { | ||
332 | + static char packet[188]; | ||
333 | + char* p = packet; | ||
334 | + | ||
335 | + frame->cc++; | ||
336 | + | ||
337 | + // sync_byte; //8bits | ||
338 | + *p++ = 0x47; | ||
339 | + // pid; //13bits | ||
340 | + *p++ = (frame->pid >> 8) & 0x1f; | ||
341 | + // payload_unit_start_indicator; //1bit | ||
342 | + if (first) { | ||
343 | + p[-1] |= 0x40; | ||
344 | + } | ||
345 | + *p++ = frame->pid; | ||
346 | + | ||
347 | + // transport_scrambling_control; //2bits | ||
348 | + // adaption_field_control; //2bits, 0x01: PayloadOnly | ||
349 | + // continuity_counter; //4bits | ||
350 | + *p++ = 0x10 | (frame->cc & 0x0f); | ||
351 | + | ||
352 | + if (first) { | ||
353 | + first = false; | ||
354 | + if (frame->key) { | ||
355 | + p[-1] |= 0x20; // Both Adaption and Payload | ||
356 | + *p++ = 7; // size | ||
357 | + *p++ = 0x50; // random access + PCR | ||
358 | + p = write_pcr(p, frame->dts - SRS_HLS_DELAY); | ||
359 | + } | ||
360 | + | ||
361 | + // PES header | ||
362 | + // packet_start_code_prefix; //24bits, '00 00 01' | ||
363 | + *p++ = 0x00; | ||
364 | + *p++ = 0x00; | ||
365 | + *p++ = 0x01; | ||
366 | + //8bits | ||
367 | + *p++ = frame->sid; | ||
368 | + | ||
369 | + // pts(33bits) need 5bytes. | ||
370 | + u_int8_t header_size = 5; | ||
371 | + u_int8_t flags = 0x80; // pts | ||
372 | + | ||
373 | + // dts(33bits) need 5bytes also | ||
374 | + if (frame->dts != frame->pts) { | ||
375 | + header_size += 5; | ||
376 | + flags |= 0x40; // dts | ||
377 | + } | ||
378 | + | ||
379 | + // 3bytes: flag fields from PES_packet_length to PES_header_data_length | ||
380 | + int pes_size = (last - pos) + header_size + 3; | ||
381 | + if (pes_size > 0xffff) { | ||
382 | + /** | ||
383 | + * when actual packet length > 0xffff(65535), | ||
384 | + * which exceed the max u_int16_t packet length, | ||
385 | + * use 0 packet length, the next unit start indicates the end of packet. | ||
386 | + */ | ||
387 | + pes_size = 0; | ||
388 | + } | ||
389 | + | ||
390 | + // PES_packet_length; //16bits | ||
391 | + *p++ = (pes_size >> 8); | ||
392 | + *p++ = pes_size; | ||
393 | + | ||
394 | + // PES_scrambling_control; //2bits, '10' | ||
395 | + // PES_priority; //1bit | ||
396 | + // data_alignment_indicator; //1bit | ||
397 | + // copyright; //1bit | ||
398 | + // original_or_copy; //1bit | ||
399 | + *p++ = 0x80; /* H222 */ | ||
400 | + | ||
401 | + // PTS_DTS_flags; //2bits | ||
402 | + // ESCR_flag; //1bit | ||
403 | + // ES_rate_flag; //1bit | ||
404 | + // DSM_trick_mode_flag; //1bit | ||
405 | + // additional_copy_info_flag; //1bit | ||
406 | + // PES_CRC_flag; //1bit | ||
407 | + // PES_extension_flag; //1bit | ||
408 | + *p++ = flags; | ||
409 | + | ||
410 | + // PES_header_data_length; //8bits | ||
411 | + *p++ = header_size; | ||
412 | + | ||
413 | + // pts; // 33bits | ||
414 | + p = write_pts(p, flags >> 6, frame->pts + SRS_HLS_DELAY); | ||
415 | + | ||
416 | + // dts; // 33bits | ||
417 | + if (frame->dts != frame->pts) { | ||
418 | + p = write_pts(p, 1, frame->dts + SRS_HLS_DELAY); | ||
419 | + } | ||
420 | + } | ||
421 | + | ||
422 | + int body_size = sizeof(packet) - (p - packet); | ||
423 | + int in_size = last - pos; | ||
424 | + | ||
425 | + if (body_size <= in_size) { | ||
426 | + memcpy(p, pos, body_size); | ||
427 | + pos += body_size; | ||
428 | + } else { | ||
429 | + p = fill_stuff(p, packet, body_size, in_size); | ||
430 | + memcpy(p, pos, in_size); | ||
431 | + pos = last; | ||
432 | + } | ||
433 | + | ||
434 | + // write ts packet | ||
435 | + if (::write(fd, packet, sizeof(packet)) != sizeof(packet)) { | ||
436 | + ret = ERROR_HLS_WRITE_FAILED; | ||
437 | + srs_error("write ts file failed. ret=%d", ret); | ||
438 | + return ret; | ||
439 | + } | ||
440 | + } | ||
441 | + | ||
442 | + // write success, clear and free the buffer | ||
443 | + buffer->free(); | ||
444 | + | ||
445 | + return ret; | ||
446 | + } | ||
447 | +private: | ||
448 | + static char* fill_stuff(char* pes_body_end, char* packet, int body_size, int in_size) | ||
449 | + { | ||
450 | + char* p = pes_body_end; | ||
451 | + | ||
452 | + // insert the stuff bytes before PES body | ||
453 | + int stuff_size = (body_size - in_size); | ||
454 | + | ||
455 | + // adaption_field_control; //2bits | ||
456 | + if (packet[3] & 0x20) { | ||
457 | + // has adaptation | ||
458 | + // packet[4]: adaption_field_length | ||
459 | + // packet[5]: adaption field data | ||
460 | + // base: start of PES body | ||
461 | + char* base = &packet[5] + packet[4]; | ||
462 | + int len = p - base; | ||
463 | + p = (char*)memmove(base + stuff_size, base, len) + len; | ||
464 | + // increase the adaption field size. | ||
465 | + packet[4] += stuff_size; | ||
466 | + | ||
467 | + return p; | ||
468 | + } | ||
469 | + | ||
470 | + // create adaption field. | ||
471 | + // adaption_field_control; //2bits | ||
472 | + packet[3] |= 0x20; | ||
473 | + // base: start of PES body | ||
474 | + char* base = &packet[4]; | ||
475 | + int len = p - base; | ||
476 | + p = (char*)memmove(base + stuff_size, base, len) + len; | ||
477 | + // adaption_field_length; //8bits | ||
478 | + packet[4] = (stuff_size - 1); | ||
479 | + if (stuff_size >= 2) { | ||
480 | + // adaption field flags. | ||
481 | + packet[5] = 0; | ||
482 | + // adaption data. | ||
483 | + if (stuff_size > 2) { | ||
484 | + memset(&packet[6], 0xff, stuff_size - 2); | ||
485 | + } | ||
486 | + } | ||
487 | + | ||
488 | + return p; | ||
489 | + } | ||
490 | + static char* write_pcr(char* p, int64_t pcr) | ||
491 | + { | ||
492 | + *p++ = (char) (pcr >> 25); | ||
493 | + *p++ = (char) (pcr >> 17); | ||
494 | + *p++ = (char) (pcr >> 9); | ||
495 | + *p++ = (char) (pcr >> 1); | ||
496 | + *p++ = (char) (pcr << 7 | 0x7e); | ||
497 | + *p++ = 0; | ||
498 | + | ||
499 | + return p; | ||
500 | + } | ||
501 | + static char* write_pts(char* p, u_int8_t fb, int64_t pts) | ||
502 | + { | ||
503 | + int32_t val; | ||
504 | + | ||
505 | + val = fb << 4 | (((pts >> 30) & 0x07) << 1) | 1; | ||
506 | + *p++ = val; | ||
507 | + | ||
508 | + val = (((pts >> 15) & 0x7fff) << 1) | 1; | ||
509 | + *p++ = (val >> 8); | ||
510 | + *p++ = val; | ||
511 | + | ||
512 | + val = (((pts) & 0x7fff) << 1) | 1; | ||
513 | + *p++ = (val >> 8); | ||
514 | + *p++ = val; | ||
515 | + | ||
516 | + return p; | ||
517 | + } | ||
518 | +}; | ||
519 | + | ||
520 | +// the mpegts header specifed the video/audio pid. | ||
521 | +#define TS_VIDEO_PID 256 | ||
522 | +#define TS_AUDIO_PID 257 | ||
523 | + | ||
524 | +// ts aac stream id. | ||
525 | +#define TS_AUDIO_AAC 0xc0 | ||
526 | +// ts avc stream id. | ||
527 | +#define TS_VIDEO_AVC 0xe0 | ||
528 | + | ||
529 | +SrsTSMuxer::SrsTSMuxer() | ||
530 | +{ | ||
531 | + fd = -1; | ||
532 | + | ||
533 | + audio_buffer = new SrsCodecBuffer(); | ||
534 | + video_buffer = new SrsCodecBuffer(); | ||
535 | + | ||
536 | + audio_frame = new SrsMpegtsFrame(); | ||
537 | + video_frame = new SrsMpegtsFrame(); | ||
538 | +} | ||
539 | + | ||
540 | +SrsTSMuxer::~SrsTSMuxer() | ||
541 | +{ | ||
542 | + close(); | ||
543 | + | ||
544 | + audio_buffer->free(); | ||
545 | + video_buffer->free(); | ||
546 | + | ||
547 | + srs_freep(audio_buffer); | ||
548 | + srs_freep(video_buffer); | ||
549 | + | ||
550 | + srs_freep(audio_frame); | ||
551 | + srs_freep(video_frame); | ||
552 | +} | ||
553 | + | ||
554 | +int SrsTSMuxer::open(std::string _path) | ||
555 | +{ | ||
556 | + int ret = ERROR_SUCCESS; | ||
557 | + | ||
558 | + path = _path; | ||
559 | + | ||
560 | + close(); | ||
561 | + | ||
562 | + int flags = O_CREAT|O_WRONLY|O_TRUNC; | ||
563 | + mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH; | ||
564 | + if ((fd = ::open(path.c_str(), flags, mode)) < 0) { | ||
565 | + ret = ERROR_HLS_OPEN_FAILED; | ||
566 | + srs_error("open ts file %s failed. ret=%d", path.c_str(), ret); | ||
567 | + return ret; | ||
568 | + } | ||
569 | + | ||
570 | + // write mpegts header | ||
571 | + if ((ret = SrsMpegtsWriter::write_header(fd)) != ERROR_SUCCESS) { | ||
572 | + return ret; | ||
573 | + } | ||
574 | + | ||
575 | + return ret; | ||
576 | +} | ||
577 | + | ||
578 | +int SrsTSMuxer::write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample) | ||
579 | +{ | ||
580 | + int ret = ERROR_SUCCESS; | ||
581 | + | ||
582 | + audio_frame->dts = audio_frame->pts = time * 90; | ||
583 | + audio_frame->pid = TS_AUDIO_PID; | ||
584 | + audio_frame->sid = TS_AUDIO_AAC; | ||
585 | + | ||
586 | + for (int i = 0; i < sample->nb_buffers; i++) { | ||
587 | + SrsCodecBuffer* buf = &sample->buffers[i]; | ||
588 | + int32_t size = buf->size; | ||
589 | + | ||
590 | + if (!buf->bytes || size <= 0 || size > 0x1fff) { | ||
591 | + ret = ERROR_HLS_AAC_FRAME_LENGTH; | ||
592 | + srs_error("invalid aac frame length=%d, ret=%d", size, ret); | ||
593 | + return ret; | ||
594 | + } | ||
595 | + | ||
596 | + // the frame length is the AAC raw data plus the adts header size. | ||
597 | + int32_t frame_length = size + 7; | ||
598 | + | ||
599 | + // AAC-ADTS | ||
600 | + // 6.2 Audio Data Transport Stream, ADTS | ||
601 | + // in aac-iso-13818-7.pdf, page 26. | ||
602 | + // fixed 7bytes header | ||
603 | + static u_int8_t adts_header[7] = {0xff, 0xf1, 0x00, 0x00, 0x00, 0x0f, 0xfc}; | ||
604 | + /* | ||
605 | + // adts_fixed_header | ||
606 | + // 2B, 16bits | ||
607 | + int16_t syncword; //12bits, '1111 1111 1111' | ||
608 | + int8_t ID; //1bit, '0' | ||
609 | + int8_t layer; //2bits, '00' | ||
610 | + int8_t protection_absent; //1bit, can be '1' | ||
611 | + // 12bits | ||
612 | + int8_t profile; //2bit, 7.1 Profiles, page 40 | ||
613 | + TSAacSampleFrequency sampling_frequency_index; //4bits, Table 35, page 46 | ||
614 | + int8_t private_bit; //1bit, can be '0' | ||
615 | + int8_t channel_configuration; //3bits, Table 8 | ||
616 | + int8_t original_or_copy; //1bit, can be '0' | ||
617 | + int8_t home; //1bit, can be '0' | ||
618 | + | ||
619 | + // adts_variable_header | ||
620 | + // 28bits | ||
621 | + int8_t copyright_identification_bit; //1bit, can be '0' | ||
622 | + int8_t copyright_identification_start; //1bit, can be '0' | ||
623 | + int16_t frame_length; //13bits | ||
624 | + int16_t adts_buffer_fullness; //11bits, 7FF signals that the bitstream is a variable rate bitstream. | ||
625 | + int8_t number_of_raw_data_blocks_in_frame; //2bits, 0 indicating 1 raw_data_block() | ||
626 | + */ | ||
627 | + // profile, 2bits | ||
628 | + adts_header[2] = (codec->aac_profile << 6) & 0xc0; | ||
629 | + // sampling_frequency_index 4bits | ||
630 | + adts_header[2] |= (codec->aac_sample_rate << 2) & 0x3c; | ||
631 | + // channel_configuration 3bits | ||
632 | + adts_header[2] |= (codec->aac_channels >> 2) & 0x01; | ||
633 | + adts_header[3] = (codec->aac_channels << 6) & 0xc0; | ||
634 | + // frame_length 13bits | ||
635 | + adts_header[3] |= (frame_length >> 11) & 0x03; | ||
636 | + adts_header[4] = (frame_length >> 3) & 0xff; | ||
637 | + adts_header[5] = ((frame_length << 5) & 0xe0); | ||
638 | + // adts_buffer_fullness; //11bits | ||
639 | + adts_header[5] |= 0x1f; | ||
640 | + | ||
641 | + // copy to audio buffer | ||
642 | + audio_buffer->append(adts_header, sizeof(adts_header)); | ||
643 | + audio_buffer->append(buf->bytes, buf->size); | ||
644 | + } | ||
645 | + | ||
646 | + if ((ret = SrsMpegtsWriter::write_frame(fd, audio_frame, audio_buffer)) != ERROR_SUCCESS) { | ||
647 | + return ret; | ||
648 | + } | ||
649 | + | ||
650 | + return ret; | ||
651 | +} | ||
652 | + | ||
653 | +int SrsTSMuxer::write_video(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample) | ||
654 | +{ | ||
655 | + int ret = ERROR_SUCCESS; | ||
656 | + | ||
657 | + video_frame->dts = time * 90; | ||
658 | + video_frame->pts = video_frame->dts + sample->cts * 90; | ||
659 | + video_frame->pid = TS_VIDEO_PID; | ||
660 | + video_frame->sid = TS_VIDEO_AVC; | ||
661 | + video_frame->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame; | ||
662 | + | ||
663 | + static u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 }; | ||
664 | + video_buffer->append(aud_nal, sizeof(aud_nal)); | ||
665 | + | ||
666 | + bool sps_pps_sent = false; | ||
667 | + for (int i = 0; i < sample->nb_buffers; i++) { | ||
668 | + SrsCodecBuffer* buf = &sample->buffers[i]; | ||
669 | + int32_t size = buf->size; | ||
670 | + | ||
671 | + if (!buf->bytes || size <= 0) { | ||
672 | + ret = ERROR_HLS_AVC_SAMPLE_SIZE; | ||
673 | + srs_error("invalid avc sample length=%d, ret=%d", size, ret); | ||
674 | + return ret; | ||
675 | + } | ||
676 | + | ||
677 | + // 5bits, 7.3.1 NAL unit syntax, | ||
678 | + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
679 | + u_int8_t nal_unit_type; | ||
680 | + nal_unit_type = *buf->bytes; | ||
681 | + nal_unit_type &= 0x1f; | ||
682 | + | ||
683 | + // Table 7-1 – NAL unit type codes, page 61 | ||
684 | + // 1: Coded slice | ||
685 | + if (nal_unit_type == 1) { | ||
686 | + sps_pps_sent = false; | ||
687 | + } | ||
688 | + // 5: Coded slice of an IDR picture. | ||
689 | + // insert sps/pps before IDR or key frame is ok. | ||
690 | + if (nal_unit_type == 5 && !sps_pps_sent) { | ||
691 | + //if (video_frame->key && !sps_pps_sent) { | ||
692 | + sps_pps_sent = true; | ||
693 | + | ||
694 | + // ngx_rtmp_hls_append_sps_pps | ||
695 | + if (codec->sequenceParameterSetLength > 0) { | ||
696 | + // AnnexB prefix | ||
697 | + video_buffer->append(aud_nal, 4); | ||
698 | + // sps | ||
699 | + video_buffer->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength); | ||
700 | + } | ||
701 | + if (codec->pictureParameterSetLength > 0) { | ||
702 | + // AnnexB prefix | ||
703 | + video_buffer->append(aud_nal, 4); | ||
704 | + // pps | ||
705 | + video_buffer->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength); | ||
706 | + } | ||
707 | + } | ||
708 | + | ||
709 | + // sample start prefix, '00 00 00 01' or '00 00 01' | ||
710 | + u_int8_t* p = aud_nal + 1; | ||
711 | + u_int8_t* end = p + 3; | ||
712 | + | ||
713 | + // first AnnexB prefix is long (4 bytes) | ||
714 | + if (i == 0) { | ||
715 | + p = aud_nal; | ||
716 | + } | ||
717 | + video_buffer->append(p, end - p); | ||
718 | + | ||
719 | + // sample data | ||
720 | + video_buffer->append(buf->bytes, buf->size); | ||
721 | + } | ||
722 | + | ||
723 | + if ((ret = SrsMpegtsWriter::write_frame(fd, video_frame, video_buffer)) != ERROR_SUCCESS) { | ||
724 | + return ret; | ||
725 | + } | ||
726 | + | ||
727 | + return ret; | ||
728 | +} | ||
729 | + | ||
730 | +void SrsTSMuxer::close() | ||
731 | +{ | ||
732 | + if (fd > 0) { | ||
733 | + ::close(fd); | ||
734 | + fd = -1; | ||
735 | + } | ||
736 | +} | ||
737 | + |
-
请 注册 或 登录 后发表评论