1 /*
2 * Copyright (c) 2012 Martin Storsjo
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /*
22 * To create a simple file for smooth streaming:
23 * ffmpeg <normal input/transcoding options> -movflags frag_keyframe foo.ismv
24 * ismindex -n foo foo.ismv
25 * This step creates foo.ism and foo.ismc that is required by IIS for
26 * serving it.
27 *
28 * To pre-split files for serving as static files by a web server without
29 * any extra server support, create the ismv file as above, and split it:
30 * ismindex -split foo.ismv
31 * This step creates a file Manifest and directories QualityLevel(...),
32 * that can be read directly by a smooth streaming player.
33 */
34
35 #include <stdio.h>
36 #include <string.h>
37
39
44
46 {
47 fprintf(stderr, "%s [-split] [-n basename] file1 [file2] ...\n", argv0);
49 }
50
55 };
56
73 };
74
81 };
82
84 {
86
91 if (tag != tag_name)
92 return -1;
93 size -= 8;
94 while (size > 0) {
98 break;
101 }
102 return 0;
103 }
104
106 {
109
114
117
119 }
120
123 {
124 char dirname[100], filename[500];
125 int i, j;
126
127 for (i = start_index; i < tracks->
nb_tracks; i++) {
130 snprintf(dirname,
sizeof(dirname),
"QualityLevels(%d)", track->
bitrate);
131 if (mkdir(dirname, 0777) == -1)
133 for (j = 0; j < track->
chunks; j++) {
134 snprintf(filename,
sizeof(filename),
"%s/Fragments(%s=%"PRId64
")",
138 }
139 }
140 return 0;
141 }
142
144 {
146 int version, fieldlength, i, j;
149 struct Track *track = NULL;
150
152 goto fail;
156 for (i = start_index; i < tracks->
nb_tracks && !track; i++)
158 track = tracks->
tracks[i];
159 if (!track) {
160 /* Ok, continue parsing the next atom */
161 ret = 0;
162 goto fail;
163 }
169 goto fail;
170 }
171 for (i = 0; i < track->
chunks; i++) {
172 if (version == 1) {
175 } else {
178 }
179 for (j = 0; j < ((fieldlength >> 4) & 3) + 1; j++)
181 for (j = 0; j < ((fieldlength >> 2) & 3) + 1; j++)
183 for (j = 0; j < ((fieldlength >> 0) & 3) + 1; j++)
185 if (i > 0)
188 }
192 ret = 0;
193
194 fail:
197 }
198
200 const char *file,
int split)
201 {
202 int err = 0;
205
207 goto fail;
213 goto fail;
214 }
217 goto fail;
218 }
219 while (!
read_tfra(tracks, start_index, f)) {
220 /* Empty */
221 }
222
223 if (split)
225
226 fail:
227 if (f)
229 if (err)
230 fprintf(stderr, "Unable to read the MFRA atom in %s\n", file);
231 return err;
232 }
233
235 {
241 return 0;
242 }
243
245 {
247 uint16_t sps_size, pps_size;
249
252
255 goto fail;
256 }
258 goto fail;
261 goto fail;
266 goto fail;
269 err = 0;
270
271 fail:
273 return err;
274 }
275
277 {
279 int err = 0, i, orig_tracks = tracks->
nb_tracks;
280 char errbuf[50], *ptr;
282
284 if (err < 0) {
286 fprintf(stderr, "Unable to open %s: %s\n", file, errbuf);
287 return 1;
288 }
289
291 if (err < 0) {
293 fprintf(stderr, "Unable to identify %s: %s\n", file, errbuf);
294 goto fail;
295 }
296
298 fprintf(stderr, "No streams found in %s\n", file);
299 goto fail;
300 }
301
306 if (!track) {
308 goto fail;
309 }
312 if (!temp) {
315 goto fail;
316 }
319
321 if ((ptr = strrchr(file, '/')) != NULL)
322 track->
name = ptr + 1;
323
330
332 fprintf(stderr,
333 "Track %d in %s is neither video nor audio, skipping\n",
336 continue;
337 }
338
342
357 }
359 }
371 }
372
374 }
375
377
378 err =
read_mfra(tracks, orig_tracks, file, split);
379
380 fail:
381 if (ctx)
383 return err;
384 }
385
387 const char *basename)
388 {
389 char filename[1000];
391 int i;
392
393 snprintf(filename,
sizeof(filename),
"%s.ism", basename);
394 out = fopen(filename, "w");
395 if (!out) {
396 perror(filename);
397 return;
398 }
399 fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
400 fprintf(out, "<smil xmlns=\"http://www.w3.org/2001/SMIL20/Language\">\n");
401 fprintf(out, "\t<head>\n");
402 fprintf(out, "\t\t<meta name=\"clientManifestRelativePath\" "
403 "content=\"%s.ismc\" />\n", basename);
404 fprintf(out, "\t</head>\n");
405 fprintf(out, "\t<body>\n");
406 fprintf(out, "\t\t<switch>\n");
407 for (i = 0; i < tracks->
nb_tracks; i++) {
410 fprintf(out, "\t\t\t<%s src=\"%s\" systemBitrate=\"%d\">\n",
412 fprintf(out, "\t\t\t\t<param name=\"trackID\" value=\"%d\" "
413 "valueType=\"data\" />\n", track->
track_id);
414 fprintf(out, "\t\t\t</%s>\n", type);
415 }
416 fprintf(out, "\t\t</switch>\n");
417 fprintf(out, "\t</body>\n");
418 fprintf(out, "</smil>\n");
419 fclose(out);
420 }
421
424 {
425 int i, j;
427 for (i = 0; i < track->
chunks; i++) {
428 for (j = main + 1; j < tracks->
nb_tracks; j++) {
431 fprintf(stderr, "Mismatched duration of %s chunk %d in %s and %s\n",
433 }
434 fprintf(out, "\t\t<c n=\"%d\" d=\"%"PRId64"\" />\n",
436 }
437 }
438
440 const char *basename,
int split)
441 {
442 char filename[1000];
444 int i, j;
445
446 if (split)
447 snprintf(filename,
sizeof(filename),
"Manifest");
448 else
449 snprintf(filename,
sizeof(filename),
"%s.ismc", basename);
450 out = fopen(filename, "w");
451 if (!out) {
452 perror(filename);
453 return;
454 }
455 fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
456 fprintf(out, "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" "
457 "Duration=\"%"PRId64
"\">\n", tracks->
duration * 10);
460 struct Track *first_track = track;
462 fprintf(out,
463 "\t<StreamIndex Type=\"video\" QualityLevels=\"%d\" "
464 "Chunks=\"%d\" "
465 "Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n",
467 for (i = 0; i < tracks->
nb_tracks; i++) {
468 track = tracks->
tracks[i];
470 continue;
471 fprintf(out,
472 "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
473 "FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" "
474 "CodecPrivateData=\"",
478 fprintf(out, "\" />\n");
479 index++;
481 fprintf(stderr, "Mismatched number of video chunks in %s and %s\n",
483 }
485 fprintf(out, "\t</StreamIndex>\n");
486 }
489 struct Track *first_track = track;
491 fprintf(out,
492 "\t<StreamIndex Type=\"audio\" QualityLevels=\"%d\" "
493 "Chunks=\"%d\" "
494 "Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n",
496 for (i = 0; i < tracks->
nb_tracks; i++) {
497 track = tracks->
tracks[i];
499 continue;
500 fprintf(out,
501 "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
502 "FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" "
503 "BitsPerSample=\"16\" PacketSize=\"%d\" "
504 "AudioTag=\"%d\" CodecPrivateData=\"",
509 fprintf(out, "\" />\n");
510 index++;
512 fprintf(stderr, "Mismatched number of audio chunks in %s and %s\n",
514 }
516 fprintf(out, "\t</StreamIndex>\n");
517 }
518 fprintf(out, "</SmoothStreamingMedia>\n");
519 fclose(out);
520 }
521
523 {
524 int i;
525 for (i = 0; i < tracks->
nb_tracks; i++) {
529 }
532 }
533
534 int main(
int argc,
char **argv)
535 {
536 const char *basename = NULL;
539
541
542 for (i = 1; i < argc; i++) {
543 if (!strcmp(argv[i], "-n")) {
544 basename = argv[i + 1];
545 i++;
546 } else if (!strcmp(argv[i], "-split")) {
547 split = 1;
548 } else if (argv[i][0] == '-') {
549 return usage(argv[0], 1);
550 } else {
552 return 1;
553 }
554 }
555 if (!tracks.
nb_tracks || (!basename && !split))
556 return usage(argv[0], 1);
557
558 if (!split)
561
563
564 return 0;
565 }