winlin

refine code, to 644 and unix encoding.

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