正在显示
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); |
-
请 注册 或 登录 后发表评论