1 /*
2 * Live smooth streaming fragmenter
3 * Copyright (c) 2012 Martin Storsjo
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include "config.h"
24 #if HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27
34
40
48
54 URLContext *
out;
// Current output stream where all output is written
55 URLContext *
out2;
// Auxiliary output stream where all output is also written
56 URLContext *
tail_out;
// The actual main output stream, if we're currently seeked back to write elsewhere
62
68
70 const AVClass *
class;
/* Class for private options. */
80
82 {
91 return buf_size;
92 }
93
95 {
97 int i;
98 if (whence != SEEK_SET)
103 }
106 }
110 }
116 }
119 if (offset >= frag->
start_pos && offset < frag->start_pos + frag->
size) {
126 if (ret < 0) {
130 }
139 }
140 }
142 }
143
145 {
149 int i;
152 if (!ptr)
154 }
155 if (!ptr)
156 return;
159 goto fail;
160 for (i = 0; i <
size; i++)
162 fail:
165 }
166
168 {
170 int i, j;
172 return;
189 }
191 }
192
194 {
195 int removed = 0, i,
start = 0;
197 return;
199 removed = 1;
200 if (final)
201 skip = 0;
202 if (window_size)
206 if (!final || removed)
208 else
210 }
211 }
212
214 {
217 char filename[1024], temp_filename[1024];
218 int ret, i, video_chunks = 0, audio_chunks = 0, video_streams = 0, audio_streams = 0;
220
222 snprintf(temp_filename,
sizeof(temp_filename),
"%s/Manifest.tmp", s->
filename);
224 if (ret < 0) {
227 }
228 avio_printf(out,
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
234 }
237 video_streams++;
238 } else {
240 audio_streams++;
241 }
242 }
243 if (!final) {
244 duration = 0;
245 video_chunks = audio_chunks = 0;
246 }
250 }
251 avio_printf(out,
"<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" Duration=\"%"PRIu64
"\"", duration);
252 if (!final)
256 int last = -1,
index = 0;
257 avio_printf(out,
"<StreamIndex Type=\"video\" QualityLevels=\"%d\" Chunks=\"%d\" Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n", video_streams, video_chunks);
261 continue;
262 last = i;
265 }
268 }
270 int last = -1,
index = 0;
271 avio_printf(out,
"<StreamIndex Type=\"audio\" QualityLevels=\"%d\" Chunks=\"%d\" Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n", audio_streams, audio_chunks);
275 continue;
276 last = i;
279 }
282 }
286 return ff_rename(temp_filename, filename, s);
287 }
288
290 {
294
295 if (mkdir(s->
filename, 0777) == -1 && errno != EEXIST) {
298 goto fail;
299 }
300
302 if (!oformat) {
304 goto fail;
305 }
306
310 goto fail;
311 }
312
318
322 goto fail;
323 }
325 if (mkdir(os->
dirname, 0777) == -1 && errno != EEXIST) {
328 goto fail;
329 }
330
332 if (!ctx) {
334 goto fail;
335 }
339
342 goto fail;
343 }
347
351 goto fail;
352 }
353
357 goto fail;
358 }
370 } else {
373 goto fail;
374 }
375 } else {
384 } else {
387 goto fail;
388 }
390 }
392 }
393
397 goto fail;
398 }
400
401 fail:
402 if (ret)
405 }
406
408 {
416 if (*moof_size < 8 || *moof_size > size)
417 goto fail;
419 goto fail;
421 if (len > *moof_size)
422 goto fail;
424 goto fail;
428 goto fail;
433 if (len < 8 || len >= *moof_size)
434 goto fail;
435 if (tag ==
MKTAG(
'u',
'u',
'i',
'd')) {
436 static const uint8_t tfxd[] = {
437 0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
438 0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
439 };
442 if (!memcmp(uuid, tfxd, 16) && len >= 8 + 16 + 4 + 16) {
446 ret = 0;
447 break;
448 }
449 }
451 }
452 fail:
455 }
456
458 {
459 int err;
467 return err;
468 }
469 }
471 if (!frag)
482 return 0;
483 }
484
486 {
494 }
495 while (size > 0) {
497 int n =
FFMIN(size,
sizeof(buf));
499 if (n <= 0) {
501 break;
502 }
505 }
510 }
511
513 {
516
519 char filename[1024], target_filename[1024], header_filename[1024];
521 int64_t start_ts,
duration, moof_size;
523 continue;
524
527 if (ret < 0)
528 break;
535
539 if ((ret =
parse_fragment(s, filename, &start_ts, &duration, &moof_size, size)) < 0)
540 break;
543 copy_moof(s, filename, header_filename, moof_size);
544 ret =
ff_rename(filename, target_filename, s);
545 if (ret < 0)
546 break;
547 add_fragment(os, target_filename, header_filename, start_ts, duration,
549 }
550
554 int j;
558 if (remove > 0) {
559 for (j = 0; j < remove; j++) {
563 }
566 }
569 }
570 }
571
572 if (ret >= 0)
575 }
576
578 {
584
587
592
594 return ret;
596 }
597
600 }
601
603 {
606
608 char filename[1024];
610 unlink(filename);
612 }
613
615 return 0;
616 }
617
618 #define OFFSET(x) offsetof(SmoothStreamingContext, x)
619 #define E AV_OPT_FLAG_ENCODING_PARAM
621 {
"window_size",
"number of fragments kept in the manifest",
OFFSET(window_size),
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX,
E },
622 {
"extra_window_size",
"number of fragments kept outside of the manifest before removing from disk",
OFFSET(extra_window_size),
AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX,
E },
623 {
"lookahead_count",
"number of lookahead fragments",
OFFSET(lookahead_count),
AV_OPT_TYPE_INT, { .i64 = 2 }, 0, INT_MAX,
E },
624 {
"min_frag_duration",
"minimum fragment duration (in microseconds)",
OFFSET(min_frag_duration),
AV_OPT_TYPE_INT64, { .i64 = 5000000 }, 0, INT_MAX,
E },
625 {
"remove_at_exit",
"remove all fragments when finished",
OFFSET(remove_at_exit),
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1,
E },
627 };
628
634 };
635
636
638 .
name =
"smoothstreaming",
649 };