正在显示
5 个修改的文件
包含
449 行增加
和
227 行删除
@@ -332,7 +332,7 @@ int SrsClient::publish(SrsSource* source, bool is_fmle) | @@ -332,7 +332,7 @@ int SrsClient::publish(SrsSource* source, bool is_fmle) | ||
332 | SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER); | 332 | SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER); |
333 | 333 | ||
334 | // notify the hls to prepare when publish start. | 334 | // notify the hls to prepare when publish start. |
335 | - if ((ret = source->on_publish(req->vhost)) != ERROR_SUCCESS) { | 335 | + if ((ret = source->on_publish(req->vhost, req->stream)) != ERROR_SUCCESS) { |
336 | srs_error("hls on_publish failed. ret=%d", ret); | 336 | srs_error("hls on_publish failed. ret=%d", ret); |
337 | return ret; | 337 | return ret; |
338 | } | 338 | } |
@@ -37,194 +37,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -37,194 +37,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
37 | #include <srs_core_source.hpp> | 37 | #include <srs_core_source.hpp> |
38 | #include <srs_core_autofree.hpp> | 38 | #include <srs_core_autofree.hpp> |
39 | 39 | ||
40 | -SrsHLS::SrsHLS() | ||
41 | -{ | ||
42 | - hls_enabled = false; | ||
43 | - codec = new SrsCodec(); | ||
44 | - sample = new SrsCodecSample(); | ||
45 | - muxer = NULL; | ||
46 | - jitter = new SrsRtmpJitter(); | ||
47 | -} | ||
48 | - | ||
49 | -SrsHLS::~SrsHLS() | ||
50 | -{ | ||
51 | - srs_freep(codec); | ||
52 | - srs_freep(sample); | ||
53 | - srs_freep(muxer); | ||
54 | - srs_freep(jitter); | ||
55 | -} | ||
56 | - | ||
57 | -int SrsHLS::on_publish(std::string _vhost) | ||
58 | -{ | ||
59 | - int ret = ERROR_SUCCESS; | ||
60 | - | ||
61 | - // TODO: check config. | ||
62 | - if (muxer) { | ||
63 | - hls_enabled = true; | ||
64 | - srs_trace("hls is reopen, continue streaming HLS, vhost=%s", _vhost.c_str()); | ||
65 | - return ret; | ||
66 | - } | ||
67 | - | ||
68 | - vhost = _vhost; | ||
69 | - muxer = new SrsTSMuxer(); | ||
70 | - | ||
71 | - // try to open the HLS muxer | ||
72 | - SrsConfDirective* conf = config->get_hls(vhost); | ||
73 | - if (!conf && conf->arg0() == "off") { | ||
74 | - return ret; | ||
75 | - } | ||
76 | - | ||
77 | - // TODO: check the audio and video, ensure both exsists. | ||
78 | - // for use fixed mpegts header specifeid the audio and video pid. | ||
79 | - | ||
80 | - hls_enabled = true; | ||
81 | - | ||
82 | - std::string path = SRS_CONF_DEFAULT_HLS_PATH; | ||
83 | - if ((conf = config->get_hls_path(vhost)) != NULL) { | ||
84 | - path = conf->arg0(); | ||
85 | - } | ||
86 | - | ||
87 | - // TODO: generate by m3u8 muxer. | ||
88 | - path += "/1.ts"; | ||
89 | - | ||
90 | - if ((ret = muxer->open(path)) != ERROR_SUCCESS) { | ||
91 | - srs_error("open hls muxer failed. ret=%d", ret); | ||
92 | - return ret; | ||
93 | - } | ||
94 | - | ||
95 | - return ret; | ||
96 | -} | ||
97 | - | ||
98 | -void SrsHLS::on_unpublish() | ||
99 | -{ | ||
100 | - hls_enabled = false; | ||
101 | - //muxer->close(); | ||
102 | - //srs_freep(muxer); | ||
103 | -} | ||
104 | - | ||
105 | -int SrsHLS::on_meta_data(SrsOnMetaDataPacket* metadata) | ||
106 | -{ | ||
107 | - int ret = ERROR_SUCCESS; | ||
108 | - | ||
109 | - if (!metadata || !metadata->metadata) { | ||
110 | - srs_trace("no metadata persent, hls ignored it."); | ||
111 | - return ret; | ||
112 | - } | ||
113 | - | ||
114 | - SrsAmf0Object* obj = metadata->metadata; | ||
115 | - if (obj->size() <= 0) { | ||
116 | - srs_trace("no metadata persent, hls ignored it."); | ||
117 | - return ret; | ||
118 | - } | ||
119 | - | ||
120 | - // finger out the codec info from metadata if possible. | ||
121 | - SrsAmf0Any* prop = NULL; | ||
122 | - | ||
123 | - if ((prop = obj->get_property("duration")) != NULL && prop->is_number()) { | ||
124 | - codec->duration = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
125 | - } | ||
126 | - if ((prop = obj->get_property("width")) != NULL && prop->is_number()) { | ||
127 | - codec->width = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
128 | - } | ||
129 | - if ((prop = obj->get_property("height")) != NULL && prop->is_number()) { | ||
130 | - codec->height = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
131 | - } | ||
132 | - if ((prop = obj->get_property("framerate")) != NULL && prop->is_number()) { | ||
133 | - codec->frame_rate = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
134 | - } | ||
135 | - if ((prop = obj->get_property("videocodecid")) != NULL && prop->is_number()) { | ||
136 | - codec->video_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
137 | - } | ||
138 | - if ((prop = obj->get_property("videodatarate")) != NULL && prop->is_number()) { | ||
139 | - codec->video_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
140 | - } | ||
141 | - | ||
142 | - if ((prop = obj->get_property("audiocodecid")) != NULL && prop->is_number()) { | ||
143 | - codec->audio_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
144 | - } | ||
145 | - if ((prop = obj->get_property("audiodatarate")) != NULL && prop->is_number()) { | ||
146 | - codec->audio_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
147 | - } | ||
148 | - | ||
149 | - // ignore the following, for each flv/rtmp packet contains them: | ||
150 | - // audiosamplerate, sample->sound_rate | ||
151 | - // audiosamplesize, sample->sound_size | ||
152 | - // stereo, sample->sound_type | ||
153 | - | ||
154 | - return ret; | ||
155 | -} | 40 | +// @see: NGX_RTMP_HLS_DELAY, |
41 | +// 63000: 700ms, ts_tbn=90000 | ||
42 | +#define SRS_HLS_DELAY 63000 | ||
156 | 43 | ||
157 | -int SrsHLS::on_audio(SrsSharedPtrMessage* audio) | ||
158 | -{ | ||
159 | - int ret = ERROR_SUCCESS; | ||
160 | - | ||
161 | - SrsAutoFree(SrsSharedPtrMessage, audio, false); | ||
162 | - | ||
163 | - sample->clear(); | ||
164 | - if ((ret = codec->audio_aac_demux(audio->payload, audio->size, sample)) != ERROR_SUCCESS) { | ||
165 | - return ret; | ||
166 | - } | ||
167 | - | ||
168 | - if (codec->audio_codec_id != SrsCodecAudioAAC) { | ||
169 | - return ret; | ||
170 | - } | ||
171 | - | ||
172 | - // TODO: maybe donot need to demux the aac? | ||
173 | - if (!hls_enabled) { | ||
174 | - return ret; | ||
175 | - } | ||
176 | - | ||
177 | - // ignore sequence header | ||
178 | - if (sample->aac_packet_type == SrsCodecAudioTypeSequenceHeader) { | ||
179 | - return ret; | ||
180 | - } | ||
181 | - | ||
182 | - if ((ret = jitter->correct(audio, 0, 0)) != ERROR_SUCCESS) { | ||
183 | - return ret; | ||
184 | - } | ||
185 | - | ||
186 | - if ((ret = muxer->write_audio(audio->header.timestamp, codec, sample)) != ERROR_SUCCESS) { | ||
187 | - return ret; | ||
188 | - } | ||
189 | - | ||
190 | - return ret; | ||
191 | -} | 44 | +// the mpegts header specifed the video/audio pid. |
45 | +#define TS_VIDEO_PID 256 | ||
46 | +#define TS_AUDIO_PID 257 | ||
192 | 47 | ||
193 | -int SrsHLS::on_video(SrsSharedPtrMessage* video) | ||
194 | -{ | ||
195 | - int ret = ERROR_SUCCESS; | ||
196 | - | ||
197 | - SrsAutoFree(SrsSharedPtrMessage, video, false); | ||
198 | - | ||
199 | - sample->clear(); | ||
200 | - if ((ret = codec->video_avc_demux(video->payload, video->size, sample)) != ERROR_SUCCESS) { | ||
201 | - return ret; | ||
202 | - } | ||
203 | - | ||
204 | - if (codec->video_codec_id != SrsCodecVideoAVC) { | ||
205 | - return ret; | ||
206 | - } | ||
207 | - | ||
208 | - // TODO: maybe donot need to demux the avc? | ||
209 | - if (!hls_enabled) { | ||
210 | - return ret; | ||
211 | - } | ||
212 | - | ||
213 | - // ignore sequence header | ||
214 | - if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) { | ||
215 | - return ret; | ||
216 | - } | ||
217 | - | ||
218 | - if ((ret = jitter->correct(video, 0, 0)) != ERROR_SUCCESS) { | ||
219 | - return ret; | ||
220 | - } | ||
221 | - | ||
222 | - if ((ret = muxer->write_video(video->header.timestamp, codec, sample)) != ERROR_SUCCESS) { | ||
223 | - return ret; | ||
224 | - } | ||
225 | - | ||
226 | - return ret; | ||
227 | -} | 48 | +// ts aac stream id. |
49 | +#define TS_AUDIO_AAC 0xc0 | ||
50 | +// ts avc stream id. | ||
51 | +#define TS_VIDEO_AVC 0xe0 | ||
228 | 52 | ||
229 | // @see: ngx_rtmp_mpegts_header | 53 | // @see: ngx_rtmp_mpegts_header |
230 | u_int8_t mpegts_header[] = { | 54 | u_int8_t mpegts_header[] = { |
@@ -287,10 +111,6 @@ u_int8_t mpegts_header[] = { | @@ -287,10 +111,6 @@ u_int8_t mpegts_header[] = { | ||
287 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff | 111 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff |
288 | }; | 112 | }; |
289 | 113 | ||
290 | -// @see: NGX_RTMP_HLS_DELAY, | ||
291 | -// 63000: 700ms, ts_tbn=90000 | ||
292 | -#define SRS_HLS_DELAY 63000 | ||
293 | - | ||
294 | // @see: ngx_rtmp_SrsMpegtsFrame_t | 114 | // @see: ngx_rtmp_SrsMpegtsFrame_t |
295 | struct SrsMpegtsFrame | 115 | struct SrsMpegtsFrame |
296 | { | 116 | { |
@@ -527,18 +347,29 @@ private: | @@ -527,18 +347,29 @@ private: | ||
527 | } | 347 | } |
528 | }; | 348 | }; |
529 | 349 | ||
530 | -// the mpegts header specifed the video/audio pid. | ||
531 | -#define TS_VIDEO_PID 256 | ||
532 | -#define TS_AUDIO_PID 257 | 350 | +SrsM3u8Segment::SrsM3u8Segment() |
351 | +{ | ||
352 | + duration = 0; | ||
353 | + sequence_no = 0; | ||
354 | + muxer = new SrsTSMuxer(); | ||
355 | + segment_start_dts = 0; | ||
356 | +} | ||
533 | 357 | ||
534 | -// ts aac stream id. | ||
535 | -#define TS_AUDIO_AAC 0xc0 | ||
536 | -// ts avc stream id. | ||
537 | -#define TS_VIDEO_AVC 0xe0 | 358 | +SrsM3u8Segment::~SrsM3u8Segment() |
359 | +{ | ||
360 | + muxer->close(); | ||
361 | + srs_freep(muxer); | ||
362 | +} | ||
538 | 363 | ||
539 | -SrsTSMuxer::SrsTSMuxer() | 364 | +SrsHLS::SrsHLS() |
540 | { | 365 | { |
541 | - fd = -1; | 366 | + hls_enabled = false; |
367 | + codec = new SrsCodec(); | ||
368 | + sample = new SrsCodecSample(); | ||
369 | + current = NULL; | ||
370 | + jitter = new SrsRtmpJitter(); | ||
371 | + file_index = 0; | ||
372 | + m3u8_dts = stream_dts = 0; | ||
542 | 373 | ||
543 | audio_buffer = new SrsCodecBuffer(); | 374 | audio_buffer = new SrsCodecBuffer(); |
544 | video_buffer = new SrsCodecBuffer(); | 375 | video_buffer = new SrsCodecBuffer(); |
@@ -547,9 +378,20 @@ SrsTSMuxer::SrsTSMuxer() | @@ -547,9 +378,20 @@ SrsTSMuxer::SrsTSMuxer() | ||
547 | video_frame = new SrsMpegtsFrame(); | 378 | video_frame = new SrsMpegtsFrame(); |
548 | } | 379 | } |
549 | 380 | ||
550 | -SrsTSMuxer::~SrsTSMuxer() | 381 | +SrsHLS::~SrsHLS() |
551 | { | 382 | { |
552 | - close(); | 383 | + srs_freep(codec); |
384 | + srs_freep(sample); | ||
385 | + srs_freep(jitter); | ||
386 | + | ||
387 | + std::vector<SrsM3u8Segment*>::iterator it; | ||
388 | + for (it = segments.begin(); it != segments.end(); ++it) { | ||
389 | + SrsM3u8Segment* segment = *it; | ||
390 | + srs_freep(segment); | ||
391 | + } | ||
392 | + segments.clear(); | ||
393 | + | ||
394 | + srs_freep(current); | ||
553 | 395 | ||
554 | audio_buffer->free(); | 396 | audio_buffer->free(); |
555 | video_buffer->free(); | 397 | video_buffer->free(); |
@@ -561,6 +403,346 @@ SrsTSMuxer::~SrsTSMuxer() | @@ -561,6 +403,346 @@ SrsTSMuxer::~SrsTSMuxer() | ||
561 | srs_freep(video_frame); | 403 | srs_freep(video_frame); |
562 | } | 404 | } |
563 | 405 | ||
406 | +int SrsHLS::on_publish(std::string _vhost, std::string _stream) | ||
407 | +{ | ||
408 | + int ret = ERROR_SUCCESS; | ||
409 | + | ||
410 | + vhost = _vhost; | ||
411 | + stream = _stream; | ||
412 | + | ||
413 | + if ((ret = reopen()) != ERROR_SUCCESS) { | ||
414 | + return ret; | ||
415 | + } | ||
416 | + | ||
417 | + return ret; | ||
418 | +} | ||
419 | + | ||
420 | +void SrsHLS::on_unpublish() | ||
421 | +{ | ||
422 | + hls_enabled = false; | ||
423 | +} | ||
424 | + | ||
425 | +int SrsHLS::on_meta_data(SrsOnMetaDataPacket* metadata) | ||
426 | +{ | ||
427 | + int ret = ERROR_SUCCESS; | ||
428 | + | ||
429 | + if (!metadata || !metadata->metadata) { | ||
430 | + srs_trace("no metadata persent, hls ignored it."); | ||
431 | + return ret; | ||
432 | + } | ||
433 | + | ||
434 | + SrsAmf0Object* obj = metadata->metadata; | ||
435 | + if (obj->size() <= 0) { | ||
436 | + srs_trace("no metadata persent, hls ignored it."); | ||
437 | + return ret; | ||
438 | + } | ||
439 | + | ||
440 | + // finger out the codec info from metadata if possible. | ||
441 | + SrsAmf0Any* prop = NULL; | ||
442 | + | ||
443 | + if ((prop = obj->get_property("duration")) != NULL && prop->is_number()) { | ||
444 | + codec->duration = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
445 | + } | ||
446 | + if ((prop = obj->get_property("width")) != NULL && prop->is_number()) { | ||
447 | + codec->width = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
448 | + } | ||
449 | + if ((prop = obj->get_property("height")) != NULL && prop->is_number()) { | ||
450 | + codec->height = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
451 | + } | ||
452 | + if ((prop = obj->get_property("framerate")) != NULL && prop->is_number()) { | ||
453 | + codec->frame_rate = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
454 | + } | ||
455 | + if ((prop = obj->get_property("videocodecid")) != NULL && prop->is_number()) { | ||
456 | + codec->video_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
457 | + } | ||
458 | + if ((prop = obj->get_property("videodatarate")) != NULL && prop->is_number()) { | ||
459 | + codec->video_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
460 | + } | ||
461 | + | ||
462 | + if ((prop = obj->get_property("audiocodecid")) != NULL && prop->is_number()) { | ||
463 | + codec->audio_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
464 | + } | ||
465 | + if ((prop = obj->get_property("audiodatarate")) != NULL && prop->is_number()) { | ||
466 | + codec->audio_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
467 | + } | ||
468 | + | ||
469 | + // ignore the following, for each flv/rtmp packet contains them: | ||
470 | + // audiosamplerate, sample->sound_rate | ||
471 | + // audiosamplesize, sample->sound_size | ||
472 | + // stereo, sample->sound_type | ||
473 | + | ||
474 | + return ret; | ||
475 | +} | ||
476 | + | ||
477 | +int SrsHLS::on_audio(SrsSharedPtrMessage* audio) | ||
478 | +{ | ||
479 | + int ret = ERROR_SUCCESS; | ||
480 | + | ||
481 | + SrsAutoFree(SrsSharedPtrMessage, audio, false); | ||
482 | + | ||
483 | + // TODO: maybe donot need to demux the aac? | ||
484 | + if (!hls_enabled) { | ||
485 | + return ret; | ||
486 | + } | ||
487 | + | ||
488 | + sample->clear(); | ||
489 | + if ((ret = codec->audio_aac_demux(audio->payload, audio->size, sample)) != ERROR_SUCCESS) { | ||
490 | + return ret; | ||
491 | + } | ||
492 | + | ||
493 | + if (codec->audio_codec_id != SrsCodecAudioAAC) { | ||
494 | + return ret; | ||
495 | + } | ||
496 | + | ||
497 | + // ignore sequence header | ||
498 | + if (sample->aac_packet_type == SrsCodecAudioTypeSequenceHeader) { | ||
499 | + return ret; | ||
500 | + } | ||
501 | + | ||
502 | + if ((ret = jitter->correct(audio, 0, 0)) != ERROR_SUCCESS) { | ||
503 | + return ret; | ||
504 | + } | ||
505 | + | ||
506 | + srs_assert(current); | ||
507 | + | ||
508 | + stream_dts = audio_frame->dts = audio_frame->pts = audio->header.timestamp * 90; | ||
509 | + audio_frame->pid = TS_AUDIO_PID; | ||
510 | + audio_frame->sid = TS_AUDIO_AAC; | ||
511 | + | ||
512 | + if ((ret = current->muxer->write_audio(audio_frame, audio_buffer, codec, sample)) != ERROR_SUCCESS) { | ||
513 | + return ret; | ||
514 | + } | ||
515 | + | ||
516 | + return ret; | ||
517 | +} | ||
518 | + | ||
519 | +int SrsHLS::on_video(SrsSharedPtrMessage* video) | ||
520 | +{ | ||
521 | + int ret = ERROR_SUCCESS; | ||
522 | + | ||
523 | + SrsAutoFree(SrsSharedPtrMessage, video, false); | ||
524 | + | ||
525 | + // TODO: maybe donot need to demux the avc? | ||
526 | + if (!hls_enabled) { | ||
527 | + return ret; | ||
528 | + } | ||
529 | + | ||
530 | + sample->clear(); | ||
531 | + if ((ret = codec->video_avc_demux(video->payload, video->size, sample)) != ERROR_SUCCESS) { | ||
532 | + return ret; | ||
533 | + } | ||
534 | + | ||
535 | + if (codec->video_codec_id != SrsCodecVideoAVC) { | ||
536 | + return ret; | ||
537 | + } | ||
538 | + | ||
539 | + // ignore sequence header | ||
540 | + if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) { | ||
541 | + return ret; | ||
542 | + } | ||
543 | + | ||
544 | + if ((ret = jitter->correct(video, 0, 0)) != ERROR_SUCCESS) { | ||
545 | + return ret; | ||
546 | + } | ||
547 | + | ||
548 | + stream_dts = video_frame->dts = video->header.timestamp * 90; | ||
549 | + video_frame->pts = video_frame->dts + sample->cts * 90; | ||
550 | + video_frame->pid = TS_VIDEO_PID; | ||
551 | + video_frame->sid = TS_VIDEO_AVC; | ||
552 | + video_frame->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame; | ||
553 | + | ||
554 | + // reopen the muxer for a gop | ||
555 | + if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame) { | ||
556 | + int64_t diff = stream_dts - m3u8_dts; | ||
557 | + // 10s. | ||
558 | + if (diff / 90000 >= 10) { | ||
559 | + if ((ret = reopen()) != ERROR_SUCCESS) { | ||
560 | + return ret; | ||
561 | + } | ||
562 | + } | ||
563 | + } | ||
564 | + | ||
565 | + srs_assert(current); | ||
566 | + if ((ret = current->muxer->write_video(video_frame, video_buffer, codec, sample)) != ERROR_SUCCESS) { | ||
567 | + return ret; | ||
568 | + } | ||
569 | + | ||
570 | + return ret; | ||
571 | +} | ||
572 | + | ||
573 | +int SrsHLS::reopen() | ||
574 | +{ | ||
575 | + int ret = ERROR_SUCCESS; | ||
576 | + | ||
577 | + // try to open the HLS muxer | ||
578 | + SrsConfDirective* conf = config->get_hls(vhost); | ||
579 | + if (!conf && conf->arg0() == "off") { | ||
580 | + return ret; | ||
581 | + } | ||
582 | + | ||
583 | + // TODO: check the audio and video, ensure both exsists. | ||
584 | + // for use fixed mpegts header specifeid the audio and video pid. | ||
585 | + | ||
586 | + hls_enabled = true; | ||
587 | + | ||
588 | + hls_path = SRS_CONF_DEFAULT_HLS_PATH; | ||
589 | + if ((conf = config->get_hls_path(vhost)) != NULL) { | ||
590 | + hls_path = conf->arg0(); | ||
591 | + } | ||
592 | + | ||
593 | + // start new segment. | ||
594 | + if (current) { | ||
595 | + current->duration = (stream_dts - current->segment_start_dts) / 90000.0; | ||
596 | + segments.push_back(current); | ||
597 | + current = NULL; | ||
598 | + | ||
599 | + if ((ret = refresh_m3u8()) != ERROR_SUCCESS) { | ||
600 | + return ret; | ||
601 | + } | ||
602 | + } | ||
603 | + // new segment. | ||
604 | + current = new SrsM3u8Segment(); | ||
605 | + m3u8_dts = current->segment_start_dts = stream_dts; | ||
606 | + | ||
607 | + // generate filename. | ||
608 | + char filename[128]; | ||
609 | + snprintf(filename, sizeof(filename), "%s-%d.ts", stream.c_str(), file_index++); | ||
610 | + | ||
611 | + current->full_path = hls_path; | ||
612 | + current->full_path += "/"; | ||
613 | + current->full_path += filename; | ||
614 | + | ||
615 | + // TODO: support base url, and so on. | ||
616 | + current->uri = filename; | ||
617 | + | ||
618 | + if ((ret = current->muxer->open(current->full_path)) != ERROR_SUCCESS) { | ||
619 | + srs_error("open hls muxer failed. ret=%d", ret); | ||
620 | + return ret; | ||
621 | + } | ||
622 | + srs_trace("open HLS muxer success. vhost=%s, path=%s", vhost.c_str(), current->full_path.c_str()); | ||
623 | + | ||
624 | + return ret; | ||
625 | +} | ||
626 | + | ||
627 | +int SrsHLS::refresh_m3u8() | ||
628 | +{ | ||
629 | + int ret = ERROR_SUCCESS; | ||
630 | + | ||
631 | + int fd = -1; | ||
632 | + ret = _refresh_m3u8(fd); | ||
633 | + if (fd >= 0) { | ||
634 | + close(fd); | ||
635 | + } | ||
636 | + | ||
637 | + return ret; | ||
638 | +} | ||
639 | + | ||
640 | +int SrsHLS::_refresh_m3u8(int& fd) | ||
641 | +{ | ||
642 | + int ret = ERROR_SUCCESS; | ||
643 | + | ||
644 | + // no segments, return. | ||
645 | + if (segments.size() == 0) { | ||
646 | + return ret; | ||
647 | + } | ||
648 | + | ||
649 | + m3u8 = hls_path; | ||
650 | + m3u8 += "/"; | ||
651 | + m3u8 += stream; | ||
652 | + m3u8 += ".m3u8"; | ||
653 | + | ||
654 | + int flags = O_CREAT|O_WRONLY|O_TRUNC; | ||
655 | + mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH; | ||
656 | + if ((fd = ::open(m3u8.c_str(), flags, mode)) < 0) { | ||
657 | + ret = ERROR_HLS_OPEN_FAILED; | ||
658 | + srs_error("open m3u8 file %s failed. ret=%d", m3u8.c_str(), ret); | ||
659 | + return ret; | ||
660 | + } | ||
661 | + srs_info("open m3u8 file %s success.", m3u8.c_str()); | ||
662 | + | ||
663 | + // #EXTM3U\n#EXT-X-VERSION:3\n | ||
664 | + char header[] = { | ||
665 | + // #EXTM3U\n | ||
666 | + 0x23, 0x45, 0x58, 0x54, 0x4d, 0x33, 0x55, 0xa, | ||
667 | + // #EXT-X-VERSION:3\n | ||
668 | + 0x23, 0x45, 0x58, 0x54, 0x2d, 0x58, 0x2d, 0x56, 0x45, 0x52, | ||
669 | + 0x53, 0x49, 0x4f, 0x4e, 0x3a, 0x33, 0xa | ||
670 | + }; | ||
671 | + if (::write(fd, header, sizeof(header)) != sizeof(header)) { | ||
672 | + ret = ERROR_HLS_WRITE_FAILED; | ||
673 | + srs_error("write m3u8 header failed. ret=%d", ret); | ||
674 | + return ret; | ||
675 | + } | ||
676 | + srs_verbose("write m3u8 header success."); | ||
677 | + | ||
678 | + // #EXT-X-MEDIA-SEQUENCE:4294967295\n | ||
679 | + SrsM3u8Segment* first = *segments.begin(); | ||
680 | + char sequence[34] = {}; | ||
681 | + int len = snprintf(sequence, sizeof(sequence), "#EXT-X-MEDIA-SEQUENCE:%d\n", first->sequence_no); | ||
682 | + if (::write(fd, sequence, len) != len) { | ||
683 | + ret = ERROR_HLS_WRITE_FAILED; | ||
684 | + srs_error("write m3u8 sequence failed. ret=%d", ret); | ||
685 | + return ret; | ||
686 | + } | ||
687 | + srs_verbose("write m3u8 sequence success."); | ||
688 | + | ||
689 | + // #EXT-X-TARGETDURATION:4294967295\n | ||
690 | + int target_duration = 0; | ||
691 | + std::vector<SrsM3u8Segment*>::iterator it; | ||
692 | + for (it = segments.begin(); it != segments.end(); ++it) { | ||
693 | + SrsM3u8Segment* segment = *it; | ||
694 | + target_duration = srs_max(target_duration, (int)segment->duration); | ||
695 | + } | ||
696 | + // TODO: maybe need to take an around value | ||
697 | + target_duration += 1; | ||
698 | + char duration[34]; | ||
699 | + len = snprintf(duration, sizeof(duration), "#EXT-X-TARGETDURATION:%d\n", target_duration); | ||
700 | + if (::write(fd, duration, len) != len) { | ||
701 | + ret = ERROR_HLS_WRITE_FAILED; | ||
702 | + srs_error("write m3u8 duration failed. ret=%d", ret); | ||
703 | + return ret; | ||
704 | + } | ||
705 | + srs_verbose("write m3u8 duration success."); | ||
706 | + | ||
707 | + // write all segments | ||
708 | + for (it = segments.begin(); it != segments.end(); ++it) { | ||
709 | + SrsM3u8Segment* segment = *it; | ||
710 | + | ||
711 | + // "#EXTINF:4294967295.208,\n" | ||
712 | + char ext_info[25]; | ||
713 | + len = snprintf(ext_info, sizeof(ext_info), "#EXTINF:%.3f\n", segment->duration); | ||
714 | + if (::write(fd, ext_info, len) != len) { | ||
715 | + ret = ERROR_HLS_WRITE_FAILED; | ||
716 | + srs_error("write m3u8 segment failed. ret=%d", ret); | ||
717 | + return ret; | ||
718 | + } | ||
719 | + srs_verbose("write m3u8 segment success."); | ||
720 | + | ||
721 | + // file name | ||
722 | + std::string filename = segment->uri; | ||
723 | + filename += "\n"; | ||
724 | + if (::write(fd, filename.c_str(), filename.length()) != (int)filename.length()) { | ||
725 | + ret = ERROR_HLS_WRITE_FAILED; | ||
726 | + srs_error("write m3u8 segment uri failed. ret=%d", ret); | ||
727 | + return ret; | ||
728 | + } | ||
729 | + srs_verbose("write m3u8 segment uri success."); | ||
730 | + } | ||
731 | + srs_info("write m3u8 %s success.", m3u8.c_str()); | ||
732 | + | ||
733 | + return ret; | ||
734 | +} | ||
735 | + | ||
736 | +SrsTSMuxer::SrsTSMuxer() | ||
737 | +{ | ||
738 | + fd = -1; | ||
739 | +} | ||
740 | + | ||
741 | +SrsTSMuxer::~SrsTSMuxer() | ||
742 | +{ | ||
743 | + close(); | ||
744 | +} | ||
745 | + | ||
564 | int SrsTSMuxer::open(std::string _path) | 746 | int SrsTSMuxer::open(std::string _path) |
565 | { | 747 | { |
566 | int ret = ERROR_SUCCESS; | 748 | int ret = ERROR_SUCCESS; |
@@ -585,14 +767,10 @@ int SrsTSMuxer::open(std::string _path) | @@ -585,14 +767,10 @@ int SrsTSMuxer::open(std::string _path) | ||
585 | return ret; | 767 | return ret; |
586 | } | 768 | } |
587 | 769 | ||
588 | -int SrsTSMuxer::write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample) | 770 | +int SrsTSMuxer::write_audio(SrsMpegtsFrame* audio_frame, SrsCodecBuffer* audio_buffer, SrsCodec* codec, SrsCodecSample* sample) |
589 | { | 771 | { |
590 | int ret = ERROR_SUCCESS; | 772 | int ret = ERROR_SUCCESS; |
591 | 773 | ||
592 | - audio_frame->dts = audio_frame->pts = time * 90; | ||
593 | - audio_frame->pid = TS_AUDIO_PID; | ||
594 | - audio_frame->sid = TS_AUDIO_AAC; | ||
595 | - | ||
596 | for (int i = 0; i < sample->nb_buffers; i++) { | 774 | for (int i = 0; i < sample->nb_buffers; i++) { |
597 | SrsCodecBuffer* buf = &sample->buffers[i]; | 775 | SrsCodecBuffer* buf = &sample->buffers[i]; |
598 | int32_t size = buf->size; | 776 | int32_t size = buf->size; |
@@ -660,16 +838,10 @@ int SrsTSMuxer::write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sam | @@ -660,16 +838,10 @@ int SrsTSMuxer::write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sam | ||
660 | return ret; | 838 | return ret; |
661 | } | 839 | } |
662 | 840 | ||
663 | -int SrsTSMuxer::write_video(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample) | 841 | +int SrsTSMuxer::write_video(SrsMpegtsFrame* video_frame, SrsCodecBuffer* video_buffer, SrsCodec* codec, SrsCodecSample* sample) |
664 | { | 842 | { |
665 | int ret = ERROR_SUCCESS; | 843 | int ret = ERROR_SUCCESS; |
666 | 844 | ||
667 | - video_frame->dts = time * 90; | ||
668 | - video_frame->pts = video_frame->dts + sample->cts * 90; | ||
669 | - video_frame->pid = TS_VIDEO_PID; | ||
670 | - video_frame->sid = TS_VIDEO_AVC; | ||
671 | - video_frame->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame; | ||
672 | - | ||
673 | static u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 }; | 845 | static u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 }; |
674 | video_buffer->append(aud_nal, sizeof(aud_nal)); | 846 | video_buffer->append(aud_nal, sizeof(aud_nal)); |
675 | 847 |
@@ -30,34 +30,88 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -30,34 +30,88 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
30 | #include <srs_core.hpp> | 30 | #include <srs_core.hpp> |
31 | 31 | ||
32 | #include <string> | 32 | #include <string> |
33 | +#include <vector> | ||
33 | 34 | ||
34 | class SrsOnMetaDataPacket; | 35 | class SrsOnMetaDataPacket; |
35 | class SrsSharedPtrMessage; | 36 | class SrsSharedPtrMessage; |
36 | class SrsCodecSample; | 37 | class SrsCodecSample; |
37 | class SrsCodecBuffer; | 38 | class SrsCodecBuffer; |
38 | class SrsMpegtsFrame; | 39 | class SrsMpegtsFrame; |
40 | +class SrsRtmpJitter; | ||
39 | class SrsTSMuxer; | 41 | class SrsTSMuxer; |
40 | class SrsCodec; | 42 | class SrsCodec; |
41 | -class SrsRtmpJitter; | ||
42 | 43 | ||
44 | +/** | ||
45 | +* 3.3.2. EXTINF | ||
46 | +* The EXTINF tag specifies the duration of a media segment. | ||
47 | +*/ | ||
48 | +struct SrsM3u8Segment | ||
49 | +{ | ||
50 | + // duration in seconds in m3u8. | ||
51 | + double duration; | ||
52 | + // sequence number in m3u8. | ||
53 | + int sequence_no; | ||
54 | + // ts uri in m3u8. | ||
55 | + std::string uri; | ||
56 | + // ts full file to write. | ||
57 | + std::string full_path; | ||
58 | + // the muxer to write ts. | ||
59 | + SrsTSMuxer* muxer; | ||
60 | + // current segment start dts for m3u8 | ||
61 | + int64_t segment_start_dts; | ||
62 | + | ||
63 | + SrsM3u8Segment(); | ||
64 | + virtual ~SrsM3u8Segment(); | ||
65 | +}; | ||
66 | + | ||
67 | +/** | ||
68 | +* write m3u8 hls. | ||
69 | +*/ | ||
43 | class SrsHLS | 70 | class SrsHLS |
44 | { | 71 | { |
45 | private: | 72 | private: |
46 | std::string vhost; | 73 | std::string vhost; |
74 | + std::string stream; | ||
75 | + std::string hls_path; | ||
76 | +private: | ||
77 | + int file_index; | ||
78 | + std::string m3u8; | ||
79 | +private: | ||
80 | + /** | ||
81 | + * m3u8 segments. | ||
82 | + */ | ||
83 | + std::vector<SrsM3u8Segment*> segments; | ||
84 | + /** | ||
85 | + * current writing segment. | ||
86 | + */ | ||
87 | + SrsM3u8Segment* current; | ||
88 | + // current frame and buffer | ||
89 | + SrsMpegtsFrame* audio_frame; | ||
90 | + SrsCodecBuffer* audio_buffer; | ||
91 | + SrsMpegtsFrame* video_frame; | ||
92 | + SrsCodecBuffer* video_buffer; | ||
93 | + // last known dts | ||
94 | + int64_t stream_dts; | ||
95 | + // last segment dts in m3u8 | ||
96 | + int64_t m3u8_dts; | ||
97 | +private: | ||
47 | bool hls_enabled; | 98 | bool hls_enabled; |
48 | SrsCodec* codec; | 99 | SrsCodec* codec; |
49 | SrsCodecSample* sample; | 100 | SrsCodecSample* sample; |
50 | - SrsTSMuxer* muxer; | ||
51 | SrsRtmpJitter* jitter; | 101 | SrsRtmpJitter* jitter; |
52 | public: | 102 | public: |
53 | SrsHLS(); | 103 | SrsHLS(); |
54 | virtual ~SrsHLS(); | 104 | virtual ~SrsHLS(); |
55 | public: | 105 | public: |
56 | - virtual int on_publish(std::string _vhost); | 106 | + virtual int on_publish(std::string _vhost, std::string _stream); |
57 | virtual void on_unpublish(); | 107 | virtual void on_unpublish(); |
58 | virtual int on_meta_data(SrsOnMetaDataPacket* metadata); | 108 | virtual int on_meta_data(SrsOnMetaDataPacket* metadata); |
59 | virtual int on_audio(SrsSharedPtrMessage* audio); | 109 | virtual int on_audio(SrsSharedPtrMessage* audio); |
60 | virtual int on_video(SrsSharedPtrMessage* video); | 110 | virtual int on_video(SrsSharedPtrMessage* video); |
111 | +private: | ||
112 | + virtual int reopen(); | ||
113 | + virtual int refresh_m3u8(); | ||
114 | + virtual int _refresh_m3u8(int& fd); | ||
61 | }; | 115 | }; |
62 | 116 | ||
63 | class SrsTSMuxer | 117 | class SrsTSMuxer |
@@ -65,18 +119,13 @@ class SrsTSMuxer | @@ -65,18 +119,13 @@ class SrsTSMuxer | ||
65 | private: | 119 | private: |
66 | int fd; | 120 | int fd; |
67 | std::string path; | 121 | std::string path; |
68 | -private: | ||
69 | - SrsMpegtsFrame* audio_frame; | ||
70 | - SrsCodecBuffer* audio_buffer; | ||
71 | - SrsMpegtsFrame* video_frame; | ||
72 | - SrsCodecBuffer* video_buffer; | ||
73 | public: | 122 | public: |
74 | SrsTSMuxer(); | 123 | SrsTSMuxer(); |
75 | virtual ~SrsTSMuxer(); | 124 | virtual ~SrsTSMuxer(); |
76 | public: | 125 | public: |
77 | virtual int open(std::string _path); | 126 | virtual int open(std::string _path); |
78 | - virtual int write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample); | ||
79 | - virtual int write_video(u_int32_t time, SrsCodec* codec, SrsCodecSample* sample); | 127 | + virtual int write_audio(SrsMpegtsFrame* audio_frame, SrsCodecBuffer* audio_buffer, SrsCodec* codec, SrsCodecSample* sample); |
128 | + virtual int write_video(SrsMpegtsFrame* video_frame, SrsCodecBuffer* video_buffer, SrsCodec* codec, SrsCodecSample* sample); | ||
80 | virtual void close(); | 129 | virtual void close(); |
81 | }; | 130 | }; |
82 | 131 |
@@ -413,6 +413,7 @@ int SrsSource::on_video(SrsCommonMessage* video) | @@ -413,6 +413,7 @@ int SrsSource::on_video(SrsCommonMessage* video) | ||
413 | } | 413 | } |
414 | srs_verbose("initialize shared ptr video success."); | 414 | srs_verbose("initialize shared ptr video success."); |
415 | 415 | ||
416 | + // TODO: when return error, crash. | ||
416 | if ((ret = hls->on_video(msg->copy())) != ERROR_SUCCESS) { | 417 | if ((ret = hls->on_video(msg->copy())) != ERROR_SUCCESS) { |
417 | srs_error("hls process video message failed. ret=%d", ret); | 418 | srs_error("hls process video message failed. ret=%d", ret); |
418 | return ret; | 419 | return ret; |
@@ -451,9 +452,9 @@ int SrsSource::on_video(SrsCommonMessage* video) | @@ -451,9 +452,9 @@ int SrsSource::on_video(SrsCommonMessage* video) | ||
451 | return ret; | 452 | return ret; |
452 | } | 453 | } |
453 | 454 | ||
454 | -int SrsSource::on_publish(std::string vhost) | 455 | +int SrsSource::on_publish(std::string vhost, std::string stream) |
455 | { | 456 | { |
456 | - return hls->on_publish(vhost); | 457 | + return hls->on_publish(vhost, stream); |
457 | } | 458 | } |
458 | 459 | ||
459 | void SrsSource::on_unpublish() | 460 | void SrsSource::on_unpublish() |
@@ -166,7 +166,7 @@ public: | @@ -166,7 +166,7 @@ public: | ||
166 | virtual int on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata); | 166 | virtual int on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata); |
167 | virtual int on_audio(SrsCommonMessage* audio); | 167 | virtual int on_audio(SrsCommonMessage* audio); |
168 | virtual int on_video(SrsCommonMessage* video); | 168 | virtual int on_video(SrsCommonMessage* video); |
169 | - virtual int on_publish(std::string vhost); | 169 | + virtual int on_publish(std::string vhost, std::string stream); |
170 | virtual void on_unpublish(); | 170 | virtual void on_unpublish(); |
171 | public: | 171 | public: |
172 | virtual int create_consumer(SrsConsumer*& consumer); | 172 | virtual int create_consumer(SrsConsumer*& consumer); |
-
请 注册 或 登录 后发表评论