正在显示
8 个修改的文件
包含
818 行增加
和
818 行删除
trunk/src/app/srs_app_edge.cpp
100755 → 100644
trunk/src/app/srs_app_edge.hpp
100755 → 100644
| 1 | -/* | ||
| 2 | -The MIT License (MIT) | ||
| 3 | - | ||
| 4 | -Copyright (c) 2013-2015 SRS(simple-rtmp-server) | ||
| 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 | -#include <srs_app_hds.hpp> | ||
| 24 | - | ||
| 25 | -#ifdef SRS_AUTO_HDS | ||
| 26 | - | ||
| 27 | -#include <unistd.h> | ||
| 28 | -#include <string> | ||
| 29 | -#include <vector> | ||
| 30 | -#include <sys/types.h> | ||
| 31 | -#include <sys/stat.h> | ||
| 32 | -#include <fcntl.h> | ||
| 33 | -using namespace std; | ||
| 34 | - | ||
| 35 | -#include <srs_app_hds.hpp> | ||
| 36 | -#include <srs_rtmp_stack.hpp> | ||
| 37 | -#include <srs_kernel_log.hpp> | ||
| 38 | -#include <srs_kernel_codec.hpp> | ||
| 39 | -#include <srs_rtmp_stack.hpp> | ||
| 40 | -#include <srs_kernel_stream.hpp> | ||
| 41 | -#include <srs_core_autofree.hpp> | ||
| 42 | -#include <srs_kernel_utility.hpp> | ||
| 43 | -#include <srs_app_config.hpp> | ||
| 44 | - | ||
| 45 | -static void update_box(char *start, int size) | ||
| 46 | -{ | ||
| 47 | - char *p_size = (char*)&size; | ||
| 48 | - start[0] = p_size[3]; | ||
| 49 | - start[1] = p_size[2]; | ||
| 50 | - start[2] = p_size[1]; | ||
| 51 | - start[3] = p_size[0]; | ||
| 52 | -} | ||
| 53 | - | ||
| 54 | -char flv_header[] = {'F', 'L', 'V', | ||
| 55 | - 0x01, 0x05, 0x00, 0x00, 0x00, 0x09, | ||
| 56 | - 0x00, 0x00, 0x00, 0x00}; | ||
| 57 | - | ||
| 58 | -string serialFlv(SrsSharedPtrMessage *msg) | ||
| 59 | -{ | ||
| 60 | - SrsStream *stream = new SrsStream; | ||
| 61 | - | ||
| 62 | - int size = 15 + msg->size; | ||
| 63 | - char *byte = new char[size]; | ||
| 64 | - stream->initialize(byte, size); | ||
| 65 | - | ||
| 66 | - // tag header | ||
| 67 | - long long dts = msg->timestamp; | ||
| 68 | - char type = msg->is_video() ? 0x09 : 0x08; | ||
| 69 | - | ||
| 70 | - stream->write_1bytes(type); | ||
| 71 | - stream->write_3bytes(msg->size); | ||
| 72 | - stream->write_3bytes(dts); | ||
| 73 | - stream->write_1bytes(dts >> 24 & 0xFF); | ||
| 74 | - stream->write_3bytes(0); | ||
| 75 | - stream->write_bytes(msg->payload, msg->size); | ||
| 76 | - | ||
| 77 | - // pre tag size | ||
| 78 | - int preTagSize = msg->size + 11; | ||
| 79 | - stream->write_4bytes(preTagSize); | ||
| 80 | - | ||
| 81 | - string ret(stream->data(), stream->size()); | ||
| 82 | - | ||
| 83 | - delete stream; | ||
| 84 | - delete [] byte; | ||
| 85 | - | ||
| 86 | - return ret; | ||
| 87 | -} | ||
| 88 | - | ||
| 89 | -class SrsHdsFragment | ||
| 90 | -{ | ||
| 91 | -public: | ||
| 92 | - SrsHdsFragment(SrsRequest *r) | ||
| 93 | - : req(r) | ||
| 94 | - , index(-1) | ||
| 95 | - , start_time(0) | ||
| 96 | - , videoSh(NULL) | ||
| 97 | - , audioSh(NULL) | ||
| 98 | - { | ||
| 99 | - | ||
| 100 | - } | ||
| 101 | - | ||
| 102 | - ~SrsHdsFragment() | ||
| 103 | - { | ||
| 104 | - srs_freep(videoSh); | ||
| 105 | - srs_freep(audioSh); | ||
| 106 | - | ||
| 107 | - // clean msgs | ||
| 108 | - list<SrsSharedPtrMessage *>::iterator iter; | ||
| 109 | - for (iter = msgs.begin(); iter != msgs.end(); ++iter) { | ||
| 110 | - SrsSharedPtrMessage *msg = *iter; | ||
| 111 | - srs_freep(msg); | ||
| 112 | - } | ||
| 113 | - } | ||
| 114 | - | ||
| 115 | - void on_video(SrsSharedPtrMessage *msg) | ||
| 116 | - { | ||
| 117 | - SrsSharedPtrMessage *_msg = msg->copy(); | ||
| 118 | - msgs.push_back(_msg); | ||
| 119 | - } | ||
| 120 | - | ||
| 121 | - void on_audio(SrsSharedPtrMessage *msg) | ||
| 122 | - { | ||
| 123 | - SrsSharedPtrMessage *_msg = msg->copy(); | ||
| 124 | - msgs.push_back(_msg); | ||
| 125 | - } | ||
| 126 | - | ||
| 127 | - /*! | ||
| 128 | - flush data to disk. | ||
| 129 | - */ | ||
| 130 | - int flush() | ||
| 131 | - { | ||
| 132 | - string data; | ||
| 133 | - if (videoSh) { | ||
| 134 | - videoSh->timestamp = start_time; | ||
| 135 | - data.append(serialFlv(videoSh)); | ||
| 136 | - } | ||
| 137 | - | ||
| 138 | - if (audioSh) { | ||
| 139 | - audioSh->timestamp = start_time; | ||
| 140 | - data.append(serialFlv(audioSh)); | ||
| 141 | - } | ||
| 142 | - | ||
| 143 | - list<SrsSharedPtrMessage *>::iterator iter; | ||
| 144 | - for (iter = msgs.begin(); iter != msgs.end(); ++iter) { | ||
| 145 | - SrsSharedPtrMessage *msg = *iter; | ||
| 146 | - data.append(serialFlv(msg)); | ||
| 147 | - } | ||
| 148 | - | ||
| 149 | - char box_header[8]; | ||
| 150 | - SrsStream ss; | ||
| 151 | - ss.initialize(box_header, 8); | ||
| 152 | - ss.write_4bytes(8 + data.size()); | ||
| 153 | - ss.write_string("mdat"); | ||
| 154 | - | ||
| 155 | - data = string(ss.data(), ss.size()) + data; | ||
| 156 | - | ||
| 157 | - const char *file_path = path.c_str(); | ||
| 158 | - int fd = open(file_path, O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); | ||
| 159 | - if (fd < 0) { | ||
| 160 | - srs_error("open fragment file failed, path=%s", file_path); | ||
| 161 | - return -1; | ||
| 162 | - } | ||
| 163 | - | ||
| 164 | - if (write(fd, data.data(), data.size()) != (int)data.size()) { | ||
| 165 | - srs_error("write fragment file failed, path=", file_path); | ||
| 166 | - close(fd); | ||
| 167 | - return -1; | ||
| 168 | - } | ||
| 169 | - close(fd); | ||
| 170 | - | ||
| 171 | - srs_trace("build fragment success=%s", file_path); | ||
| 172 | - | ||
| 173 | - return ERROR_SUCCESS; | ||
| 174 | - } | ||
| 175 | - | ||
| 176 | - /*! | ||
| 177 | - calc the segment duration in milliseconds. | ||
| 178 | - @return 0 if no msgs | ||
| 179 | - or the last msg dts minus the first msg dts. | ||
| 180 | - */ | ||
| 181 | - int duration() | ||
| 182 | - { | ||
| 183 | - int duration_ms = 0; | ||
| 184 | - long long first_msg_ts = 0; | ||
| 185 | - long long last_msg_ts = 0; | ||
| 186 | - | ||
| 187 | - if (msgs.size() >= 2) { | ||
| 188 | - SrsSharedPtrMessage *first_msg = msgs.front(); | ||
| 189 | - first_msg_ts = first_msg->timestamp; | ||
| 190 | - | ||
| 191 | - SrsSharedPtrMessage *last_msg = msgs.back(); | ||
| 192 | - last_msg_ts = last_msg->timestamp; | ||
| 193 | - | ||
| 194 | - duration_ms = last_msg_ts - first_msg_ts; | ||
| 195 | - } | ||
| 196 | - | ||
| 197 | - return duration_ms; | ||
| 198 | - } | ||
| 199 | - | ||
| 200 | - /*! | ||
| 201 | - set/get index | ||
| 202 | - */ | ||
| 203 | - inline void set_index(int idx) | ||
| 204 | - { | ||
| 205 | - char file_path[1024] = {0}; | ||
| 206 | - sprintf(file_path, "%s/%s/%sSeg1-Frag%d", _srs_config->get_hds_path(req->vhost).c_str() | ||
| 207 | - , req->app.c_str(), req->stream.c_str(), idx); | ||
| 208 | - | ||
| 209 | - path = file_path; | ||
| 210 | - index = idx; | ||
| 211 | - } | ||
| 212 | - | ||
| 213 | - inline int get_index() | ||
| 214 | - { | ||
| 215 | - return index; | ||
| 216 | - } | ||
| 217 | - | ||
| 218 | - /*! | ||
| 219 | - set/get start time | ||
| 220 | - */ | ||
| 221 | - inline void set_start_time(long long st) | ||
| 222 | - { | ||
| 223 | - start_time = st; | ||
| 224 | - } | ||
| 225 | - | ||
| 226 | - inline long long get_start_time() | ||
| 227 | - { | ||
| 228 | - return start_time; | ||
| 229 | - } | ||
| 230 | - | ||
| 231 | - void set_video_sh(SrsSharedPtrMessage *msg) | ||
| 232 | - { | ||
| 233 | - srs_freep(videoSh); | ||
| 234 | - videoSh = msg->copy(); | ||
| 235 | - } | ||
| 236 | - | ||
| 237 | - void set_audio_sh(SrsSharedPtrMessage *msg) | ||
| 238 | - { | ||
| 239 | - srs_freep(audioSh); | ||
| 240 | - audioSh = msg->copy(); | ||
| 241 | - } | ||
| 242 | - | ||
| 243 | - string fragment_path() | ||
| 244 | - { | ||
| 245 | - return path; | ||
| 246 | - } | ||
| 247 | - | ||
| 248 | -private: | ||
| 249 | - SrsRequest *req; | ||
| 250 | - list<SrsSharedPtrMessage *> msgs; | ||
| 251 | - | ||
| 252 | - /*! | ||
| 253 | - the index of this fragment | ||
| 254 | - */ | ||
| 255 | - int index; | ||
| 256 | - long long start_time; | ||
| 257 | - | ||
| 258 | - SrsSharedPtrMessage *videoSh; | ||
| 259 | - SrsSharedPtrMessage *audioSh; | ||
| 260 | - string path; | ||
| 261 | -}; | ||
| 262 | - | ||
| 263 | -SrsHds::SrsHds(SrsSource *s) | ||
| 264 | - : currentSegment(NULL) | ||
| 265 | - , fragment_index(1) | ||
| 266 | - , video_sh(NULL) | ||
| 267 | - , audio_sh(NULL) | ||
| 268 | - , hds_req(NULL) | ||
| 269 | - , hds_enabled(false) | ||
| 270 | -{ | ||
| 271 | - | ||
| 272 | -} | ||
| 273 | - | ||
| 274 | -SrsHds::~SrsHds() | ||
| 275 | -{ | ||
| 276 | - | ||
| 277 | -} | ||
| 278 | - | ||
| 279 | -int SrsHds::on_publish(SrsRequest *req) | ||
| 280 | -{ | ||
| 281 | - int ret = ERROR_SUCCESS; | ||
| 282 | - if (hds_enabled) { | ||
| 283 | - return ret; | ||
| 284 | - } | ||
| 285 | - | ||
| 286 | - std::string vhost = req->vhost; | ||
| 287 | - if (!_srs_config->get_hds_enabled(vhost)) { | ||
| 288 | - hds_enabled = false; | ||
| 289 | - return ret; | ||
| 290 | - } | ||
| 291 | - hds_enabled = true; | ||
| 292 | - | ||
| 293 | - hds_req = req->copy(); | ||
| 294 | - | ||
| 295 | - return flush_mainfest(); | ||
| 296 | -} | ||
| 297 | - | ||
| 298 | -int SrsHds::on_unpublish() | ||
| 299 | -{ | ||
| 300 | - int ret = ERROR_SUCCESS; | ||
| 301 | - | ||
| 302 | - if (!hds_enabled) { | ||
| 303 | - return ret; | ||
| 304 | - } | ||
| 305 | - | ||
| 306 | - hds_enabled = false; | ||
| 307 | - | ||
| 308 | - srs_freep(video_sh); | ||
| 309 | - srs_freep(audio_sh); | ||
| 310 | - srs_freep(hds_req); | ||
| 311 | - | ||
| 312 | - // clean fragments | ||
| 313 | - list<SrsHdsFragment *>::iterator iter; | ||
| 314 | - for (iter = fragments.begin(); iter != fragments.end(); ++iter) { | ||
| 315 | - SrsHdsFragment *st = *iter; | ||
| 316 | - srs_freep(st); | ||
| 317 | - } | ||
| 318 | - fragments.clear(); | ||
| 319 | - | ||
| 320 | - srs_freep(currentSegment); | ||
| 321 | - | ||
| 322 | - srs_trace("HDS un-published"); | ||
| 323 | - | ||
| 324 | - return ret; | ||
| 325 | -} | ||
| 326 | - | ||
| 327 | -int SrsHds::on_video(SrsSharedPtrMessage* msg) | ||
| 328 | -{ | ||
| 329 | - int ret = ERROR_SUCCESS; | ||
| 330 | - | ||
| 331 | - if (!hds_enabled) { | ||
| 332 | - return ret; | ||
| 333 | - } | ||
| 334 | - | ||
| 335 | - if (SrsFlvCodec::video_is_sequence_header(msg->payload, msg->size)) { | ||
| 336 | - srs_freep(video_sh); | ||
| 337 | - video_sh = msg->copy(); | ||
| 338 | - } | ||
| 339 | - | ||
| 340 | - if (!currentSegment) { | ||
| 341 | - currentSegment = new SrsHdsFragment(hds_req); | ||
| 342 | - currentSegment->set_index(fragment_index++); | ||
| 343 | - currentSegment->set_start_time(msg->timestamp); | ||
| 344 | - | ||
| 345 | - if (video_sh) | ||
| 346 | - currentSegment->set_video_sh(video_sh); | ||
| 347 | - | ||
| 348 | - if (audio_sh) | ||
| 349 | - currentSegment->set_audio_sh(audio_sh); | ||
| 350 | - } | ||
| 351 | - | ||
| 352 | - currentSegment->on_video(msg); | ||
| 353 | - | ||
| 354 | - double fragment_duration = _srs_config->get_hds_fragment(hds_req->vhost) * 1000; | ||
| 355 | - if (currentSegment->duration() >= fragment_duration) { | ||
| 356 | - // flush segment | ||
| 357 | - if ((ret = currentSegment->flush()) != ERROR_SUCCESS) { | ||
| 358 | - srs_error("flush segment failed."); | ||
| 359 | - return ret; | ||
| 360 | - } | ||
| 361 | - | ||
| 362 | - srs_trace("flush Segment success."); | ||
| 363 | - fragments.push_back(currentSegment); | ||
| 364 | - currentSegment = NULL; | ||
| 365 | - adjust_windows(); | ||
| 366 | - | ||
| 367 | - // flush bootstrap | ||
| 368 | - if ((ret = flush_bootstrap()) != ERROR_SUCCESS) { | ||
| 369 | - srs_error("flush bootstrap failed."); | ||
| 370 | - return ret; | ||
| 371 | - } | ||
| 372 | - | ||
| 373 | - srs_trace("flush BootStrap success."); | ||
| 374 | - } | ||
| 375 | - | ||
| 376 | - return ret; | ||
| 377 | -} | ||
| 378 | - | ||
| 379 | -int SrsHds::on_audio(SrsSharedPtrMessage* msg) | ||
| 380 | -{ | ||
| 381 | - int ret = ERROR_SUCCESS; | ||
| 382 | - | ||
| 383 | - if (!hds_enabled) { | ||
| 384 | - return ret; | ||
| 385 | - } | ||
| 386 | - | ||
| 387 | - if (SrsFlvCodec::audio_is_sequence_header(msg->payload, msg->size)) { | ||
| 388 | - srs_freep(audio_sh); | ||
| 389 | - audio_sh = msg->copy(); | ||
| 390 | - } | ||
| 391 | - | ||
| 392 | - if (!currentSegment) { | ||
| 393 | - currentSegment = new SrsHdsFragment(hds_req); | ||
| 394 | - currentSegment->set_index(fragment_index++); | ||
| 395 | - currentSegment->set_start_time(msg->timestamp); | ||
| 396 | - | ||
| 397 | - if (video_sh) | ||
| 398 | - currentSegment->set_video_sh(video_sh); | ||
| 399 | - | ||
| 400 | - if (audio_sh) | ||
| 401 | - currentSegment->set_audio_sh(audio_sh); | ||
| 402 | - } | ||
| 403 | - | ||
| 404 | - currentSegment->on_audio(msg); | ||
| 405 | - | ||
| 406 | - double fragment_duration = _srs_config->get_hds_fragment(hds_req->vhost) * 1000; | ||
| 407 | - if (currentSegment->duration() >= fragment_duration) { | ||
| 408 | - // flush segment | ||
| 409 | - if ((ret = currentSegment->flush()) != ERROR_SUCCESS) { | ||
| 410 | - srs_error("flush segment failed."); | ||
| 411 | - return ret; | ||
| 412 | - } | ||
| 413 | - | ||
| 414 | - srs_info("flush Segment success."); | ||
| 415 | - | ||
| 416 | - // reset the current segment | ||
| 417 | - fragments.push_back(currentSegment); | ||
| 418 | - currentSegment = NULL; | ||
| 419 | - adjust_windows(); | ||
| 420 | - | ||
| 421 | - // flush bootstrap | ||
| 422 | - if ((ret = flush_bootstrap()) != ERROR_SUCCESS) { | ||
| 423 | - srs_error("flush bootstrap failed."); | ||
| 424 | - return ret; | ||
| 425 | - } | ||
| 426 | - | ||
| 427 | - srs_info("flush BootStrap success."); | ||
| 428 | - } | ||
| 429 | - | ||
| 430 | - return ret; | ||
| 431 | -} | ||
| 432 | - | ||
| 433 | -int SrsHds::flush_mainfest() | ||
| 434 | -{ | ||
| 435 | - int ret = ERROR_SUCCESS; | ||
| 436 | - | ||
| 437 | - char buf[1024] = {0}; | ||
| 438 | - sprintf(buf, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" | ||
| 439 | - "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n\t" | ||
| 440 | - "<id>%s.f4m</id>\n\t" | ||
| 441 | - "<streamType>live</streamType>\n\t" | ||
| 442 | - "<deliveryType>streaming</deliveryType>\n\t" | ||
| 443 | - "<bootstrapInfo profile=\"named\" url=\"%s.abst\" id=\"bootstrap0\" />\n\t" | ||
| 444 | - "<media bitrate=\"0\" url=\"%s\" bootstrapInfoId=\"bootstrap0\"></media>\n" | ||
| 445 | - "</manifest>" | ||
| 446 | - , hds_req->stream.c_str(), hds_req->stream.c_str(), hds_req->stream.c_str()); | ||
| 447 | - | ||
| 448 | - string dir = _srs_config->get_hds_path(hds_req->vhost) + "/" + hds_req->app; | ||
| 449 | - if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) { | ||
| 450 | - srs_error("hds create dir failed. ret=%d", ret); | ||
| 451 | - return ret; | ||
| 452 | - } | ||
| 453 | - string path = dir + "/" + hds_req->stream + ".f4m"; | ||
| 454 | - | ||
| 455 | - int fd = open(path.c_str(), O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); | ||
| 456 | - if (fd < 0) { | ||
| 457 | - srs_error("open manifest file failed, path=%s", path.c_str()); | ||
| 458 | - ret = ERROR_HDS_OPEN_F4M_FAILED; | ||
| 459 | - return ret; | ||
| 460 | - } | ||
| 461 | - | ||
| 462 | - int f4m_size = strlen(buf); | ||
| 463 | - if (write(fd, buf, f4m_size) != f4m_size) { | ||
| 464 | - srs_error("write manifest file failed, path=", path.c_str()); | ||
| 465 | - close(fd); | ||
| 466 | - ret = ERROR_HDS_WRITE_F4M_FAILED; | ||
| 467 | - return ret; | ||
| 468 | - } | ||
| 469 | - close(fd); | ||
| 470 | - | ||
| 471 | - srs_trace("build manifest success=%s", path.c_str()); | ||
| 472 | - | ||
| 473 | - return ERROR_SUCCESS; | ||
| 474 | -} | ||
| 475 | - | ||
| 476 | -int SrsHds::flush_bootstrap() | ||
| 477 | -{ | ||
| 478 | - int ret = ERROR_SUCCESS; | ||
| 479 | - | ||
| 480 | - SrsStream abst; | ||
| 481 | - | ||
| 482 | - int size = 1024*100; | ||
| 483 | - | ||
| 484 | - char *start_abst = new char[1024*100]; | ||
| 485 | - SrsAutoFree(char, start_abst); | ||
| 486 | - | ||
| 487 | - int size_abst = 0; | ||
| 488 | - char *start_asrt = NULL; | ||
| 489 | - int size_asrt = 0; | ||
| 490 | - char *start_afrt = NULL; | ||
| 491 | - int size_afrt = 0; | ||
| 492 | - | ||
| 493 | - if ((ret = abst.initialize(start_abst, size)) != ERROR_SUCCESS) { | ||
| 494 | - return ret; | ||
| 495 | - } | ||
| 496 | - | ||
| 497 | - // @see video_file_format_spec_v10_1 | ||
| 498 | - // page: 46 | ||
| 499 | - abst.write_4bytes(0); | ||
| 500 | - abst.write_string("abst"); | ||
| 501 | - abst.write_1bytes(0x00); // Either 0 or 1 | ||
| 502 | - abst.write_3bytes(0x00); // Flags always 0 | ||
| 503 | - size_abst += 12; | ||
| 504 | - /*! | ||
| 505 | - @BootstrapinfoVersion UI32 | ||
| 506 | - The version number of the bootstrap information. | ||
| 507 | - When the Update field is set, BootstrapinfoVersion | ||
| 508 | - indicates the version number that is being updated. | ||
| 509 | - we assume this is the last. | ||
| 510 | - */ | ||
| 511 | - abst.write_4bytes(fragment_index - 1); // BootstrapinfoVersion | ||
| 512 | - | ||
| 513 | - abst.write_1bytes(0x20); // profile, live, update | ||
| 514 | - abst.write_4bytes(1000); // TimeScale Typically, the value is 1000, for a unit of milliseconds | ||
| 515 | - size_abst += 9; | ||
| 516 | - /*! | ||
| 517 | - The timestamp in TimeScale units of the latest available Fragment in the media presentation. | ||
| 518 | - This timestamp is used to request the right fragment number. | ||
| 519 | - The CurrentMedia Time can be the total duration. | ||
| 520 | - For media presentations that are not live, CurrentMediaTime can be 0. | ||
| 521 | - */ | ||
| 522 | - SrsHdsFragment *st = fragments.back(); | ||
| 523 | - abst.write_8bytes(st->get_start_time()); | ||
| 524 | - | ||
| 525 | - // SmpteTimeCodeOffset | ||
| 526 | - abst.write_8bytes(0); | ||
| 527 | - size_abst += 16; | ||
| 528 | - | ||
| 529 | - /*! | ||
| 530 | - @MovieIdentifier STRING | ||
| 531 | - The identifier of this presentation. | ||
| 532 | - we write null string. | ||
| 533 | - */ | ||
| 534 | - abst.write_1bytes(0); | ||
| 535 | - size_abst += 1; | ||
| 536 | - /*! | ||
| 537 | - @ServerEntryCount UI8 | ||
| 538 | - The number of ServerEntryTable entries. | ||
| 539 | - The minimum value is 0. | ||
| 540 | - */ | ||
| 541 | - abst.write_1bytes(0); | ||
| 542 | - size_abst += 1; | ||
| 543 | - /*! | ||
| 544 | - @ServerEntryTable | ||
| 545 | - because we write 0 of ServerEntryCount, so this feild is ignored. | ||
| 546 | - */ | ||
| 547 | - | ||
| 548 | - /*! | ||
| 549 | - @QualityEntryCount UI8 | ||
| 550 | - The number of QualityEntryTable entries, which is | ||
| 551 | - also the number of available quality levels. The | ||
| 552 | - minimum value is 0. Available quality levels are for, | ||
| 553 | - for example, multi bit rate files or trick files. | ||
| 554 | - */ | ||
| 555 | - abst.write_1bytes(0); | ||
| 556 | - size_abst += 1; | ||
| 557 | - /*! | ||
| 558 | - @QualityEntryTable | ||
| 559 | - because we write 0 of QualityEntryCount, so this feild is ignored. | ||
| 560 | - */ | ||
| 561 | - | ||
| 562 | - /*! | ||
| 563 | - @DrmData STRING | ||
| 564 | - Null or null-terminated UTF-8 string. This string | ||
| 565 | - holds Digital Rights Management metadata. | ||
| 566 | - Encrypted files use this metadata to get the | ||
| 567 | - necessary keys and licenses for decryption and play back. | ||
| 568 | - we write null string. | ||
| 569 | - */ | ||
| 570 | - abst.write_1bytes(0); | ||
| 571 | - size_abst += 1; | ||
| 572 | - /*! | ||
| 573 | - @MetaData STRING | ||
| 574 | - Null or null-terminated UTF - 8 string that holds metadata. | ||
| 575 | - we write null string. | ||
| 576 | - */ | ||
| 577 | - abst.write_1bytes(0); | ||
| 578 | - size_abst += 1; | ||
| 579 | - /*! | ||
| 580 | - @SegmentRunTableCount UI8 | ||
| 581 | - The number of entries in SegmentRunTableEntries. | ||
| 582 | - The minimum value is 1. Typically, one table | ||
| 583 | - contains all segment runs. However, this count | ||
| 584 | - provides the flexibility to define the segment runs | ||
| 585 | - individually for each quality level (or trick file). | ||
| 586 | - */ | ||
| 587 | - abst.write_1bytes(1); | ||
| 588 | - size_abst += 1; | ||
| 589 | - | ||
| 590 | - start_asrt = start_abst + size_abst; | ||
| 591 | - | ||
| 592 | - // follows by asrt | ||
| 593 | - abst.write_4bytes(0); | ||
| 594 | - abst.write_string("asrt"); | ||
| 595 | - size_asrt += 8; | ||
| 596 | - /*! | ||
| 597 | - @Version UI8 | ||
| 598 | - @Flags UI24 | ||
| 599 | - */ | ||
| 600 | - abst.write_4bytes(0); | ||
| 601 | - size_asrt += 4; | ||
| 602 | - /*! | ||
| 603 | - @QualityEntryCount UI8 | ||
| 604 | - The number of QualitySegmen tUrlModifiers | ||
| 605 | - (quality level references) that follow. If 0, this | ||
| 606 | - Segment Run Table applies to all quality levels, | ||
| 607 | - and there shall be only one Segment Run Table | ||
| 608 | - box in the Bootstrap Info box. | ||
| 609 | - */ | ||
| 610 | - abst.write_1bytes(0); | ||
| 611 | - size_asrt += 1; | ||
| 612 | - | ||
| 613 | - /*! | ||
| 614 | - @QualitySegmentUrlModifiers | ||
| 615 | - ignored. | ||
| 616 | - */ | ||
| 617 | - | ||
| 618 | - /*! | ||
| 619 | - @SegmentRunEntryCount | ||
| 620 | - The number of items in this | ||
| 621 | - SegmentRunEn tryTable. The minimum value is 1. | ||
| 622 | - */ | ||
| 623 | - abst.write_4bytes(1); | ||
| 624 | - size_asrt += 4; | ||
| 625 | - /*! | ||
| 626 | - @SegmentRunEntryTable | ||
| 627 | - */ | ||
| 628 | - for (int i = 0; i < 1; ++i) { | ||
| 629 | - /*! | ||
| 630 | - @FirstSegment UI32 | ||
| 631 | - The identifying number of the first segment in the run of | ||
| 632 | - segments containing the same number of fragments. | ||
| 633 | - The segment corresponding to the FirstSegment in the next | ||
| 634 | - SEGMENTRUNENTRY will terminate this run. | ||
| 635 | - */ | ||
| 636 | - abst.write_4bytes(1); | ||
| 637 | - | ||
| 638 | - /*! | ||
| 639 | - @FragmentsPerSegment UI32 | ||
| 640 | - The number of fragments in each segment in this run. | ||
| 641 | - */ | ||
| 642 | - abst.write_4bytes(fragment_index - 1); | ||
| 643 | - size_asrt += 8; | ||
| 644 | - } | ||
| 645 | - | ||
| 646 | - update_box(start_asrt, size_asrt); | ||
| 647 | - size_abst += size_asrt; | ||
| 648 | - | ||
| 649 | - /*! | ||
| 650 | - @FragmentRunTableCount UI8 | ||
| 651 | - The number of entries in FragmentRunTable-Entries. | ||
| 652 | - The min i mum value is 1. | ||
| 653 | - */ | ||
| 654 | - abst.write_1bytes(1); | ||
| 655 | - size_abst += 1; | ||
| 656 | - | ||
| 657 | - // follows by afrt | ||
| 658 | - start_afrt = start_abst + size_abst; | ||
| 659 | - | ||
| 660 | - abst.write_4bytes(0); | ||
| 661 | - abst.write_string("afrt"); | ||
| 662 | - size_afrt += 8; | ||
| 663 | - | ||
| 664 | - /*! | ||
| 665 | - @Version UI8 | ||
| 666 | - @Flags UI24 | ||
| 667 | - */ | ||
| 668 | - abst.write_4bytes(0); | ||
| 669 | - size_afrt += 4; | ||
| 670 | - /*! | ||
| 671 | - @TimeScale UI32 | ||
| 672 | - The number of time units per second, used in the FirstFragmentTime stamp and | ||
| 673 | - Fragment Duration fields. | ||
| 674 | - Typically, the value is 1000. | ||
| 675 | - */ | ||
| 676 | - abst.write_4bytes(1000); | ||
| 677 | - size_afrt += 4; | ||
| 678 | - /*! | ||
| 679 | - @QualityEntryCount UI8 | ||
| 680 | - The number of QualitySegment Url Modifiers | ||
| 681 | - (quality level references) that follow. | ||
| 682 | - If 0, this Fragment Run Table applies to all quality levels, | ||
| 683 | - and there shall be only one Fragment Run Table | ||
| 684 | - box in the Bootstrap Info box. | ||
| 685 | - */ | ||
| 686 | - abst.write_1bytes(0); | ||
| 687 | - size_afrt += 1; | ||
| 688 | - | ||
| 689 | - /*! | ||
| 690 | - @FragmentRunEntryCount UI32 | ||
| 691 | - The number of items in this FragmentRunEntryTable. | ||
| 692 | - The minimum value is 1. | ||
| 693 | - */ | ||
| 694 | - abst.write_4bytes(fragments.size()); | ||
| 695 | - size_afrt += 4; | ||
| 696 | - | ||
| 697 | - list<SrsHdsFragment *>::iterator iter; | ||
| 698 | - for (iter = fragments.begin(); iter != fragments.end(); ++iter) { | ||
| 699 | - SrsHdsFragment *st = *iter; | ||
| 700 | - abst.write_4bytes(st->get_index()); | ||
| 701 | - abst.write_8bytes(st->get_start_time()); | ||
| 702 | - abst.write_4bytes(st->duration()); | ||
| 703 | - size_afrt += 16; | ||
| 704 | - } | ||
| 705 | - | ||
| 706 | - update_box(start_afrt, size_afrt); | ||
| 707 | - size_abst += size_afrt; | ||
| 708 | - update_box(start_abst, size_abst); | ||
| 709 | - | ||
| 710 | - string path = _srs_config->get_hds_path(hds_req->vhost) + "/" + hds_req->app + "/" + hds_req->stream +".abst"; | ||
| 711 | - | ||
| 712 | - int fd = open(path.c_str(), O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); | ||
| 713 | - if (fd < 0) { | ||
| 714 | - srs_error("open bootstrap file failed, path=%s", path.c_str()); | ||
| 715 | - ret = ERROR_HDS_OPEN_BOOTSTRAP_FAILED; | ||
| 716 | - return ret; | ||
| 717 | - } | ||
| 718 | - | ||
| 719 | - if (write(fd, start_abst, size_abst) != size_abst) { | ||
| 720 | - srs_error("write bootstrap file failed, path=", path.c_str()); | ||
| 721 | - close(fd); | ||
| 722 | - ret = ERROR_HDS_WRITE_BOOTSTRAP_FAILED; | ||
| 723 | - return ret; | ||
| 724 | - } | ||
| 725 | - close(fd); | ||
| 726 | - | ||
| 727 | - srs_trace("build bootstrap success=%s", path.c_str()); | ||
| 728 | - | ||
| 729 | - return ERROR_SUCCESS; | ||
| 730 | -} | ||
| 731 | - | ||
| 732 | -void SrsHds::adjust_windows() | ||
| 733 | -{ | ||
| 734 | - int windows_size = 0; | ||
| 735 | - list<SrsHdsFragment *>::iterator iter; | ||
| 736 | - for (iter = fragments.begin(); iter != fragments.end(); ++iter) { | ||
| 737 | - SrsHdsFragment *fragment = *iter; | ||
| 738 | - windows_size += fragment->duration(); | ||
| 739 | - } | ||
| 740 | - | ||
| 741 | - double windows_size_limit = _srs_config->get_hds_window(hds_req->vhost) * 1000; | ||
| 742 | - if (windows_size > windows_size_limit ) { | ||
| 743 | - SrsHdsFragment *fragment = fragments.front(); | ||
| 744 | - unlink(fragment->fragment_path().c_str()); | ||
| 745 | - fragments.erase(fragments.begin()); | ||
| 746 | - srs_freep(fragment); | ||
| 747 | - } | ||
| 748 | -} | ||
| 749 | - | ||
| 750 | -#endif | 1 | +/* |
| 2 | +The MIT License (MIT) | ||
| 3 | + | ||
| 4 | +Copyright (c) 2013-2015 SRS(simple-rtmp-server) | ||
| 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 | +#include <srs_app_hds.hpp> | ||
| 24 | + | ||
| 25 | +#ifdef SRS_AUTO_HDS | ||
| 26 | + | ||
| 27 | +#include <unistd.h> | ||
| 28 | +#include <string> | ||
| 29 | +#include <vector> | ||
| 30 | +#include <sys/types.h> | ||
| 31 | +#include <sys/stat.h> | ||
| 32 | +#include <fcntl.h> | ||
| 33 | +using namespace std; | ||
| 34 | + | ||
| 35 | +#include <srs_app_hds.hpp> | ||
| 36 | +#include <srs_rtmp_stack.hpp> | ||
| 37 | +#include <srs_kernel_log.hpp> | ||
| 38 | +#include <srs_kernel_codec.hpp> | ||
| 39 | +#include <srs_rtmp_stack.hpp> | ||
| 40 | +#include <srs_kernel_stream.hpp> | ||
| 41 | +#include <srs_core_autofree.hpp> | ||
| 42 | +#include <srs_kernel_utility.hpp> | ||
| 43 | +#include <srs_app_config.hpp> | ||
| 44 | + | ||
| 45 | +static void update_box(char *start, int size) | ||
| 46 | +{ | ||
| 47 | + char *p_size = (char*)&size; | ||
| 48 | + start[0] = p_size[3]; | ||
| 49 | + start[1] = p_size[2]; | ||
| 50 | + start[2] = p_size[1]; | ||
| 51 | + start[3] = p_size[0]; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +char flv_header[] = {'F', 'L', 'V', | ||
| 55 | + 0x01, 0x05, 0x00, 0x00, 0x00, 0x09, | ||
| 56 | + 0x00, 0x00, 0x00, 0x00}; | ||
| 57 | + | ||
| 58 | +string serialFlv(SrsSharedPtrMessage *msg) | ||
| 59 | +{ | ||
| 60 | + SrsStream *stream = new SrsStream; | ||
| 61 | + | ||
| 62 | + int size = 15 + msg->size; | ||
| 63 | + char *byte = new char[size]; | ||
| 64 | + stream->initialize(byte, size); | ||
| 65 | + | ||
| 66 | + // tag header | ||
| 67 | + long long dts = msg->timestamp; | ||
| 68 | + char type = msg->is_video() ? 0x09 : 0x08; | ||
| 69 | + | ||
| 70 | + stream->write_1bytes(type); | ||
| 71 | + stream->write_3bytes(msg->size); | ||
| 72 | + stream->write_3bytes(dts); | ||
| 73 | + stream->write_1bytes(dts >> 24 & 0xFF); | ||
| 74 | + stream->write_3bytes(0); | ||
| 75 | + stream->write_bytes(msg->payload, msg->size); | ||
| 76 | + | ||
| 77 | + // pre tag size | ||
| 78 | + int preTagSize = msg->size + 11; | ||
| 79 | + stream->write_4bytes(preTagSize); | ||
| 80 | + | ||
| 81 | + string ret(stream->data(), stream->size()); | ||
| 82 | + | ||
| 83 | + delete stream; | ||
| 84 | + delete [] byte; | ||
| 85 | + | ||
| 86 | + return ret; | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +class SrsHdsFragment | ||
| 90 | +{ | ||
| 91 | +public: | ||
| 92 | + SrsHdsFragment(SrsRequest *r) | ||
| 93 | + : req(r) | ||
| 94 | + , index(-1) | ||
| 95 | + , start_time(0) | ||
| 96 | + , videoSh(NULL) | ||
| 97 | + , audioSh(NULL) | ||
| 98 | + { | ||
| 99 | + | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + ~SrsHdsFragment() | ||
| 103 | + { | ||
| 104 | + srs_freep(videoSh); | ||
| 105 | + srs_freep(audioSh); | ||
| 106 | + | ||
| 107 | + // clean msgs | ||
| 108 | + list<SrsSharedPtrMessage *>::iterator iter; | ||
| 109 | + for (iter = msgs.begin(); iter != msgs.end(); ++iter) { | ||
| 110 | + SrsSharedPtrMessage *msg = *iter; | ||
| 111 | + srs_freep(msg); | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + void on_video(SrsSharedPtrMessage *msg) | ||
| 116 | + { | ||
| 117 | + SrsSharedPtrMessage *_msg = msg->copy(); | ||
| 118 | + msgs.push_back(_msg); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + void on_audio(SrsSharedPtrMessage *msg) | ||
| 122 | + { | ||
| 123 | + SrsSharedPtrMessage *_msg = msg->copy(); | ||
| 124 | + msgs.push_back(_msg); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + /*! | ||
| 128 | + flush data to disk. | ||
| 129 | + */ | ||
| 130 | + int flush() | ||
| 131 | + { | ||
| 132 | + string data; | ||
| 133 | + if (videoSh) { | ||
| 134 | + videoSh->timestamp = start_time; | ||
| 135 | + data.append(serialFlv(videoSh)); | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + if (audioSh) { | ||
| 139 | + audioSh->timestamp = start_time; | ||
| 140 | + data.append(serialFlv(audioSh)); | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + list<SrsSharedPtrMessage *>::iterator iter; | ||
| 144 | + for (iter = msgs.begin(); iter != msgs.end(); ++iter) { | ||
| 145 | + SrsSharedPtrMessage *msg = *iter; | ||
| 146 | + data.append(serialFlv(msg)); | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + char box_header[8]; | ||
| 150 | + SrsStream ss; | ||
| 151 | + ss.initialize(box_header, 8); | ||
| 152 | + ss.write_4bytes(8 + data.size()); | ||
| 153 | + ss.write_string("mdat"); | ||
| 154 | + | ||
| 155 | + data = string(ss.data(), ss.size()) + data; | ||
| 156 | + | ||
| 157 | + const char *file_path = path.c_str(); | ||
| 158 | + int fd = open(file_path, O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); | ||
| 159 | + if (fd < 0) { | ||
| 160 | + srs_error("open fragment file failed, path=%s", file_path); | ||
| 161 | + return -1; | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + if (write(fd, data.data(), data.size()) != (int)data.size()) { | ||
| 165 | + srs_error("write fragment file failed, path=", file_path); | ||
| 166 | + close(fd); | ||
| 167 | + return -1; | ||
| 168 | + } | ||
| 169 | + close(fd); | ||
| 170 | + | ||
| 171 | + srs_trace("build fragment success=%s", file_path); | ||
| 172 | + | ||
| 173 | + return ERROR_SUCCESS; | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + /*! | ||
| 177 | + calc the segment duration in milliseconds. | ||
| 178 | + @return 0 if no msgs | ||
| 179 | + or the last msg dts minus the first msg dts. | ||
| 180 | + */ | ||
| 181 | + int duration() | ||
| 182 | + { | ||
| 183 | + int duration_ms = 0; | ||
| 184 | + long long first_msg_ts = 0; | ||
| 185 | + long long last_msg_ts = 0; | ||
| 186 | + | ||
| 187 | + if (msgs.size() >= 2) { | ||
| 188 | + SrsSharedPtrMessage *first_msg = msgs.front(); | ||
| 189 | + first_msg_ts = first_msg->timestamp; | ||
| 190 | + | ||
| 191 | + SrsSharedPtrMessage *last_msg = msgs.back(); | ||
| 192 | + last_msg_ts = last_msg->timestamp; | ||
| 193 | + | ||
| 194 | + duration_ms = last_msg_ts - first_msg_ts; | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + return duration_ms; | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + /*! | ||
| 201 | + set/get index | ||
| 202 | + */ | ||
| 203 | + inline void set_index(int idx) | ||
| 204 | + { | ||
| 205 | + char file_path[1024] = {0}; | ||
| 206 | + sprintf(file_path, "%s/%s/%sSeg1-Frag%d", _srs_config->get_hds_path(req->vhost).c_str() | ||
| 207 | + , req->app.c_str(), req->stream.c_str(), idx); | ||
| 208 | + | ||
| 209 | + path = file_path; | ||
| 210 | + index = idx; | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | + inline int get_index() | ||
| 214 | + { | ||
| 215 | + return index; | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + /*! | ||
| 219 | + set/get start time | ||
| 220 | + */ | ||
| 221 | + inline void set_start_time(long long st) | ||
| 222 | + { | ||
| 223 | + start_time = st; | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + inline long long get_start_time() | ||
| 227 | + { | ||
| 228 | + return start_time; | ||
| 229 | + } | ||
| 230 | + | ||
| 231 | + void set_video_sh(SrsSharedPtrMessage *msg) | ||
| 232 | + { | ||
| 233 | + srs_freep(videoSh); | ||
| 234 | + videoSh = msg->copy(); | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + void set_audio_sh(SrsSharedPtrMessage *msg) | ||
| 238 | + { | ||
| 239 | + srs_freep(audioSh); | ||
| 240 | + audioSh = msg->copy(); | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + string fragment_path() | ||
| 244 | + { | ||
| 245 | + return path; | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | +private: | ||
| 249 | + SrsRequest *req; | ||
| 250 | + list<SrsSharedPtrMessage *> msgs; | ||
| 251 | + | ||
| 252 | + /*! | ||
| 253 | + the index of this fragment | ||
| 254 | + */ | ||
| 255 | + int index; | ||
| 256 | + long long start_time; | ||
| 257 | + | ||
| 258 | + SrsSharedPtrMessage *videoSh; | ||
| 259 | + SrsSharedPtrMessage *audioSh; | ||
| 260 | + string path; | ||
| 261 | +}; | ||
| 262 | + | ||
| 263 | +SrsHds::SrsHds(SrsSource *s) | ||
| 264 | + : currentSegment(NULL) | ||
| 265 | + , fragment_index(1) | ||
| 266 | + , video_sh(NULL) | ||
| 267 | + , audio_sh(NULL) | ||
| 268 | + , hds_req(NULL) | ||
| 269 | + , hds_enabled(false) | ||
| 270 | +{ | ||
| 271 | + | ||
| 272 | +} | ||
| 273 | + | ||
| 274 | +SrsHds::~SrsHds() | ||
| 275 | +{ | ||
| 276 | + | ||
| 277 | +} | ||
| 278 | + | ||
| 279 | +int SrsHds::on_publish(SrsRequest *req) | ||
| 280 | +{ | ||
| 281 | + int ret = ERROR_SUCCESS; | ||
| 282 | + if (hds_enabled) { | ||
| 283 | + return ret; | ||
| 284 | + } | ||
| 285 | + | ||
| 286 | + std::string vhost = req->vhost; | ||
| 287 | + if (!_srs_config->get_hds_enabled(vhost)) { | ||
| 288 | + hds_enabled = false; | ||
| 289 | + return ret; | ||
| 290 | + } | ||
| 291 | + hds_enabled = true; | ||
| 292 | + | ||
| 293 | + hds_req = req->copy(); | ||
| 294 | + | ||
| 295 | + return flush_mainfest(); | ||
| 296 | +} | ||
| 297 | + | ||
| 298 | +int SrsHds::on_unpublish() | ||
| 299 | +{ | ||
| 300 | + int ret = ERROR_SUCCESS; | ||
| 301 | + | ||
| 302 | + if (!hds_enabled) { | ||
| 303 | + return ret; | ||
| 304 | + } | ||
| 305 | + | ||
| 306 | + hds_enabled = false; | ||
| 307 | + | ||
| 308 | + srs_freep(video_sh); | ||
| 309 | + srs_freep(audio_sh); | ||
| 310 | + srs_freep(hds_req); | ||
| 311 | + | ||
| 312 | + // clean fragments | ||
| 313 | + list<SrsHdsFragment *>::iterator iter; | ||
| 314 | + for (iter = fragments.begin(); iter != fragments.end(); ++iter) { | ||
| 315 | + SrsHdsFragment *st = *iter; | ||
| 316 | + srs_freep(st); | ||
| 317 | + } | ||
| 318 | + fragments.clear(); | ||
| 319 | + | ||
| 320 | + srs_freep(currentSegment); | ||
| 321 | + | ||
| 322 | + srs_trace("HDS un-published"); | ||
| 323 | + | ||
| 324 | + return ret; | ||
| 325 | +} | ||
| 326 | + | ||
| 327 | +int SrsHds::on_video(SrsSharedPtrMessage* msg) | ||
| 328 | +{ | ||
| 329 | + int ret = ERROR_SUCCESS; | ||
| 330 | + | ||
| 331 | + if (!hds_enabled) { | ||
| 332 | + return ret; | ||
| 333 | + } | ||
| 334 | + | ||
| 335 | + if (SrsFlvCodec::video_is_sequence_header(msg->payload, msg->size)) { | ||
| 336 | + srs_freep(video_sh); | ||
| 337 | + video_sh = msg->copy(); | ||
| 338 | + } | ||
| 339 | + | ||
| 340 | + if (!currentSegment) { | ||
| 341 | + currentSegment = new SrsHdsFragment(hds_req); | ||
| 342 | + currentSegment->set_index(fragment_index++); | ||
| 343 | + currentSegment->set_start_time(msg->timestamp); | ||
| 344 | + | ||
| 345 | + if (video_sh) | ||
| 346 | + currentSegment->set_video_sh(video_sh); | ||
| 347 | + | ||
| 348 | + if (audio_sh) | ||
| 349 | + currentSegment->set_audio_sh(audio_sh); | ||
| 350 | + } | ||
| 351 | + | ||
| 352 | + currentSegment->on_video(msg); | ||
| 353 | + | ||
| 354 | + double fragment_duration = _srs_config->get_hds_fragment(hds_req->vhost) * 1000; | ||
| 355 | + if (currentSegment->duration() >= fragment_duration) { | ||
| 356 | + // flush segment | ||
| 357 | + if ((ret = currentSegment->flush()) != ERROR_SUCCESS) { | ||
| 358 | + srs_error("flush segment failed."); | ||
| 359 | + return ret; | ||
| 360 | + } | ||
| 361 | + | ||
| 362 | + srs_trace("flush Segment success."); | ||
| 363 | + fragments.push_back(currentSegment); | ||
| 364 | + currentSegment = NULL; | ||
| 365 | + adjust_windows(); | ||
| 366 | + | ||
| 367 | + // flush bootstrap | ||
| 368 | + if ((ret = flush_bootstrap()) != ERROR_SUCCESS) { | ||
| 369 | + srs_error("flush bootstrap failed."); | ||
| 370 | + return ret; | ||
| 371 | + } | ||
| 372 | + | ||
| 373 | + srs_trace("flush BootStrap success."); | ||
| 374 | + } | ||
| 375 | + | ||
| 376 | + return ret; | ||
| 377 | +} | ||
| 378 | + | ||
| 379 | +int SrsHds::on_audio(SrsSharedPtrMessage* msg) | ||
| 380 | +{ | ||
| 381 | + int ret = ERROR_SUCCESS; | ||
| 382 | + | ||
| 383 | + if (!hds_enabled) { | ||
| 384 | + return ret; | ||
| 385 | + } | ||
| 386 | + | ||
| 387 | + if (SrsFlvCodec::audio_is_sequence_header(msg->payload, msg->size)) { | ||
| 388 | + srs_freep(audio_sh); | ||
| 389 | + audio_sh = msg->copy(); | ||
| 390 | + } | ||
| 391 | + | ||
| 392 | + if (!currentSegment) { | ||
| 393 | + currentSegment = new SrsHdsFragment(hds_req); | ||
| 394 | + currentSegment->set_index(fragment_index++); | ||
| 395 | + currentSegment->set_start_time(msg->timestamp); | ||
| 396 | + | ||
| 397 | + if (video_sh) | ||
| 398 | + currentSegment->set_video_sh(video_sh); | ||
| 399 | + | ||
| 400 | + if (audio_sh) | ||
| 401 | + currentSegment->set_audio_sh(audio_sh); | ||
| 402 | + } | ||
| 403 | + | ||
| 404 | + currentSegment->on_audio(msg); | ||
| 405 | + | ||
| 406 | + double fragment_duration = _srs_config->get_hds_fragment(hds_req->vhost) * 1000; | ||
| 407 | + if (currentSegment->duration() >= fragment_duration) { | ||
| 408 | + // flush segment | ||
| 409 | + if ((ret = currentSegment->flush()) != ERROR_SUCCESS) { | ||
| 410 | + srs_error("flush segment failed."); | ||
| 411 | + return ret; | ||
| 412 | + } | ||
| 413 | + | ||
| 414 | + srs_info("flush Segment success."); | ||
| 415 | + | ||
| 416 | + // reset the current segment | ||
| 417 | + fragments.push_back(currentSegment); | ||
| 418 | + currentSegment = NULL; | ||
| 419 | + adjust_windows(); | ||
| 420 | + | ||
| 421 | + // flush bootstrap | ||
| 422 | + if ((ret = flush_bootstrap()) != ERROR_SUCCESS) { | ||
| 423 | + srs_error("flush bootstrap failed."); | ||
| 424 | + return ret; | ||
| 425 | + } | ||
| 426 | + | ||
| 427 | + srs_info("flush BootStrap success."); | ||
| 428 | + } | ||
| 429 | + | ||
| 430 | + return ret; | ||
| 431 | +} | ||
| 432 | + | ||
| 433 | +int SrsHds::flush_mainfest() | ||
| 434 | +{ | ||
| 435 | + int ret = ERROR_SUCCESS; | ||
| 436 | + | ||
| 437 | + char buf[1024] = {0}; | ||
| 438 | + sprintf(buf, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" | ||
| 439 | + "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n\t" | ||
| 440 | + "<id>%s.f4m</id>\n\t" | ||
| 441 | + "<streamType>live</streamType>\n\t" | ||
| 442 | + "<deliveryType>streaming</deliveryType>\n\t" | ||
| 443 | + "<bootstrapInfo profile=\"named\" url=\"%s.abst\" id=\"bootstrap0\" />\n\t" | ||
| 444 | + "<media bitrate=\"0\" url=\"%s\" bootstrapInfoId=\"bootstrap0\"></media>\n" | ||
| 445 | + "</manifest>" | ||
| 446 | + , hds_req->stream.c_str(), hds_req->stream.c_str(), hds_req->stream.c_str()); | ||
| 447 | + | ||
| 448 | + string dir = _srs_config->get_hds_path(hds_req->vhost) + "/" + hds_req->app; | ||
| 449 | + if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) { | ||
| 450 | + srs_error("hds create dir failed. ret=%d", ret); | ||
| 451 | + return ret; | ||
| 452 | + } | ||
| 453 | + string path = dir + "/" + hds_req->stream + ".f4m"; | ||
| 454 | + | ||
| 455 | + int fd = open(path.c_str(), O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); | ||
| 456 | + if (fd < 0) { | ||
| 457 | + srs_error("open manifest file failed, path=%s", path.c_str()); | ||
| 458 | + ret = ERROR_HDS_OPEN_F4M_FAILED; | ||
| 459 | + return ret; | ||
| 460 | + } | ||
| 461 | + | ||
| 462 | + int f4m_size = strlen(buf); | ||
| 463 | + if (write(fd, buf, f4m_size) != f4m_size) { | ||
| 464 | + srs_error("write manifest file failed, path=", path.c_str()); | ||
| 465 | + close(fd); | ||
| 466 | + ret = ERROR_HDS_WRITE_F4M_FAILED; | ||
| 467 | + return ret; | ||
| 468 | + } | ||
| 469 | + close(fd); | ||
| 470 | + | ||
| 471 | + srs_trace("build manifest success=%s", path.c_str()); | ||
| 472 | + | ||
| 473 | + return ERROR_SUCCESS; | ||
| 474 | +} | ||
| 475 | + | ||
| 476 | +int SrsHds::flush_bootstrap() | ||
| 477 | +{ | ||
| 478 | + int ret = ERROR_SUCCESS; | ||
| 479 | + | ||
| 480 | + SrsStream abst; | ||
| 481 | + | ||
| 482 | + int size = 1024*100; | ||
| 483 | + | ||
| 484 | + char *start_abst = new char[1024*100]; | ||
| 485 | + SrsAutoFree(char, start_abst); | ||
| 486 | + | ||
| 487 | + int size_abst = 0; | ||
| 488 | + char *start_asrt = NULL; | ||
| 489 | + int size_asrt = 0; | ||
| 490 | + char *start_afrt = NULL; | ||
| 491 | + int size_afrt = 0; | ||
| 492 | + | ||
| 493 | + if ((ret = abst.initialize(start_abst, size)) != ERROR_SUCCESS) { | ||
| 494 | + return ret; | ||
| 495 | + } | ||
| 496 | + | ||
| 497 | + // @see video_file_format_spec_v10_1 | ||
| 498 | + // page: 46 | ||
| 499 | + abst.write_4bytes(0); | ||
| 500 | + abst.write_string("abst"); | ||
| 501 | + abst.write_1bytes(0x00); // Either 0 or 1 | ||
| 502 | + abst.write_3bytes(0x00); // Flags always 0 | ||
| 503 | + size_abst += 12; | ||
| 504 | + /*! | ||
| 505 | + @BootstrapinfoVersion UI32 | ||
| 506 | + The version number of the bootstrap information. | ||
| 507 | + When the Update field is set, BootstrapinfoVersion | ||
| 508 | + indicates the version number that is being updated. | ||
| 509 | + we assume this is the last. | ||
| 510 | + */ | ||
| 511 | + abst.write_4bytes(fragment_index - 1); // BootstrapinfoVersion | ||
| 512 | + | ||
| 513 | + abst.write_1bytes(0x20); // profile, live, update | ||
| 514 | + abst.write_4bytes(1000); // TimeScale Typically, the value is 1000, for a unit of milliseconds | ||
| 515 | + size_abst += 9; | ||
| 516 | + /*! | ||
| 517 | + The timestamp in TimeScale units of the latest available Fragment in the media presentation. | ||
| 518 | + This timestamp is used to request the right fragment number. | ||
| 519 | + The CurrentMedia Time can be the total duration. | ||
| 520 | + For media presentations that are not live, CurrentMediaTime can be 0. | ||
| 521 | + */ | ||
| 522 | + SrsHdsFragment *st = fragments.back(); | ||
| 523 | + abst.write_8bytes(st->get_start_time()); | ||
| 524 | + | ||
| 525 | + // SmpteTimeCodeOffset | ||
| 526 | + abst.write_8bytes(0); | ||
| 527 | + size_abst += 16; | ||
| 528 | + | ||
| 529 | + /*! | ||
| 530 | + @MovieIdentifier STRING | ||
| 531 | + The identifier of this presentation. | ||
| 532 | + we write null string. | ||
| 533 | + */ | ||
| 534 | + abst.write_1bytes(0); | ||
| 535 | + size_abst += 1; | ||
| 536 | + /*! | ||
| 537 | + @ServerEntryCount UI8 | ||
| 538 | + The number of ServerEntryTable entries. | ||
| 539 | + The minimum value is 0. | ||
| 540 | + */ | ||
| 541 | + abst.write_1bytes(0); | ||
| 542 | + size_abst += 1; | ||
| 543 | + /*! | ||
| 544 | + @ServerEntryTable | ||
| 545 | + because we write 0 of ServerEntryCount, so this feild is ignored. | ||
| 546 | + */ | ||
| 547 | + | ||
| 548 | + /*! | ||
| 549 | + @QualityEntryCount UI8 | ||
| 550 | + The number of QualityEntryTable entries, which is | ||
| 551 | + also the number of available quality levels. The | ||
| 552 | + minimum value is 0. Available quality levels are for, | ||
| 553 | + for example, multi bit rate files or trick files. | ||
| 554 | + */ | ||
| 555 | + abst.write_1bytes(0); | ||
| 556 | + size_abst += 1; | ||
| 557 | + /*! | ||
| 558 | + @QualityEntryTable | ||
| 559 | + because we write 0 of QualityEntryCount, so this feild is ignored. | ||
| 560 | + */ | ||
| 561 | + | ||
| 562 | + /*! | ||
| 563 | + @DrmData STRING | ||
| 564 | + Null or null-terminated UTF-8 string. This string | ||
| 565 | + holds Digital Rights Management metadata. | ||
| 566 | + Encrypted files use this metadata to get the | ||
| 567 | + necessary keys and licenses for decryption and play back. | ||
| 568 | + we write null string. | ||
| 569 | + */ | ||
| 570 | + abst.write_1bytes(0); | ||
| 571 | + size_abst += 1; | ||
| 572 | + /*! | ||
| 573 | + @MetaData STRING | ||
| 574 | + Null or null-terminated UTF - 8 string that holds metadata. | ||
| 575 | + we write null string. | ||
| 576 | + */ | ||
| 577 | + abst.write_1bytes(0); | ||
| 578 | + size_abst += 1; | ||
| 579 | + /*! | ||
| 580 | + @SegmentRunTableCount UI8 | ||
| 581 | + The number of entries in SegmentRunTableEntries. | ||
| 582 | + The minimum value is 1. Typically, one table | ||
| 583 | + contains all segment runs. However, this count | ||
| 584 | + provides the flexibility to define the segment runs | ||
| 585 | + individually for each quality level (or trick file). | ||
| 586 | + */ | ||
| 587 | + abst.write_1bytes(1); | ||
| 588 | + size_abst += 1; | ||
| 589 | + | ||
| 590 | + start_asrt = start_abst + size_abst; | ||
| 591 | + | ||
| 592 | + // follows by asrt | ||
| 593 | + abst.write_4bytes(0); | ||
| 594 | + abst.write_string("asrt"); | ||
| 595 | + size_asrt += 8; | ||
| 596 | + /*! | ||
| 597 | + @Version UI8 | ||
| 598 | + @Flags UI24 | ||
| 599 | + */ | ||
| 600 | + abst.write_4bytes(0); | ||
| 601 | + size_asrt += 4; | ||
| 602 | + /*! | ||
| 603 | + @QualityEntryCount UI8 | ||
| 604 | + The number of QualitySegmen tUrlModifiers | ||
| 605 | + (quality level references) that follow. If 0, this | ||
| 606 | + Segment Run Table applies to all quality levels, | ||
| 607 | + and there shall be only one Segment Run Table | ||
| 608 | + box in the Bootstrap Info box. | ||
| 609 | + */ | ||
| 610 | + abst.write_1bytes(0); | ||
| 611 | + size_asrt += 1; | ||
| 612 | + | ||
| 613 | + /*! | ||
| 614 | + @QualitySegmentUrlModifiers | ||
| 615 | + ignored. | ||
| 616 | + */ | ||
| 617 | + | ||
| 618 | + /*! | ||
| 619 | + @SegmentRunEntryCount | ||
| 620 | + The number of items in this | ||
| 621 | + SegmentRunEn tryTable. The minimum value is 1. | ||
| 622 | + */ | ||
| 623 | + abst.write_4bytes(1); | ||
| 624 | + size_asrt += 4; | ||
| 625 | + /*! | ||
| 626 | + @SegmentRunEntryTable | ||
| 627 | + */ | ||
| 628 | + for (int i = 0; i < 1; ++i) { | ||
| 629 | + /*! | ||
| 630 | + @FirstSegment UI32 | ||
| 631 | + The identifying number of the first segment in the run of | ||
| 632 | + segments containing the same number of fragments. | ||
| 633 | + The segment corresponding to the FirstSegment in the next | ||
| 634 | + SEGMENTRUNENTRY will terminate this run. | ||
| 635 | + */ | ||
| 636 | + abst.write_4bytes(1); | ||
| 637 | + | ||
| 638 | + /*! | ||
| 639 | + @FragmentsPerSegment UI32 | ||
| 640 | + The number of fragments in each segment in this run. | ||
| 641 | + */ | ||
| 642 | + abst.write_4bytes(fragment_index - 1); | ||
| 643 | + size_asrt += 8; | ||
| 644 | + } | ||
| 645 | + | ||
| 646 | + update_box(start_asrt, size_asrt); | ||
| 647 | + size_abst += size_asrt; | ||
| 648 | + | ||
| 649 | + /*! | ||
| 650 | + @FragmentRunTableCount UI8 | ||
| 651 | + The number of entries in FragmentRunTable-Entries. | ||
| 652 | + The min i mum value is 1. | ||
| 653 | + */ | ||
| 654 | + abst.write_1bytes(1); | ||
| 655 | + size_abst += 1; | ||
| 656 | + | ||
| 657 | + // follows by afrt | ||
| 658 | + start_afrt = start_abst + size_abst; | ||
| 659 | + | ||
| 660 | + abst.write_4bytes(0); | ||
| 661 | + abst.write_string("afrt"); | ||
| 662 | + size_afrt += 8; | ||
| 663 | + | ||
| 664 | + /*! | ||
| 665 | + @Version UI8 | ||
| 666 | + @Flags UI24 | ||
| 667 | + */ | ||
| 668 | + abst.write_4bytes(0); | ||
| 669 | + size_afrt += 4; | ||
| 670 | + /*! | ||
| 671 | + @TimeScale UI32 | ||
| 672 | + The number of time units per second, used in the FirstFragmentTime stamp and | ||
| 673 | + Fragment Duration fields. | ||
| 674 | + Typically, the value is 1000. | ||
| 675 | + */ | ||
| 676 | + abst.write_4bytes(1000); | ||
| 677 | + size_afrt += 4; | ||
| 678 | + /*! | ||
| 679 | + @QualityEntryCount UI8 | ||
| 680 | + The number of QualitySegment Url Modifiers | ||
| 681 | + (quality level references) that follow. | ||
| 682 | + If 0, this Fragment Run Table applies to all quality levels, | ||
| 683 | + and there shall be only one Fragment Run Table | ||
| 684 | + box in the Bootstrap Info box. | ||
| 685 | + */ | ||
| 686 | + abst.write_1bytes(0); | ||
| 687 | + size_afrt += 1; | ||
| 688 | + | ||
| 689 | + /*! | ||
| 690 | + @FragmentRunEntryCount UI32 | ||
| 691 | + The number of items in this FragmentRunEntryTable. | ||
| 692 | + The minimum value is 1. | ||
| 693 | + */ | ||
| 694 | + abst.write_4bytes(fragments.size()); | ||
| 695 | + size_afrt += 4; | ||
| 696 | + | ||
| 697 | + list<SrsHdsFragment *>::iterator iter; | ||
| 698 | + for (iter = fragments.begin(); iter != fragments.end(); ++iter) { | ||
| 699 | + SrsHdsFragment *st = *iter; | ||
| 700 | + abst.write_4bytes(st->get_index()); | ||
| 701 | + abst.write_8bytes(st->get_start_time()); | ||
| 702 | + abst.write_4bytes(st->duration()); | ||
| 703 | + size_afrt += 16; | ||
| 704 | + } | ||
| 705 | + | ||
| 706 | + update_box(start_afrt, size_afrt); | ||
| 707 | + size_abst += size_afrt; | ||
| 708 | + update_box(start_abst, size_abst); | ||
| 709 | + | ||
| 710 | + string path = _srs_config->get_hds_path(hds_req->vhost) + "/" + hds_req->app + "/" + hds_req->stream +".abst"; | ||
| 711 | + | ||
| 712 | + int fd = open(path.c_str(), O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); | ||
| 713 | + if (fd < 0) { | ||
| 714 | + srs_error("open bootstrap file failed, path=%s", path.c_str()); | ||
| 715 | + ret = ERROR_HDS_OPEN_BOOTSTRAP_FAILED; | ||
| 716 | + return ret; | ||
| 717 | + } | ||
| 718 | + | ||
| 719 | + if (write(fd, start_abst, size_abst) != size_abst) { | ||
| 720 | + srs_error("write bootstrap file failed, path=", path.c_str()); | ||
| 721 | + close(fd); | ||
| 722 | + ret = ERROR_HDS_WRITE_BOOTSTRAP_FAILED; | ||
| 723 | + return ret; | ||
| 724 | + } | ||
| 725 | + close(fd); | ||
| 726 | + | ||
| 727 | + srs_trace("build bootstrap success=%s", path.c_str()); | ||
| 728 | + | ||
| 729 | + return ERROR_SUCCESS; | ||
| 730 | +} | ||
| 731 | + | ||
| 732 | +void SrsHds::adjust_windows() | ||
| 733 | +{ | ||
| 734 | + int windows_size = 0; | ||
| 735 | + list<SrsHdsFragment *>::iterator iter; | ||
| 736 | + for (iter = fragments.begin(); iter != fragments.end(); ++iter) { | ||
| 737 | + SrsHdsFragment *fragment = *iter; | ||
| 738 | + windows_size += fragment->duration(); | ||
| 739 | + } | ||
| 740 | + | ||
| 741 | + double windows_size_limit = _srs_config->get_hds_window(hds_req->vhost) * 1000; | ||
| 742 | + if (windows_size > windows_size_limit ) { | ||
| 743 | + SrsHdsFragment *fragment = fragments.front(); | ||
| 744 | + unlink(fragment->fragment_path().c_str()); | ||
| 745 | + fragments.erase(fragments.begin()); | ||
| 746 | + srs_freep(fragment); | ||
| 747 | + } | ||
| 748 | +} | ||
| 749 | + | ||
| 750 | +#endif |
| 1 | -/* | ||
| 2 | -The MIT License (MIT) | ||
| 3 | - | ||
| 4 | -Copyright (c) 2013-2015 SRS(simple-rtmp-server) | ||
| 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 | -#ifndef SRS_APP_HDS_HPP | ||
| 25 | -#define SRS_APP_HDS_HPP | ||
| 26 | - | ||
| 27 | -#include <srs_core.hpp> | ||
| 28 | - | ||
| 29 | -#ifdef SRS_AUTO_HDS | ||
| 30 | - | ||
| 31 | -#include <list> | ||
| 32 | - | ||
| 33 | -class SrsRequest; | ||
| 34 | -class SrsSharedPtrMessage; | ||
| 35 | -class SrsHdsFragment; | ||
| 36 | -class SrsSource; | ||
| 37 | - | ||
| 38 | -class SrsHds | ||
| 39 | -{ | ||
| 40 | -public: | ||
| 41 | - SrsHds(SrsSource* s); | ||
| 42 | - ~SrsHds(); | ||
| 43 | - | ||
| 44 | - int on_publish(SrsRequest* req); | ||
| 45 | - int on_unpublish(); | ||
| 46 | - | ||
| 47 | - int on_video(SrsSharedPtrMessage* msg); | ||
| 48 | - int on_audio(SrsSharedPtrMessage* msg); | ||
| 49 | - | ||
| 50 | -private: | ||
| 51 | - int flush_mainfest(); | ||
| 52 | - int flush_bootstrap(); | ||
| 53 | - void adjust_windows(); | ||
| 54 | - | ||
| 55 | -private: | ||
| 56 | - std::list<SrsHdsFragment *> fragments; | ||
| 57 | - SrsHdsFragment *currentSegment; | ||
| 58 | - int fragment_index; | ||
| 59 | - SrsSharedPtrMessage *video_sh; | ||
| 60 | - SrsSharedPtrMessage *audio_sh; | ||
| 61 | - | ||
| 62 | - SrsRequest *hds_req; | ||
| 63 | - bool hds_enabled; | ||
| 64 | -}; | ||
| 65 | - | ||
| 66 | -#endif | ||
| 67 | - | ||
| 68 | -#endif | 1 | +/* |
| 2 | +The MIT License (MIT) | ||
| 3 | + | ||
| 4 | +Copyright (c) 2013-2015 SRS(simple-rtmp-server) | ||
| 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 | +#ifndef SRS_APP_HDS_HPP | ||
| 25 | +#define SRS_APP_HDS_HPP | ||
| 26 | + | ||
| 27 | +#include <srs_core.hpp> | ||
| 28 | + | ||
| 29 | +#ifdef SRS_AUTO_HDS | ||
| 30 | + | ||
| 31 | +#include <list> | ||
| 32 | + | ||
| 33 | +class SrsRequest; | ||
| 34 | +class SrsSharedPtrMessage; | ||
| 35 | +class SrsHdsFragment; | ||
| 36 | +class SrsSource; | ||
| 37 | + | ||
| 38 | +class SrsHds | ||
| 39 | +{ | ||
| 40 | +public: | ||
| 41 | + SrsHds(SrsSource* s); | ||
| 42 | + ~SrsHds(); | ||
| 43 | + | ||
| 44 | + int on_publish(SrsRequest* req); | ||
| 45 | + int on_unpublish(); | ||
| 46 | + | ||
| 47 | + int on_video(SrsSharedPtrMessage* msg); | ||
| 48 | + int on_audio(SrsSharedPtrMessage* msg); | ||
| 49 | + | ||
| 50 | +private: | ||
| 51 | + int flush_mainfest(); | ||
| 52 | + int flush_bootstrap(); | ||
| 53 | + void adjust_windows(); | ||
| 54 | + | ||
| 55 | +private: | ||
| 56 | + std::list<SrsHdsFragment *> fragments; | ||
| 57 | + SrsHdsFragment *currentSegment; | ||
| 58 | + int fragment_index; | ||
| 59 | + SrsSharedPtrMessage *video_sh; | ||
| 60 | + SrsSharedPtrMessage *audio_sh; | ||
| 61 | + | ||
| 62 | + SrsRequest *hds_req; | ||
| 63 | + bool hds_enabled; | ||
| 64 | +}; | ||
| 65 | + | ||
| 66 | +#endif | ||
| 67 | + | ||
| 68 | +#endif |
trunk/src/app/srs_app_rtmp_conn.cpp
100755 → 100644
trunk/src/app/srs_app_rtmp_conn.hpp
100755 → 100644
trunk/src/app/srs_app_source.cpp
100755 → 100644
trunk/src/app/srs_app_source.hpp
100755 → 100644
-
请 注册 或 登录 后发表评论