1 /*****************************************************************
2 |
3 | AP4 - MP4 to HLS File Converter
4 |
5 | Copyright 2002-2018 Axiomatic Systems, LLC
6 |
7 |
8 | This file is part of Bento4/AP4 (MP4 Atom Processing Library).
9 |
10 | Unless you have obtained Bento4 under a difference license,
11 | this version of Bento4 is Bento4|GPL.
12 | Bento4|GPL is free software; you can redistribute it and/or modify
13 | it under the terms of the GNU General Public License as published by
14 | the Free Software Foundation; either version 2, or (at your option)
15 | any later version.
16 |
17 | Bento4|GPL is distributed in the hope that it will be useful,
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | GNU General Public License for more details.
21 |
22 | You should have received a copy of the GNU General Public License
23 | along with Bento4|GPL; see the file COPYING. If not, write to the
24 | Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
25 | 02111-1307, USA.
26 |
27 ****************************************************************/
28
29 /*----------------------------------------------------------------------
30 | includes
31 +---------------------------------------------------------------------*/
32 #include <stdio.h>
33 #include <stdlib.h>
34
35 #include "Ap4.h"
36 #include "Ap4StreamCipher.h"
37 #include "Ap4Mp4AudioInfo.h"
38
39 /*----------------------------------------------------------------------
40 | constants
41 +---------------------------------------------------------------------*/
42 #define BANNER "MP4 To HLS File Converter - Version 1.2\n"\
43 "(Bento4 Version " AP4_VERSION_STRING ")\n"\
44 "(c) 2002-2018 Axiomatic Systems, LLC"
45
46 /*----------------------------------------------------------------------
47 | options
48 +---------------------------------------------------------------------*/
49 typedef enum {
50 ENCRYPTION_MODE_NONE,
51 ENCRYPTION_MODE_AES_128,
52 ENCRYPTION_MODE_SAMPLE_AES
53 } EncryptionMode;
54
55 typedef enum {
56 ENCRYPTION_IV_MODE_NONE,
57 ENCRYPTION_IV_MODE_SEQUENCE,
58 ENCRYPTION_IV_MODE_RANDOM,
59 ENCRYPTION_IV_MODE_FPS
60 } EncryptionIvMode;
61
62 typedef enum {
63 AUDIO_FORMAT_TS,
64 AUDIO_FORMAT_PACKED
65 } AudioFormat;
66
67 static struct _Options {
68 const char* input;
69 bool verbose;
70 unsigned int hls_version;
71 unsigned int pmt_pid;
72 unsigned int audio_pid;
73 unsigned int video_pid;
74 int audio_track_id;
75 int video_track_id;
76 AudioFormat audio_format;
77 const char* index_filename;
78 const char* iframe_index_filename;
79 bool output_single_file;
80 bool show_info;
81 const char* segment_filename_template;
82 const char* segment_url_template;
83 unsigned int segment_duration;
84 unsigned int segment_duration_threshold;
85 const char* encryption_key_hex;
86 AP4_UI08 encryption_key[16];
87 AP4_UI08 encryption_iv[16];
88 EncryptionMode encryption_mode;
89 EncryptionIvMode encryption_iv_mode;
90 const char* encryption_key_uri;
91 const char* encryption_key_format;
92 const char* encryption_key_format_versions;
93 AP4_Array<AP4_String> encryption_key_lines;
94 AP4_UI64 pcr_offset;
95 } Options;
96
97 static struct _Stats {
98 AP4_UI64 segments_total_size;
99 double segments_total_duration;
100 AP4_UI32 segment_count;
101 double max_segment_bitrate;
102 AP4_UI64 iframes_total_size;
103 AP4_UI32 iframe_count;
104 double max_iframe_bitrate;
105 } Stats;
106
107 /*----------------------------------------------------------------------
108 | constants
109 +---------------------------------------------------------------------*/
110 static const unsigned int DefaultSegmentDurationThreshold = 15; // milliseconds
111
112 const AP4_UI08 AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_AVC = 0xDB;
113 const AP4_UI08 AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ISO_IEC_13818_7 = 0xCF;
114 const AP4_UI08 AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ATSC_AC3 = 0xC1;
115 const AP4_UI08 AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ATSC_EAC3 = 0xC2;
116 const AP4_UI08 AP4_MPEG2_PRIVATE_DATA_INDICATOR_DESCRIPTOR_TAG = 15;
117 const AP4_UI08 AP4_MPEG2_REGISTRATION_DESCRIPTOR_TAG = 5;
118
119 /*----------------------------------------------------------------------
120 | PrintUsageAndExit
121 +---------------------------------------------------------------------*/
122 static void
PrintUsageAndExit()123 PrintUsageAndExit()
124 {
125 fprintf(stderr,
126 BANNER
127 "\n\nusage: mp42hls [options] <input>\n"
128 "Options:\n"
129 " --verbose\n"
130 " --show-info\n"
131 " --hls-version <n> (default: 3)\n"
132 " --pmt-pid <pid>\n"
133 " PID to use for the PMT (default: 0x100)\n"
134 " --audio-pid <pid>\n"
135 " PID to use for audio packets (default: 0x101)\n"
136 " --video-pid <pid>\n"
137 " PID to use for video packets (default: 0x102)\n"
138 " --audio-track-id <n>\n"
139 " Read audio from track ID <n> (0 means no audio)\n"
140 " --video-track-id <n>\n"
141 " Read video from track ID <n> (0 means no video)\n"
142 " --audio-format <format>\n"
143 " Format to use for audio-only segments: 'ts' or 'packed' (default: 'ts')\n"
144 " --segment-duration <n>\n"
145 " Target segment duration in seconds (default: 6)\n"
146 " --segment-duration-threshold <t>\n"
147 " Segment duration threshold in milliseconds (default: 15)\n"
148 " --pcr-offset <offset> in units of 90kHz (default 10000)\n"
149 " --index-filename <filename>\n"
150 " Filename to use for the playlist/index (default: stream.m3u8)\n"
151 " --segment-filename-template <pattern>\n"
152 " Filename pattern to use for the segments. Use a printf-style pattern with\n"
153 " one number field for the segment number, unless using single file mode\n"
154 " (default: segment-%%d.<ext> for separate segment files, or stream.<ext> for single file)\n"
155 " --segment-url-template <pattern>\n"
156 " URL pattern to use for the segments. Use a printf-style pattern with\n"
157 " one number field for the segment number unless unsing single file mode.\n"
158 " (may be a relative or absolute URI).\n"
159 " (default: segment-%%d.<ext> for separate segment files, or stream.<ext> for single file)\n"
160 " --iframe-index-filename <filename>\n"
161 " Filename to use for the I-Frame playlist (default: iframes.m3u8 when HLS version >= 4)\n"
162 " --output-single-file\n"
163 " Output all the media in a single file instead of separate segment files.\n"
164 " The segment filename template and segment URL template must be simple strings\n"
165 " without '%%d' or other printf-style patterns\n"
166 " --encryption-mode <mode>\n"
167 " Encryption mode (only used when --encryption-key is specified). AES-128 or SAMPLE-AES (default: AES-128)\n"
168 " --encryption-key <key>\n"
169 " Encryption key in hexadecimal (default: no encryption)\n"
170 " --encryption-iv-mode <mode>\n"
171 " Encryption IV mode: 'sequence', 'random' or 'fps' (FairPlay Streaming) (default: sequence)\n"
172 " (when the mode is 'fps', the encryption key must be 32 bytes: 16 bytes for the key\n"
173 " followed by 16 bytes for the IV).\n"
174 " --encryption-key-uri <uri>\n"
175 " Encryption key URI (may be a realtive or absolute URI). (default: key.bin)\n"
176 " --encryption-key-format <format>\n"
177 " Encryption key format. (default: 'identity')\n"
178 " --encryption-key-format-versions <versions>\n"
179 " Encryption key format versions.\n"
180 " --encryption-key-line <ext-x-key-line>\n"
181 " Preformatted encryption key line (only the portion after the #EXT-X-KEY: tag).\n"
182 " This option can be used multiple times, once for each preformatted key line to be included in the playlist.\n"
183 " (this option is mutually exclusive with the --encryption-key-uri, --encryption-key-format and --encryption-key-format-versions options)\n"
184 " (the IV and METHOD parameters will automatically be added, so they must not appear in the <ext-x-key-line> argument)\n"
185 );
186 exit(1);
187 }
188
189 /*----------------------------------------------------------------------
190 | SampleReader
191 +---------------------------------------------------------------------*/
192 class SampleReader
193 {
194 public:
~SampleReader()195 virtual ~SampleReader() {}
196 virtual AP4_Result ReadSample(AP4_Sample& sample, AP4_DataBuffer& sample_data) = 0;
197 };
198
199 /*----------------------------------------------------------------------
200 | TrackSampleReader
201 +---------------------------------------------------------------------*/
202 class TrackSampleReader : public SampleReader
203 {
204 public:
TrackSampleReader(AP4_Track & track)205 TrackSampleReader(AP4_Track& track) : m_Track(track), m_SampleIndex(0) {}
206 AP4_Result ReadSample(AP4_Sample& sample, AP4_DataBuffer& sample_data);
207
208 private:
209 AP4_Track& m_Track;
210 AP4_Ordinal m_SampleIndex;
211 };
212
213 /*----------------------------------------------------------------------
214 | TrackSampleReader
215 +---------------------------------------------------------------------*/
216 AP4_Result
ReadSample(AP4_Sample & sample,AP4_DataBuffer & sample_data)217 TrackSampleReader::ReadSample(AP4_Sample& sample, AP4_DataBuffer& sample_data)
218 {
219 if (m_SampleIndex >= m_Track.GetSampleCount()) return AP4_ERROR_EOS;
220 return m_Track.ReadSample(m_SampleIndex++, sample, sample_data);
221 }
222
223 /*----------------------------------------------------------------------
224 | FragmentedSampleReader
225 +---------------------------------------------------------------------*/
226 class FragmentedSampleReader : public SampleReader
227 {
228 public:
FragmentedSampleReader(AP4_LinearReader & fragment_reader,AP4_UI32 track_id)229 FragmentedSampleReader(AP4_LinearReader& fragment_reader, AP4_UI32 track_id) :
230 m_FragmentReader(fragment_reader), m_TrackId(track_id) {
231 fragment_reader.EnableTrack(track_id);
232 }
233 AP4_Result ReadSample(AP4_Sample& sample, AP4_DataBuffer& sample_data);
234
235 private:
236 AP4_LinearReader& m_FragmentReader;
237 AP4_UI32 m_TrackId;
238 };
239
240 /*----------------------------------------------------------------------
241 | FragmentedSampleReader
242 +---------------------------------------------------------------------*/
243 AP4_Result
ReadSample(AP4_Sample & sample,AP4_DataBuffer & sample_data)244 FragmentedSampleReader::ReadSample(AP4_Sample& sample, AP4_DataBuffer& sample_data)
245 {
246 return m_FragmentReader.ReadNextSample(m_TrackId, sample, sample_data);
247 }
248
249 /*----------------------------------------------------------------------
250 | OpenOutput
251 +---------------------------------------------------------------------*/
252 static AP4_ByteStream*
OpenOutput(const char * filename_pattern,unsigned int segment_number)253 OpenOutput(const char* filename_pattern, unsigned int segment_number)
254 {
255 AP4_ByteStream* output = NULL;
256 char filename[4096];
257 sprintf(filename, filename_pattern, segment_number);
258 AP4_Result result = AP4_FileByteStream::Create(filename, AP4_FileByteStream::STREAM_MODE_WRITE, output);
259 if (AP4_FAILED(result)) {
260 fprintf(stderr, "ERROR: cannot open output (%d)\n", result);
261 return NULL;
262 }
263
264 return output;
265 }
266
267 /*----------------------------------------------------------------------
268 | PreventStartCodeEmulation
269 +---------------------------------------------------------------------*/
270 static void
PreventStartCodeEmulation(const AP4_UI08 * payload,AP4_Size payload_size,AP4_DataBuffer & output)271 PreventStartCodeEmulation(const AP4_UI08* payload, AP4_Size payload_size, AP4_DataBuffer& output)
272 {
273 output.Reserve(payload_size*2); // more than enough
274 AP4_Size output_size = 0;
275 AP4_UI08* buffer = output.UseData();
276
277 unsigned int zero_counter = 0;
278 for (unsigned int i = 0; i < payload_size; i++) {
279 if (zero_counter == 2) {
280 if (payload[i] == 0 || payload[i] == 1 || payload[i] == 2 || payload[i] == 3) {
281 buffer[output_size++] = 3;
282 zero_counter = 0;
283 }
284 }
285
286 buffer[output_size++] = payload[i];
287
288 if (payload[i] == 0) {
289 ++zero_counter;
290 } else {
291 zero_counter = 0;
292 }
293 }
294
295 output.SetDataSize(output_size);
296 }
297
298 /*----------------------------------------------------------------------
299 | EncryptingStream
300 +---------------------------------------------------------------------*/
301 class EncryptingStream: public AP4_ByteStream {
302 public:
303 static AP4_Result Create(const AP4_UI08* key, const AP4_UI08* iv, AP4_ByteStream* output, EncryptingStream*& stream);
ReadPartial(void *,AP4_Size,AP4_Size &)304 virtual AP4_Result ReadPartial(void* , AP4_Size, AP4_Size&) {
305 return AP4_ERROR_NOT_SUPPORTED;
306 }
WritePartial(const void * buffer,AP4_Size bytes_to_write,AP4_Size & bytes_written)307 virtual AP4_Result WritePartial(const void* buffer,
308 AP4_Size bytes_to_write,
309 AP4_Size& bytes_written) {
310 AP4_UI08* out = new AP4_UI08[bytes_to_write+16];
311 AP4_Size out_size = bytes_to_write+16;
312 AP4_Result result = m_StreamCipher->ProcessBuffer((const AP4_UI08*)buffer,
313 bytes_to_write,
314 out,
315 &out_size);
316 if (AP4_SUCCEEDED(result)) {
317 result = m_Output->Write(out, out_size);
318 bytes_written = bytes_to_write;
319 m_Size += bytes_to_write;
320 } else {
321 bytes_written = 0;
322 }
323 delete[] out;
324 return result;
325 }
Flush()326 virtual AP4_Result Flush() {
327 AP4_UI08 trailer[16];
328 AP4_Size trailer_size = sizeof(trailer);
329 AP4_Result result = m_StreamCipher->ProcessBuffer(NULL, 0, trailer, &trailer_size, true);
330 if (AP4_SUCCEEDED(result) && trailer_size) {
331 m_Output->Write(trailer, trailer_size);
332 m_Size += 16-(m_Size%16);
333 }
334
335 return AP4_SUCCESS;
336 }
Seek(AP4_Position)337 virtual AP4_Result Seek(AP4_Position) { return AP4_ERROR_NOT_SUPPORTED; }
Tell(AP4_Position & position)338 virtual AP4_Result Tell(AP4_Position& position) { position = m_Size; return AP4_SUCCESS; }
GetSize(AP4_LargeSize & size)339 virtual AP4_Result GetSize(AP4_LargeSize& size) { size = m_Size; return AP4_SUCCESS; }
340
AddReference()341 void AddReference() {
342 ++m_ReferenceCount;
343 }
Release()344 void Release() {
345 if (--m_ReferenceCount == 0) {
346 delete this;
347 }
348 }
349
350 private:
EncryptingStream(AP4_CbcStreamCipher * stream_cipher,AP4_ByteStream * output)351 EncryptingStream(AP4_CbcStreamCipher* stream_cipher, AP4_ByteStream* output):
352 m_ReferenceCount(1),
353 m_StreamCipher(stream_cipher),
354 m_Output(output),
355 m_Size(0) {
356 output->AddReference();
357 }
~EncryptingStream()358 ~EncryptingStream() {
359 m_Output->Release();
360 delete m_StreamCipher;
361 }
362 unsigned int m_ReferenceCount;
363 AP4_CbcStreamCipher* m_StreamCipher;
364 AP4_ByteStream* m_Output;
365 AP4_LargeSize m_Size;
366 };
367
368 /*----------------------------------------------------------------------
369 | EncryptingStream::Create
370 +---------------------------------------------------------------------*/
371 AP4_Result
Create(const AP4_UI08 * key,const AP4_UI08 * iv,AP4_ByteStream * output,EncryptingStream * & stream)372 EncryptingStream::Create(const AP4_UI08* key, const AP4_UI08* iv, AP4_ByteStream* output, EncryptingStream*& stream) {
373 stream = NULL;
374 AP4_BlockCipher* block_cipher = NULL;
375 AP4_Result result = AP4_DefaultBlockCipherFactory::Instance.CreateCipher(AP4_BlockCipher::AES_128,
376 AP4_BlockCipher::ENCRYPT,
377 AP4_BlockCipher::CBC,
378 NULL,
379 key,
380 16,
381 block_cipher);
382 if (AP4_FAILED(result)) return result;
383 AP4_CbcStreamCipher* stream_cipher = new AP4_CbcStreamCipher(block_cipher);
384 stream_cipher->SetIV(iv);
385 stream = new EncryptingStream(stream_cipher, output);
386
387 return AP4_SUCCESS;
388 }
389
390 /*----------------------------------------------------------------------
391 | SampleEncrypter
392 +---------------------------------------------------------------------*/
393 class SampleEncrypter {
394 public:
395 static AP4_Result Create(const AP4_UI08* key, const AP4_UI08* iv, SampleEncrypter*& encrypter);
~SampleEncrypter()396 ~SampleEncrypter() {
397 delete m_StreamCipher;
398 }
399
400 AP4_Result EncryptAudioSample(AP4_DataBuffer& sample, AP4_SampleDescription* sample_description);
401 AP4_Result EncryptVideoSample(AP4_DataBuffer& sample, AP4_UI08 nalu_length_size);
402
403 private:
SampleEncrypter(AP4_CbcStreamCipher * stream_cipher,const AP4_UI08 * iv)404 SampleEncrypter(AP4_CbcStreamCipher* stream_cipher, const AP4_UI08* iv):
405 m_StreamCipher(stream_cipher) {
406 AP4_CopyMemory(m_IV, iv, 16);
407 }
408
409 AP4_CbcStreamCipher* m_StreamCipher;
410 AP4_UI08 m_IV[16];
411 };
412
413 /*----------------------------------------------------------------------
414 | SampleEncrypter::Create
415 +---------------------------------------------------------------------*/
416 AP4_Result
Create(const AP4_UI08 * key,const AP4_UI08 * iv,SampleEncrypter * & encrypter)417 SampleEncrypter::Create(const AP4_UI08* key, const AP4_UI08* iv, SampleEncrypter*& encrypter) {
418 encrypter = NULL;
419 AP4_BlockCipher* block_cipher = NULL;
420 AP4_Result result = AP4_DefaultBlockCipherFactory::Instance.CreateCipher(AP4_BlockCipher::AES_128,
421 AP4_BlockCipher::ENCRYPT,
422 AP4_BlockCipher::CBC,
423 NULL,
424 key,
425 16,
426 block_cipher);
427 if (AP4_FAILED(result)) return result;
428 AP4_CbcStreamCipher* stream_cipher = new AP4_CbcStreamCipher(block_cipher);
429 encrypter = new SampleEncrypter(stream_cipher, iv);
430
431 return AP4_SUCCESS;
432 }
433
434 /*----------------------------------------------------------------------
435 | SampleEncrypter::EncryptAudioSample
436 |
437 | AAC: entire frame
438 | ADTS_header // 7 or 9
439 | unencrypted_leader // 16 bytes
440 | while (bytes_remaining() >= 16) {
441 | protected_block // 16 bytes
442 | }
443 | unencrypted_trailer // 0-15 bytes
444 |
445 | AC3: entire frame
446 | unencrypted_leader // 16 bytes
447 | while (bytes_remaining() >= 16) {
448 | encrypted_block // 16 bytes
449 | }
450 | unencrypted_trailer // 0-15 bytes
451 |
452 | E-AC3: each syncframe
453 | unencrypted_leader // 16 bytes
454 | while (bytes_remaining() >= 16) {
455 | encrypted_block // 16 bytes
456 | }
457 | unencrypted_trailer // 0-15 bytes
458 |
459 +---------------------------------------------------------------------*/
460 AP4_Result
EncryptAudioSample(AP4_DataBuffer & sample,AP4_SampleDescription * sample_description)461 SampleEncrypter::EncryptAudioSample(AP4_DataBuffer& sample, AP4_SampleDescription* sample_description)
462 {
463 if (sample.GetDataSize() <= 16) {
464 return AP4_SUCCESS;
465 }
466
467 // deal with E-AC3 syncframes
468 if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
469 // syncword : 16
470 // strmtyp : 2
471 // substreamid : 3
472 // frmsiz : 11
473 AP4_UI08* data = sample.UseData();
474 unsigned int data_size = sample.GetDataSize();
475 while (data_size > 4) {
476 unsigned int syncword = (data[0]<<8) | data[1];
477 if (syncword != 0x0b77) {
478 return AP4_ERROR_INVALID_FORMAT; // unexpected!
479 }
480 unsigned int frmsiz = 1+(((data[2]<<8)|(data[3]))&0x7FF);
481 unsigned int frame_size = 2*frmsiz;
482 if (data_size < frame_size) {
483 return AP4_ERROR_INVALID_FORMAT; // unexpected!
484 }
485
486 // encrypt the syncframe
487 if (frame_size > 16) {
488 unsigned int encrypted_block_count = (frame_size-16)/16;
489 AP4_Size encrypted_size = encrypted_block_count*16;
490 m_StreamCipher->SetIV(m_IV);
491 m_StreamCipher->ProcessBuffer(data+16, encrypted_size, data+16, &encrypted_size);
492 }
493
494 data += frame_size;
495 data_size -= frame_size;
496 }
497 } else {
498 unsigned int encrypted_block_count = (sample.GetDataSize()-16)/16;
499 AP4_Size encrypted_size = encrypted_block_count*16;
500 m_StreamCipher->SetIV(m_IV);
501 m_StreamCipher->ProcessBuffer(sample.UseData()+16, encrypted_size, sample.UseData()+16, &encrypted_size);
502 }
503
504 return AP4_SUCCESS;
505 }
506
507 /*----------------------------------------------------------------------
508 | SampleEncrypter::EncryptVideoSample
509 |
510 | Sequence of NAL Units:
511 | NAL_unit_type_byte // 1 byte
512 | unencrypted_leader // 31 bytes
513 | while (bytes_remaining() > 16) {
514 | protected_block_one_in_ten // 16 bytes
515 | }
516 | unencrypted_trailer // 1-16 bytes
517 +---------------------------------------------------------------------*/
518 AP4_Result
EncryptVideoSample(AP4_DataBuffer & sample,AP4_UI08 nalu_length_size)519 SampleEncrypter::EncryptVideoSample(AP4_DataBuffer& sample, AP4_UI08 nalu_length_size)
520 {
521 AP4_DataBuffer encrypted;
522
523 AP4_UI08* nalu = sample.UseData();
524 AP4_Size bytes_remaining = sample.GetDataSize();
525 while (bytes_remaining > nalu_length_size) {
526 AP4_Size nalu_length = 0;
527 switch (nalu_length_size) {
528 case 1:
529 nalu_length = nalu[0];
530 break;
531
532 case 2:
533 nalu_length = AP4_BytesToUInt16BE(nalu);
534 break;
535
536 case 4:
537 nalu_length = AP4_BytesToUInt32BE(nalu);
538 break;
539
540 default:
541 break;
542 }
543
544 if (bytes_remaining < nalu_length_size+nalu_length) {
545 break;
546 }
547
548 AP4_UI08 nalu_type = nalu[nalu_length_size] & 0x1F;
549 if (nalu_length > 48 && (nalu_type == 1 || nalu_type == 5)) {
550 AP4_Size encrypted_size = 16*((nalu_length-32)/16);
551 if ((nalu_length%16) == 0) {
552 encrypted_size -= 16;
553 }
554 m_StreamCipher->SetIV(m_IV);
555 for (unsigned int i=0; i<encrypted_size; i += 10*16) {
556 AP4_Size one_block_size = 16;
557 m_StreamCipher->ProcessBuffer(nalu+nalu_length_size+32+i, one_block_size,
558 nalu+nalu_length_size+32+i, &one_block_size);
559 }
560
561 // perform startcode emulation prevention
562 AP4_DataBuffer escaped_nalu;
563 PreventStartCodeEmulation(nalu+nalu_length_size, nalu_length, escaped_nalu);
564
565 // the size may have changed
566 // TODO: this could overflow if nalu_length_size is too small
567 switch (nalu_length_size) {
568 case 1:
569 nalu[0] = (AP4_UI08)(escaped_nalu.GetDataSize()&0xFF);
570 break;
571
572 case 2:
573 AP4_BytesFromUInt16BE(nalu, escaped_nalu.GetDataSize());
574 break;
575
576 case 4:
577 AP4_BytesFromUInt32BE(nalu, escaped_nalu.GetDataSize());
578 break;
579
580 default:
581 break;
582 }
583
584 encrypted.AppendData(nalu, nalu_length_size);
585 encrypted.AppendData(escaped_nalu.GetData(), escaped_nalu.GetDataSize());
586 } else {
587 encrypted.AppendData(nalu, nalu_length_size);
588 encrypted.AppendData(nalu+nalu_length_size, nalu_length);
589 }
590
591 nalu += nalu_length_size+nalu_length;
592 bytes_remaining -= nalu_length_size+nalu_length;
593 }
594
595 sample.SetData(encrypted.GetData(), encrypted.GetDataSize());
596
597 return AP4_SUCCESS;
598 }
599
600 /*----------------------------------------------------------------------
601 | GetSamplingFrequencyIndex
602 +---------------------------------------------------------------------*/
603 static unsigned int
GetSamplingFrequencyIndex(unsigned int sampling_frequency)604 GetSamplingFrequencyIndex(unsigned int sampling_frequency)
605 {
606 switch (sampling_frequency) {
607 case 96000: return 0;
608 case 88200: return 1;
609 case 64000: return 2;
610 case 48000: return 3;
611 case 44100: return 4;
612 case 32000: return 5;
613 case 24000: return 6;
614 case 22050: return 7;
615 case 16000: return 8;
616 case 12000: return 9;
617 case 11025: return 10;
618 case 8000: return 11;
619 case 7350: return 12;
620 default: return 0;
621 }
622 }
623
624 /*----------------------------------------------------------------------
625 | WriteAdtsHeader
626 +---------------------------------------------------------------------*/
627 static AP4_Result
WriteAdtsHeader(AP4_ByteStream & output,unsigned int frame_size,unsigned int sampling_frequency_index,unsigned int channel_configuration)628 WriteAdtsHeader(AP4_ByteStream& output,
629 unsigned int frame_size,
630 unsigned int sampling_frequency_index,
631 unsigned int channel_configuration)
632 {
633 unsigned char bits[7];
634
635 bits[0] = 0xFF;
636 bits[1] = 0xF1; // 0xF9 (MPEG2)
637 bits[2] = 0x40 | (sampling_frequency_index << 2) | (channel_configuration >> 2);
638 bits[3] = ((channel_configuration&0x3)<<6) | ((frame_size+7) >> 11);
639 bits[4] = ((frame_size+7) >> 3)&0xFF;
640 bits[5] = (((frame_size+7) << 5)&0xFF) | 0x1F;
641 bits[6] = 0xFC;
642
643 return output.Write(bits, 7);
644
645 /*
646 0: syncword 12 always: '111111111111'
647 12: ID 1 0: MPEG-4, 1: MPEG-2
648 13: layer 2 always: '00'
649 15: protection_absent 1
650 16: profile 2
651 18: sampling_frequency_index 4
652 22: private_bit 1
653 23: channel_configuration 3
654 26: original/copy 1
655 27: home 1
656 28: emphasis 2 only if ID == 0
657
658 ADTS Variable header: these can change from frame to frame
659 28: copyright_identification_bit 1
660 29: copyright_identification_start 1
661 30: aac_frame_length 13 length of the frame including header (in bytes)
662 43: adts_buffer_fullness 11 0x7FF indicates VBR
663 54: no_raw_data_blocks_in_frame 2
664 ADTS Error check
665 crc_check 16 only if protection_absent == 0
666 */
667 }
668
669 /*----------------------------------------------------------------------
670 | WriteAc4Header
671 +---------------------------------------------------------------------*/
672 static AP4_Result
WriteAc4Header(AP4_ByteStream & output,unsigned int frame_size)673 WriteAc4Header(AP4_ByteStream& output,
674 unsigned int frame_size)
675 {
676 unsigned char bits[7];
677
678 bits[0] = 0xac;
679 bits[1] = 0x40;
680 bits[2] = 0xff;
681 bits[3] = 0xff;
682 bits[4] = (frame_size>>16)&0xFF;
683 bits[5] = (frame_size>>8 )&0xFF;
684 bits[6] = (frame_size )&0xFF;
685
686 return output.Write(bits, 7);
687 }
688
689 /*----------------------------------------------------------------------
690 | MakeAudioSetupData
691 +---------------------------------------------------------------------*/
692 static AP4_Result
MakeAudioSetupData(AP4_DataBuffer & audio_setup_data,AP4_SampleDescription * sample_description,AP4_DataBuffer & sample_data)693 MakeAudioSetupData(AP4_DataBuffer& audio_setup_data,
694 AP4_SampleDescription* sample_description,
695 AP4_DataBuffer& sample_data /* needed for encrypted AC3 */)
696 {
697 AP4_UI08 audio_type[4];
698 AP4_DataBuffer setup_data;
699
700 if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
701 AP4_MpegAudioSampleDescription* mpeg_audio_desc = AP4_DYNAMIC_CAST(AP4_MpegAudioSampleDescription, sample_description);
702 if (mpeg_audio_desc == NULL ||
703 !(mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG4_AUDIO ||
704 mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_LC ||
705 mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_MAIN)) {
706 return AP4_ERROR_NOT_SUPPORTED;
707 }
708 const AP4_DataBuffer& dsi = mpeg_audio_desc->GetDecoderInfo();
709 setup_data.SetData(dsi.GetData(), dsi.GetDataSize());
710
711 AP4_Mp4AudioDecoderConfig dec_config;
712 AP4_Result result = dec_config.Parse(dsi.GetData(), dsi.GetDataSize());
713 if (AP4_FAILED(result)) {
714 return AP4_ERROR_INVALID_FORMAT;
715 }
716
717 if (dec_config.m_Extension.m_SbrPresent || dec_config.m_Extension.m_PsPresent) {
718 if (dec_config.m_Extension.m_PsPresent) {
719 audio_type[0] = 'z';
720 audio_type[1] = 'a';
721 audio_type[2] = 'c';
722 audio_type[3] = 'p';
723 } else {
724 audio_type[0] = 'z';
725 audio_type[1] = 'a';
726 audio_type[2] = 'c';
727 audio_type[3] = 'h';
728 }
729 } else {
730 audio_type[0] = 'z';
731 audio_type[1] = 'a';
732 audio_type[2] = 'a';
733 audio_type[3] = 'c';
734 }
735 } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
736 audio_type[0] = 'z';
737 audio_type[1] = 'a';
738 audio_type[2] = 'c';
739 audio_type[3] = '3';
740
741 // the setup data is the first 10 bytes of a syncframe
742 if (sample_data.GetDataSize() >= 10) {
743 setup_data.SetData(sample_data.GetData(), 10);
744 }
745 } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
746 audio_type[0] = 'z';
747 audio_type[1] = 'e';
748 audio_type[2] = 'c';
749 audio_type[3] = '3';
750
751 AP4_Dec3Atom* dec3 = AP4_DYNAMIC_CAST(AP4_Dec3Atom, sample_description->GetDetails().GetChild(AP4_ATOM_TYPE_DEC3));
752 if (dec3 == NULL) {
753 fprintf(stderr, "ERROR: failed to find 'dec3' atom in sample description\n");
754 return 1;
755 }
756 setup_data.SetData(dec3->GetRawBytes().GetData(), dec3->GetRawBytes().GetDataSize());
757 } else {
758 return AP4_SUCCESS;
759 }
760
761 audio_setup_data.SetDataSize(8+setup_data.GetDataSize());
762 AP4_UI08* payload = audio_setup_data.UseData();
763 payload[0] = audio_type[0];
764 payload[1] = audio_type[1];
765 payload[2] = audio_type[2];
766 payload[3] = audio_type[3];
767 payload[4] = 0; // priming
768 payload[5] = 0; // priming
769 payload[6] = 1; // version
770 payload[7] = setup_data.GetDataSize(); // setup_data_length
771 if (setup_data.GetDataSize()) {
772 AP4_CopyMemory(&payload[8], setup_data.GetData(), setup_data.GetDataSize());
773 }
774
775 return AP4_SUCCESS;
776 }
777
778 /*----------------------------------------------------------------------
779 | MakeSampleAesAudioDescriptor
780 +---------------------------------------------------------------------*/
781 static AP4_Result
MakeSampleAesAudioDescriptor(AP4_DataBuffer & descriptor,AP4_SampleDescription * sample_description,AP4_DataBuffer & sample_data)782 MakeSampleAesAudioDescriptor(AP4_DataBuffer& descriptor,
783 AP4_SampleDescription* sample_description,
784 AP4_DataBuffer& sample_data /* needed for encrypted AC3 */)
785 {
786 // descriptor
787 descriptor.SetDataSize(6);
788 AP4_UI08* payload = descriptor.UseData();
789 payload[0] = AP4_MPEG2_PRIVATE_DATA_INDICATOR_DESCRIPTOR_TAG;
790 payload[1] = 4;
791 if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
792 payload[2] = 'a';
793 payload[3] = 'a';
794 payload[4] = 'c';
795 payload[5] = 'd';
796 } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
797 payload[2] = 'a';
798 payload[3] = 'c';
799 payload[4] = '3';
800 payload[5] = 'd';
801 } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
802 payload[2] = 'e';
803 payload[3] = 'c';
804 payload[4] = '3';
805 payload[5] = 'd';
806 }
807
808 // compute the audio setup data
809 AP4_DataBuffer audio_setup_data;
810 AP4_Result result = MakeAudioSetupData(audio_setup_data, sample_description, sample_data);
811 if (AP4_FAILED(result)) {
812 fprintf(stderr, "ERROR: failed to make audio setup data (%d)\n", result);
813 return result;
814 }
815
816 descriptor.SetDataSize(descriptor.GetDataSize()+6+audio_setup_data.GetDataSize());
817 payload = descriptor.UseData()+6;
818 payload[0] = AP4_MPEG2_REGISTRATION_DESCRIPTOR_TAG;
819 payload[1] = 4+audio_setup_data.GetDataSize();
820 payload[2] = 'a';
821 payload[3] = 'p';
822 payload[4] = 'a';
823 payload[5] = 'd';
824 AP4_CopyMemory(&payload[6], audio_setup_data.GetData(), audio_setup_data.GetDataSize());
825
826 return AP4_SUCCESS;
827 }
828
829 /*----------------------------------------------------------------------
830 | MakeSampleAesVideoDescriptor
831 +---------------------------------------------------------------------*/
832 static AP4_Result
MakeSampleAesVideoDescriptor(AP4_DataBuffer & descriptor)833 MakeSampleAesVideoDescriptor(AP4_DataBuffer& descriptor)
834 {
835 descriptor.SetDataSize(6);
836 AP4_UI08* payload = descriptor.UseData();
837 payload[0] = AP4_MPEG2_PRIVATE_DATA_INDICATOR_DESCRIPTOR_TAG;
838 payload[1] = 4;
839 payload[2] = 'z';
840 payload[3] = 'a';
841 payload[4] = 'v';
842 payload[5] = 'c';
843
844 return AP4_SUCCESS;
845 }
846
847 /*----------------------------------------------------------------------
848 | PackedAudioWriter
849 +---------------------------------------------------------------------*/
850 class PackedAudioWriter {
851 public:
852 AP4_Result WriteHeader(double timestamp,
853 const char* private_extension_name,
854 const AP4_UI08* private_extension_data,
855 unsigned int private_extension_data_size,
856 AP4_ByteStream& output);
857 AP4_Result WriteSample(AP4_Sample& sample,
858 AP4_DataBuffer& sample_data,
859 AP4_SampleDescription* sample_description,
860 AP4_ByteStream& output);
861
862 };
863
864 /*----------------------------------------------------------------------
865 | PackedAudioWriter::WriteHeader
866 +---------------------------------------------------------------------*/
867 AP4_Result
WriteHeader(double timestamp,const char * private_extension_name,const AP4_UI08 * private_extension_data,unsigned int private_extension_data_size,AP4_ByteStream & output)868 PackedAudioWriter::WriteHeader(double timestamp,
869 const char* private_extension_name,
870 const AP4_UI08* private_extension_data,
871 unsigned int private_extension_data_size,
872 AP4_ByteStream& output)
873 {
874 unsigned int header_size = 10+10+45+8;
875 unsigned int private_extension_name_size = private_extension_name?(unsigned int)AP4_StringLength(private_extension_name)+1:0;
876 if (private_extension_name) {
877 header_size += 10+private_extension_name_size+private_extension_data_size;;
878 }
879 AP4_UI08* header = new AP4_UI08[header_size];
880
881 /* ID3 Tag Header
882 ID3v2/file identifier "ID3"
883 ID3v2 version $04 00
884 ID3v2 flags %abcd0000
885 ID3v2 size 4 * %0xxxxxxx
886 */
887 header[0] = 'I';
888 header[1] = 'D';
889 header[2] = '3';
890 header[3] = 4;
891 header[4] = 0;
892 header[5] = 0;
893 header[6] = 0;
894 header[7] = 0;
895 header[8] = ((header_size-10)>>7) & 0x7F;
896 header[9] = (header_size-10) & 0x7F;
897
898 /* PRIV frame
899 Frame ID $xx xx xx xx (four characters)
900 Size $xx xx xx xx
901 Flags $xx xx
902
903 Owner identifier <text string> $00
904 The private data <binary data>
905 */
906 header[10] = 'P';
907 header[11] = 'R';
908 header[12] = 'I';
909 header[13] = 'V';
910 header[14] = 0;
911 header[15] = 0;
912 header[16] = 0;
913 header[17] = 45+8;
914 header[18] = 0;
915 header[19] = 0;
916 AP4_CopyMemory(&header[20], "com.apple.streaming.transportStreamTimestamp", 45);
917 AP4_UI64 mpeg_ts = (AP4_UI64)(timestamp*90000.0);
918 AP4_BytesFromUInt64BE(&header[10+10+45], mpeg_ts);
919
920 // add the extra private extension if needed
921 if (private_extension_name) {
922 AP4_UI08* ext = &header[10+10+45+8];
923 ext[0] = 'P';
924 ext[1] = 'R';
925 ext[2] = 'I';
926 ext[3] = 'V';
927 ext[4] = 0;
928 ext[5] = 0;
929 ext[6] = ((private_extension_name_size+private_extension_data_size)>>7) & 0x7F;
930 ext[7] = (private_extension_name_size+private_extension_data_size) & 0x7F;
931 ext[8] = 0;
932 ext[9] = 0;
933 AP4_CopyMemory(&ext[10], private_extension_name, private_extension_name_size);
934 AP4_CopyMemory(&ext[10+private_extension_name_size], private_extension_data, private_extension_data_size);
935 }
936
937 // write the header out
938 AP4_Result result = output.Write(header, header_size);
939 delete[] header;
940
941 return result;
942 }
943
944 /*----------------------------------------------------------------------
945 | PackedAudioWriter::WriteSample
946 +---------------------------------------------------------------------*/
947 AP4_Result
WriteSample(AP4_Sample & sample,AP4_DataBuffer & sample_data,AP4_SampleDescription * sample_description,AP4_ByteStream & output)948 PackedAudioWriter::WriteSample(AP4_Sample& sample,
949 AP4_DataBuffer& sample_data,
950 AP4_SampleDescription* sample_description,
951 AP4_ByteStream& output)
952 {
953 AP4_AudioSampleDescription* audio_desc = AP4_DYNAMIC_CAST(AP4_AudioSampleDescription, sample_description);
954 if (audio_desc == NULL) {
955 return AP4_ERROR_INVALID_FORMAT;
956 }
957 if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
958 AP4_MpegAudioSampleDescription* mpeg_audio_desc = AP4_DYNAMIC_CAST(AP4_MpegAudioSampleDescription, sample_description);
959
960 if (mpeg_audio_desc == NULL) return AP4_ERROR_NOT_SUPPORTED;
961 if (mpeg_audio_desc->GetMpeg4AudioObjectType() != AP4_MPEG4_AUDIO_OBJECT_TYPE_AAC_LC &&
962 mpeg_audio_desc->GetMpeg4AudioObjectType() != AP4_MPEG4_AUDIO_OBJECT_TYPE_AAC_MAIN &&
963 mpeg_audio_desc->GetMpeg4AudioObjectType() != AP4_MPEG4_AUDIO_OBJECT_TYPE_SBR &&
964 mpeg_audio_desc->GetMpeg4AudioObjectType() != AP4_MPEG4_AUDIO_OBJECT_TYPE_PS) {
965 return AP4_ERROR_NOT_SUPPORTED;
966 }
967
968 unsigned int sample_rate = mpeg_audio_desc->GetSampleRate();
969 unsigned int channel_count = mpeg_audio_desc->GetChannelCount();
970 const AP4_DataBuffer& dsi = mpeg_audio_desc->GetDecoderInfo();
971 if (dsi.GetDataSize()) {
972 AP4_Mp4AudioDecoderConfig dec_config;
973 AP4_Result result = dec_config.Parse(dsi.GetData(), dsi.GetDataSize());
974 if (AP4_SUCCEEDED(result)) {
975 sample_rate = dec_config.m_SamplingFrequency;
976 channel_count = dec_config.m_ChannelCount;
977 }
978 }
979 unsigned int sampling_frequency_index = GetSamplingFrequencyIndex(sample_rate);
980 unsigned int channel_configuration = channel_count;
981
982 WriteAdtsHeader(output, sample.GetSize(), sampling_frequency_index, channel_configuration);
983 } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_4) {
984 WriteAc4Header(output, sample.GetSize());
985 }
986 return output.Write(sample_data.GetData(), sample_data.GetDataSize());
987 }
988
989 /*----------------------------------------------------------------------
990 | ReadSample
991 +---------------------------------------------------------------------*/
992 static AP4_Result
ReadSample(SampleReader & reader,AP4_Track & track,AP4_Sample & sample,AP4_DataBuffer & sample_data,double & ts,double & duration,bool & eos)993 ReadSample(SampleReader& reader,
994 AP4_Track& track,
995 AP4_Sample& sample,
996 AP4_DataBuffer& sample_data,
997 double& ts,
998 double& duration,
999 bool& eos)
1000 {
1001 AP4_Result result = reader.ReadSample(sample, sample_data);
1002 if (AP4_FAILED(result)) {
1003 if (result == AP4_ERROR_EOS) {
1004 // advance the timestamp by the last sample's duration
1005 ts += duration;
1006 eos = true;
1007 return AP4_SUCCESS;
1008 } else {
1009 return result;
1010 }
1011 }
1012 ts = (double)sample.GetDts()/(double)track.GetMediaTimeScale();
1013 duration = sample.GetDuration()/(double)track.GetMediaTimeScale();
1014
1015 return AP4_SUCCESS;
1016 }
1017
1018 /*----------------------------------------------------------------------
1019 | WriteSamples
1020 +---------------------------------------------------------------------*/
1021 static AP4_Result
WriteSamples(AP4_Mpeg2TsWriter * ts_writer,PackedAudioWriter * packed_writer,AP4_Track * audio_track,SampleReader * audio_reader,AP4_Mpeg2TsWriter::SampleStream * audio_stream,AP4_Track * video_track,SampleReader * video_reader,AP4_Mpeg2TsWriter::SampleStream * video_stream,unsigned int segment_duration_threshold,AP4_UI08 nalu_length_size)1022 WriteSamples(AP4_Mpeg2TsWriter* ts_writer,
1023 PackedAudioWriter* packed_writer,
1024 AP4_Track* audio_track,
1025 SampleReader* audio_reader,
1026 AP4_Mpeg2TsWriter::SampleStream* audio_stream,
1027 AP4_Track* video_track,
1028 SampleReader* video_reader,
1029 AP4_Mpeg2TsWriter::SampleStream* video_stream,
1030 unsigned int segment_duration_threshold,
1031 AP4_UI08 nalu_length_size)
1032 {
1033 AP4_Sample audio_sample;
1034 AP4_DataBuffer audio_sample_data;
1035 unsigned int audio_sample_count = 0;
1036 double audio_ts = 0.0;
1037 double audio_frame_duration = 0.0;
1038 bool audio_eos = false;
1039 AP4_Sample video_sample;
1040 AP4_DataBuffer video_sample_data;
1041 unsigned int video_sample_count = 0;
1042 double video_ts = 0.0;
1043 double video_frame_duration = 0.0;
1044 bool video_eos = false;
1045 double last_ts = 0.0;
1046 unsigned int segment_number = 0;
1047 AP4_ByteStream* segment_output = NULL;
1048 double segment_duration = 0.0;
1049 AP4_Array<double> segment_durations;
1050 AP4_Array<AP4_UI32> segment_sizes;
1051 AP4_Position segment_position = 0;
1052 AP4_Array<AP4_Position> segment_positions;
1053 AP4_Array<AP4_Position> iframe_positions;
1054 AP4_Array<AP4_UI32> iframe_sizes;
1055 AP4_Array<double> iframe_times;
1056 AP4_Array<double> iframe_durations;
1057 AP4_Array<AP4_UI32> iframe_segment_indexes;
1058 bool new_segment = true;
1059 AP4_ByteStream* raw_output = NULL;
1060 AP4_ByteStream* playlist = NULL;
1061 char string_buffer[4096];
1062 SampleEncrypter* sample_encrypter = NULL;
1063 AP4_Result result = AP4_SUCCESS;
1064
1065 // prime the samples
1066 if (audio_reader) {
1067 result = ReadSample(*audio_reader, *audio_track, audio_sample, audio_sample_data, audio_ts, audio_frame_duration, audio_eos);
1068 if (AP4_FAILED(result)) return result;
1069 }
1070 if (video_reader) {
1071 result = ReadSample(*video_reader, *video_track, video_sample, video_sample_data, video_ts, video_frame_duration, video_eos);
1072 if (AP4_FAILED(result)) return result;
1073 }
1074
1075 for (;;) {
1076 bool sync_sample = false;
1077 AP4_Track* chosen_track= NULL;
1078 if (audio_track && !audio_eos) {
1079 chosen_track = audio_track;
1080 if (video_track == NULL) {
1081 if (audio_track->GetSampleDescription(0)->GetFormat() == AP4_SAMPLE_FORMAT_AC_4) {
1082 if (audio_sample.IsSync()) {
1083 sync_sample = true;
1084 }
1085 } else {
1086 sync_sample = true;
1087 }
1088 }
1089 }
1090 if (video_track && !video_eos) {
1091 if (audio_track) {
1092 if (video_ts <= audio_ts) {
1093 chosen_track = video_track;
1094 }
1095 } else {
1096 chosen_track = video_track;
1097 }
1098 if (chosen_track == video_track && video_sample.IsSync()) {
1099 sync_sample = true;
1100 }
1101 }
1102
1103 // check if we need to start a new segment
1104 if (Options.segment_duration && (sync_sample || chosen_track == NULL)) {
1105 if (video_track) {
1106 segment_duration = video_ts - last_ts;
1107 } else {
1108 segment_duration = audio_ts - last_ts;
1109 }
1110 if ((segment_duration >= (double)Options.segment_duration - (double)segment_duration_threshold/1000.0) ||
1111 chosen_track == NULL) {
1112 if (video_track) {
1113 last_ts = video_ts;
1114 } else {
1115 last_ts = audio_ts;
1116 }
1117 if (segment_output) {
1118 // flush the output stream
1119 segment_output->Flush();
1120
1121 // compute the segment size (including padding)
1122 AP4_Position segment_end = 0;
1123 segment_output->Tell(segment_end);
1124 AP4_UI32 segment_size = 0;
1125 if (Options.encryption_mode == ENCRYPTION_MODE_AES_128) {
1126 segment_size = (AP4_UI32)segment_end;
1127 } else if (segment_end > segment_position) {
1128 segment_size = (AP4_UI32)(segment_end-segment_position);
1129 }
1130
1131 // update counters
1132 segment_sizes.Append(segment_size);
1133 segment_positions.Append(segment_position);
1134 segment_durations.Append(segment_duration);
1135
1136 if (segment_duration != 0.0) {
1137 double segment_bitrate = 8.0*(double)segment_size/segment_duration;
1138 if (segment_bitrate > Stats.max_segment_bitrate) {
1139 Stats.max_segment_bitrate = segment_bitrate;
1140 }
1141 }
1142 if (Options.verbose) {
1143 printf("Segment %d, duration=%.2f, %d audio samples, %d video samples, %d bytes @%lld\n",
1144 segment_number,
1145 segment_duration,
1146 audio_sample_count,
1147 video_sample_count,
1148 segment_size,
1149 segment_position);
1150 }
1151 if (!Options.output_single_file) {
1152 segment_output->Release();
1153 segment_output = NULL;
1154 }
1155 ++segment_number;
1156 audio_sample_count = 0;
1157 video_sample_count = 0;
1158 }
1159 new_segment = true;
1160 }
1161 }
1162
1163 // check if we're done
1164 if (chosen_track == NULL) break;
1165
1166 if (new_segment) {
1167 new_segment = false;
1168
1169 // compute the new segment position
1170 segment_position = 0;
1171 if (Options.output_single_file) {
1172 if (raw_output) {
1173 raw_output->Tell(segment_position);
1174 }
1175 }
1176
1177 // manage the new segment stream
1178 if (segment_output == NULL) {
1179 segment_output = OpenOutput(Options.segment_filename_template, segment_number);
1180 raw_output = segment_output;
1181 if (segment_output == NULL) return AP4_ERROR_CANNOT_OPEN_FILE;
1182 }
1183 if (Options.encryption_mode != ENCRYPTION_MODE_NONE) {
1184 if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_SEQUENCE) {
1185 AP4_SetMemory(Options.encryption_iv, 0, sizeof(Options.encryption_iv));
1186 AP4_BytesFromUInt32BE(&Options.encryption_iv[12], segment_number);
1187 }
1188 }
1189 if (Options.encryption_mode == ENCRYPTION_MODE_AES_128) {
1190 EncryptingStream* encrypting_stream = NULL;
1191 result = EncryptingStream::Create(Options.encryption_key, Options.encryption_iv, raw_output, encrypting_stream);
1192 if (AP4_FAILED(result)) {
1193 fprintf(stderr, "ERROR: failed to create encrypting stream (%d)\n", result);
1194 return result;
1195 }
1196 segment_output->Release();
1197 segment_output = encrypting_stream;
1198 } else if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
1199 delete sample_encrypter;
1200 sample_encrypter = NULL;
1201 result = SampleEncrypter::Create(Options.encryption_key, Options.encryption_iv, sample_encrypter);
1202 if (AP4_FAILED(result)) {
1203 fprintf(stderr, "ERROR: failed to create sample encrypter (%d)\n", result);
1204 return result;
1205 }
1206 }
1207
1208 // write the PAT and PMT
1209 if (ts_writer) {
1210 // update the descriptors if needed
1211 if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
1212 AP4_DataBuffer descriptor;
1213 if (audio_track) {
1214 result = MakeSampleAesAudioDescriptor(descriptor, audio_track->GetSampleDescription(0), audio_sample_data);
1215 if (AP4_SUCCEEDED(result) && descriptor.GetDataSize()) {
1216 audio_stream->SetDescriptor(descriptor.GetData(), descriptor.GetDataSize());
1217 } else {
1218 fprintf(stderr, "ERROR: failed to create sample-aes descriptor (%d)\n", result);
1219 return result;
1220 }
1221 }
1222 if (video_track) {
1223 result = MakeSampleAesVideoDescriptor(descriptor);
1224 if (AP4_SUCCEEDED(result) && descriptor.GetDataSize()) {
1225 video_stream->SetDescriptor(descriptor.GetData(), descriptor.GetDataSize());
1226 } else {
1227 fprintf(stderr, "ERROR: failed to create sample-aes descriptor (%d)\n", result);
1228 return result;
1229 }
1230 }
1231 }
1232
1233 ts_writer->WritePAT(*segment_output);
1234 ts_writer->WritePMT(*segment_output);
1235 } else if (packed_writer && chosen_track == audio_track) {
1236 AP4_DataBuffer private_extension_buffer;
1237 const char* private_extension_name = NULL;
1238 const unsigned char* private_extension_data = NULL;
1239 unsigned int private_extension_data_size = 0;
1240 if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
1241 private_extension_name = "com.apple.streaming.audioDescription";
1242 result = MakeAudioSetupData(private_extension_buffer, audio_track->GetSampleDescription(0), audio_sample_data);
1243 if (AP4_FAILED(result)) {
1244 fprintf(stderr, "ERROR: failed to make audio setup data (%d)\n", result);
1245 return 1;
1246 }
1247 private_extension_data = private_extension_buffer.GetData();
1248 private_extension_data_size = private_extension_buffer.GetDataSize();
1249 }
1250 packed_writer->WriteHeader(audio_ts,
1251 private_extension_name,
1252 private_extension_data,
1253 private_extension_data_size,
1254 *segment_output);
1255 }
1256 }
1257
1258 // write the samples out and advance to the next sample
1259 if (chosen_track == audio_track) {
1260 // perform sample-level encryption if needed
1261 if (sample_encrypter) {
1262 result = sample_encrypter->EncryptAudioSample(audio_sample_data, audio_track->GetSampleDescription(audio_sample.GetDescriptionIndex()));
1263 if (AP4_FAILED(result)) {
1264 fprintf(stderr, "ERROR: failed to encrypt audio sample (%d)\n", result);
1265 return result;
1266 }
1267 }
1268
1269 // write the sample data
1270 if (audio_stream) {
1271 result = audio_stream->WriteSample(audio_sample,
1272 audio_sample_data,
1273 audio_track->GetSampleDescription(audio_sample.GetDescriptionIndex()),
1274 video_track==NULL,
1275 *segment_output);
1276 } else if (packed_writer) {
1277 result = packed_writer->WriteSample(audio_sample,
1278 audio_sample_data,
1279 audio_track->GetSampleDescription(audio_sample.GetDescriptionIndex()),
1280 *segment_output);
1281 } else {
1282 return AP4_ERROR_INTERNAL;
1283 }
1284 if (AP4_FAILED(result)) return result;
1285
1286 result = ReadSample(*audio_reader, *audio_track, audio_sample, audio_sample_data, audio_ts, audio_frame_duration, audio_eos);
1287 if (AP4_FAILED(result)) return result;
1288 ++audio_sample_count;
1289 } else if (chosen_track == video_track) {
1290 // perform sample-level encryption if needed
1291 if (sample_encrypter) {
1292 result = sample_encrypter->EncryptVideoSample(video_sample_data, nalu_length_size);
1293 if (AP4_FAILED(result)) {
1294 fprintf(stderr, "ERROR: failed to encrypt video sample (%d)\n", result);
1295 return result;
1296 }
1297 }
1298
1299 // write the sample data
1300 AP4_Position frame_start = 0;
1301 segment_output->Tell(frame_start);
1302 result = video_stream->WriteSample(video_sample,
1303 video_sample_data,
1304 video_track->GetSampleDescription(video_sample.GetDescriptionIndex()),
1305 true,
1306 *segment_output);
1307 if (AP4_FAILED(result)) return result;
1308 AP4_Position frame_end = 0;
1309 segment_output->Tell(frame_end);
1310
1311 // measure I frames
1312 if (video_sample.IsSync()) {
1313 AP4_UI64 frame_size = 0;
1314 if (frame_end > frame_start) {
1315 frame_size = frame_end-frame_start;
1316 }
1317 if (Options.encryption_mode == ENCRYPTION_MODE_AES_128) {
1318 frame_start += segment_position;
1319 }
1320 iframe_positions.Append(frame_start);
1321 iframe_sizes.Append((AP4_UI32)frame_size);
1322 iframe_times.Append(video_ts);
1323 iframe_segment_indexes.Append(segment_number);
1324 iframe_durations.Append(0.0); // will be computed later
1325 if (Options.verbose) {
1326 printf("I-Frame: %d@%lld, t=%f\n", (AP4_UI32)frame_size, frame_start, video_ts);
1327 }
1328 }
1329
1330 // read the next sample
1331 result = ReadSample(*video_reader, *video_track, video_sample, video_sample_data, video_ts, video_frame_duration, video_eos);
1332 if (AP4_FAILED(result)) return result;
1333 ++video_sample_count;
1334 } else {
1335 break;
1336 }
1337 }
1338
1339 // create the media playlist/index file
1340 playlist = OpenOutput(Options.index_filename, 0);
1341 if (playlist == NULL) return AP4_ERROR_CANNOT_OPEN_FILE;
1342
1343 unsigned int target_duration = 0;
1344 double total_duration = 0.0;
1345 for (unsigned int i=0; i<segment_durations.ItemCount(); i++) {
1346 if ((unsigned int)(segment_durations[i]+0.5) > target_duration) {
1347 target_duration = (unsigned int)segment_durations[i];
1348 }
1349 total_duration += segment_durations[i];
1350 }
1351
1352 playlist->WriteString("#EXTM3U\r\n");
1353 if (Options.hls_version > 1) {
1354 sprintf(string_buffer, "#EXT-X-VERSION:%d\r\n", Options.hls_version);
1355 playlist->WriteString(string_buffer);
1356 }
1357 playlist->WriteString("#EXT-X-PLAYLIST-TYPE:VOD\r\n");
1358 if (video_track) {
1359 playlist->WriteString("#EXT-X-INDEPENDENT-SEGMENTS\r\n");
1360 }
1361 playlist->WriteString("#EXT-X-TARGETDURATION:");
1362 sprintf(string_buffer, "%d\r\n", target_duration);
1363 playlist->WriteString(string_buffer);
1364 playlist->WriteString("#EXT-X-MEDIA-SEQUENCE:0\r\n");
1365
1366 if (Options.encryption_mode != ENCRYPTION_MODE_NONE) {
1367 if (Options.encryption_key_lines.ItemCount()) {
1368 for (unsigned int i=0; i<Options.encryption_key_lines.ItemCount(); i++) {
1369 AP4_String& key_line = Options.encryption_key_lines[i];
1370 const char* key_line_cstr = key_line.GetChars();
1371 bool omit_iv = false;
1372
1373 // omit the IV if the key line starts with a "!" (and skip the "!")
1374 if (key_line[0] == '!') {
1375 ++key_line_cstr;
1376 omit_iv = true;
1377 }
1378
1379 playlist->WriteString("#EXT-X-KEY:METHOD=");
1380 if (Options.encryption_mode == ENCRYPTION_MODE_AES_128) {
1381 playlist->WriteString("AES-128");
1382 } else if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
1383 playlist->WriteString("SAMPLE-AES");
1384 }
1385 playlist->WriteString(",");
1386 playlist->WriteString(key_line_cstr);
1387 if ((Options.encryption_iv_mode == ENCRYPTION_IV_MODE_RANDOM ||
1388 Options.encryption_iv_mode == ENCRYPTION_IV_MODE_FPS) && !omit_iv) {
1389 playlist->WriteString(",IV=0x");
1390 char iv_hex[33];
1391 iv_hex[32] = 0;
1392 AP4_FormatHex(Options.encryption_iv, 16, iv_hex);
1393 playlist->WriteString(iv_hex);
1394 }
1395 playlist->WriteString("\r\n");
1396 }
1397 } else {
1398 playlist->WriteString("#EXT-X-KEY:METHOD=");
1399 if (Options.encryption_mode == ENCRYPTION_MODE_AES_128) {
1400 playlist->WriteString("AES-128");
1401 } else if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
1402 playlist->WriteString("SAMPLE-AES");
1403 }
1404 playlist->WriteString(",URI=\"");
1405 playlist->WriteString(Options.encryption_key_uri);
1406 playlist->WriteString("\"");
1407 if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_RANDOM) {
1408 playlist->WriteString(",IV=0x");
1409 char iv_hex[33];
1410 iv_hex[32] = 0;
1411 AP4_FormatHex(Options.encryption_iv, 16, iv_hex);
1412 playlist->WriteString(iv_hex);
1413 }
1414 if (Options.encryption_key_format) {
1415 playlist->WriteString(",KEYFORMAT=\"");
1416 playlist->WriteString(Options.encryption_key_format);
1417 playlist->WriteString("\"");
1418 }
1419 if (Options.encryption_key_format_versions) {
1420 playlist->WriteString(",KEYFORMATVERSIONS=\"");
1421 playlist->WriteString(Options.encryption_key_format_versions);
1422 playlist->WriteString("\"");
1423 }
1424 playlist->WriteString("\r\n");
1425 }
1426 }
1427
1428 for (unsigned int i=0; i<segment_durations.ItemCount(); i++) {
1429 if (Options.hls_version >= 3) {
1430 sprintf(string_buffer, "#EXTINF:%f,\r\n", segment_durations[i]);
1431 } else {
1432 sprintf(string_buffer, "#EXTINF:%u,\r\n", (unsigned int)(segment_durations[i]+0.5));
1433 }
1434 playlist->WriteString(string_buffer);
1435 if (Options.output_single_file) {
1436 sprintf(string_buffer, "#EXT-X-BYTERANGE:%d@%lld\r\n", segment_sizes[i], segment_positions[i]);
1437 playlist->WriteString(string_buffer);
1438 }
1439 sprintf(string_buffer, Options.segment_url_template, i);
1440 playlist->WriteString(string_buffer);
1441 playlist->WriteString("\r\n");
1442 }
1443
1444 playlist->WriteString("#EXT-X-ENDLIST\r\n");
1445 playlist->Release();
1446
1447 // create the iframe playlist/index file
1448 if (video_track && Options.hls_version >= 4) {
1449 // compute the iframe durations and target duration
1450 for (unsigned int i=0; i<iframe_positions.ItemCount(); i++) {
1451 double iframe_duration = 0.0;
1452 if (i+1 < iframe_positions.ItemCount()) {
1453 iframe_duration = iframe_times[i+1]-iframe_times[i];
1454 } else if (total_duration > iframe_times[i]) {
1455 iframe_duration = total_duration-iframe_times[i];
1456 }
1457 iframe_durations[i] = iframe_duration;
1458 }
1459 unsigned int iframes_target_duration = 0;
1460 for (unsigned int i=0; i<iframe_durations.ItemCount(); i++) {
1461 if ((unsigned int)(iframe_durations[i]+0.5) > iframes_target_duration) {
1462 iframes_target_duration = (unsigned int)iframe_durations[i];
1463 }
1464 }
1465
1466 playlist = OpenOutput(Options.iframe_index_filename, 0);
1467 if (playlist == NULL) return AP4_ERROR_CANNOT_OPEN_FILE;
1468
1469 playlist->WriteString("#EXTM3U\r\n");
1470 if (Options.hls_version > 1) {
1471 sprintf(string_buffer, "#EXT-X-VERSION:%d\r\n", Options.hls_version);
1472 playlist->WriteString(string_buffer);
1473 }
1474 playlist->WriteString("#EXT-X-PLAYLIST-TYPE:VOD\r\n");
1475 playlist->WriteString("#EXT-X-I-FRAMES-ONLY\r\n");
1476 playlist->WriteString("#EXT-X-INDEPENDENT-SEGMENTS\r\n");
1477 playlist->WriteString("#EXT-X-TARGETDURATION:");
1478 sprintf(string_buffer, "%d\r\n", iframes_target_duration);
1479 playlist->WriteString(string_buffer);
1480 playlist->WriteString("#EXT-X-MEDIA-SEQUENCE:0\r\n");
1481
1482 if (Options.encryption_mode != ENCRYPTION_MODE_NONE) {
1483 playlist->WriteString("#EXT-X-KEY:METHOD=");
1484 if (Options.encryption_mode == ENCRYPTION_MODE_AES_128) {
1485 playlist->WriteString("AES-128");
1486 } else if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
1487 playlist->WriteString("SAMPLE-AES");
1488 }
1489 playlist->WriteString(",URI=\"");
1490 playlist->WriteString(Options.encryption_key_uri);
1491 playlist->WriteString("\"");
1492 if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_RANDOM) {
1493 playlist->WriteString(",IV=0x");
1494 char iv_hex[33];
1495 iv_hex[32] = 0;
1496 AP4_FormatHex(Options.encryption_iv, 16, iv_hex);
1497 playlist->WriteString(iv_hex);
1498 }
1499 if (Options.encryption_key_format) {
1500 playlist->WriteString(",KEYFORMAT=\"");
1501 playlist->WriteString(Options.encryption_key_format);
1502 playlist->WriteString("\"");
1503 }
1504 if (Options.encryption_key_format_versions) {
1505 playlist->WriteString(",KEYFORMATVERSIONS=\"");
1506 playlist->WriteString(Options.encryption_key_format_versions);
1507 playlist->WriteString("\"");
1508 }
1509 playlist->WriteString("\r\n");
1510 }
1511
1512 for (unsigned int i=0; i<iframe_positions.ItemCount(); i++) {
1513 sprintf(string_buffer, "#EXTINF:%f,\r\n", iframe_durations[i]);
1514 playlist->WriteString(string_buffer);
1515 sprintf(string_buffer, "#EXT-X-BYTERANGE:%d@%lld\r\n", iframe_sizes[i], iframe_positions[i]);
1516 playlist->WriteString(string_buffer);
1517 sprintf(string_buffer, Options.segment_url_template, iframe_segment_indexes[i]);
1518 playlist->WriteString(string_buffer);
1519 playlist->WriteString("\r\n");
1520 }
1521
1522 playlist->WriteString("#EXT-X-ENDLIST\r\n");
1523 playlist->Release();
1524 }
1525
1526 // update stats
1527 Stats.segment_count = segment_sizes.ItemCount();
1528 for (unsigned int i=0; i<segment_sizes.ItemCount(); i++) {
1529 Stats.segments_total_size += segment_sizes[i];
1530 Stats.segments_total_duration += segment_durations[i];
1531 }
1532 Stats.iframe_count = iframe_sizes.ItemCount();
1533 for (unsigned int i=0; i<iframe_sizes.ItemCount(); i++) {
1534 Stats.iframes_total_size += iframe_sizes[i];
1535 }
1536 for (unsigned int i=0; i<iframe_positions.ItemCount(); i++) {
1537 if (iframe_durations[i] != 0.0) {
1538 double iframe_bitrate = 8.0*(double)iframe_sizes[i]/iframe_durations[i];
1539 if (iframe_bitrate > Stats.max_iframe_bitrate) {
1540 Stats.max_iframe_bitrate = iframe_bitrate;
1541 }
1542 }
1543 }
1544
1545 if (Options.verbose) {
1546 printf("Conversion complete, total duration=%.2f secs\n", total_duration);
1547 }
1548
1549 if (segment_output) segment_output->Release();
1550 delete sample_encrypter;
1551
1552 return result;
1553 }
1554
1555 /*----------------------------------------------------------------------
1556 | main
1557 +---------------------------------------------------------------------*/
1558 int
main(int argc,char ** argv)1559 main(int argc, char** argv)
1560 {
1561 if (argc < 2) {
1562 PrintUsageAndExit();
1563 }
1564
1565 // default options
1566 Options.input = NULL;
1567 Options.verbose = false;
1568 Options.hls_version = 0;
1569 Options.pmt_pid = 0x100;
1570 Options.audio_pid = 0x101;
1571 Options.video_pid = 0x102;
1572 Options.audio_track_id = -1;
1573 Options.video_track_id = -1;
1574 Options.audio_format = AUDIO_FORMAT_TS;
1575 Options.output_single_file = false;
1576 Options.show_info = false;
1577 Options.index_filename = "stream.m3u8";
1578 Options.iframe_index_filename = NULL;
1579 Options.segment_filename_template = NULL;
1580 Options.segment_url_template = NULL;
1581 Options.segment_duration = 6;
1582 Options.segment_duration_threshold = DefaultSegmentDurationThreshold;
1583 Options.encryption_key_hex = NULL;
1584 Options.encryption_mode = ENCRYPTION_MODE_NONE;
1585 Options.encryption_iv_mode = ENCRYPTION_IV_MODE_NONE;
1586 Options.encryption_key_uri = "key.bin";
1587 Options.encryption_key_format = NULL;
1588 Options.encryption_key_format_versions = NULL;
1589 Options.pcr_offset = AP4_MPEG2_TS_DEFAULT_PCR_OFFSET;
1590 AP4_SetMemory(Options.encryption_key, 0, sizeof(Options.encryption_key));
1591 AP4_SetMemory(Options.encryption_iv, 0, sizeof(Options.encryption_iv));
1592 AP4_SetMemory(&Stats, 0, sizeof(Stats));
1593
1594 // parse command line
1595 AP4_Result result;
1596 char** args = argv+1;
1597 while (const char* arg = *args++) {
1598 if (!strcmp(arg, "--verbose")) {
1599 Options.verbose = true;
1600 } else if (!strcmp(arg, "--hls-version")) {
1601 if (*args == NULL) {
1602 fprintf(stderr, "ERROR: --hls-version requires a number\n");
1603 return 1;
1604 }
1605 Options.hls_version = (unsigned int)strtoul(*args++, NULL, 10);
1606 if (Options.hls_version ==0) {
1607 fprintf(stderr, "ERROR: --hls-version requires number > 0\n");
1608 return 1;
1609 }
1610 } else if (!strcmp(arg, "--segment-duration")) {
1611 if (*args == NULL) {
1612 fprintf(stderr, "ERROR: --segment-duration requires a number\n");
1613 return 1;
1614 }
1615 Options.segment_duration = (unsigned int)strtoul(*args++, NULL, 10);
1616 } else if (!strcmp(arg, "--segment-duration-threshold")) {
1617 if (*args == NULL) {
1618 fprintf(stderr, "ERROR: --segment-duration-threshold requires a number\n");
1619 return 1;
1620 }
1621 Options.segment_duration_threshold = (unsigned int)strtoul(*args++, NULL, 10);
1622 } else if (!strcmp(arg, "--segment-filename-template")) {
1623 if (*args == NULL) {
1624 fprintf(stderr, "ERROR: --segment-filename-template requires an argument\n");
1625 return 1;
1626 }
1627 Options.segment_filename_template = *args++;
1628 } else if (!strcmp(arg, "--segment-url-template")) {
1629 if (*args == NULL) {
1630 fprintf(stderr, "ERROR: --segment-url-template requires an argument\n");
1631 return 1;
1632 }
1633 Options.segment_url_template = *args++;
1634 } else if (!strcmp(arg, "--pmt-pid")) {
1635 if (*args == NULL) {
1636 fprintf(stderr, "ERROR: --pmt-pid requires a number\n");
1637 return 1;
1638 }
1639 Options.pmt_pid = (unsigned int)strtoul(*args++, NULL, 10);
1640 } else if (!strcmp(arg, "--audio-pid")) {
1641 if (*args == NULL) {
1642 fprintf(stderr, "ERROR: --audio-pid requires a number\n");
1643 return 1;
1644 }
1645 Options.audio_pid = (unsigned int)strtoul(*args++, NULL, 10);
1646 } else if (!strcmp(arg, "--video-pid")) {
1647 if (*args == NULL) {
1648 fprintf(stderr, "ERROR: --video-pid requires a number\n");
1649 return 1;
1650 }
1651 Options.video_pid = (unsigned int)strtoul(*args++, NULL, 10);
1652 } else if (!strcmp(arg, "--audio-track-id")) {
1653 if (*args == NULL) {
1654 fprintf(stderr, "ERROR: --audio-track-id requires a number\n");
1655 return 1;
1656 }
1657 Options.audio_track_id = (unsigned int)strtoul(*args++, NULL, 10);
1658 } else if (!strcmp(arg, "--audio-format")) {
1659 if (*args == NULL) {
1660 fprintf(stderr, "ERROR: --audio-format requires an argument\n");
1661 return 1;
1662 }
1663 const char* format = *args++;
1664 if (!strcmp(format, "ts")) {
1665 Options.audio_format = AUDIO_FORMAT_TS;
1666 } else if (!strcmp(format, "packed")) {
1667 Options.audio_format = AUDIO_FORMAT_PACKED;
1668 } else {
1669 fprintf(stderr, "ERROR: unknown audio format\n");
1670 return 1;
1671 }
1672 } else if (!strcmp(arg, "--video-track-id")) {
1673 if (*args == NULL) {
1674 fprintf(stderr, "ERROR: --video-track-id requires a number\n");
1675 return 1;
1676 }
1677 Options.video_track_id = (unsigned int)strtoul(*args++, NULL, 10);
1678 } else if (!strcmp(arg, "--pcr-offset")) {
1679 if (*args == NULL) {
1680 fprintf(stderr, "ERROR: --pcr-offset requires a number\n");
1681 return 1;
1682 }
1683 Options.pcr_offset = (unsigned int)strtoul(*args++, NULL, 10);
1684 } else if (!strcmp(arg, "--output-single-file")) {
1685 Options.output_single_file = true;
1686 } else if (!strcmp(arg, "--index-filename")) {
1687 if (*args == NULL) {
1688 fprintf(stderr, "ERROR: --index-filename requires a filename\n");
1689 return 1;
1690 }
1691 Options.index_filename = *args++;
1692 } else if (!strcmp(arg, "--iframe-index-filename")) {
1693 if (*args == NULL) {
1694 fprintf(stderr, "ERROR: --iframe-index-filename requires a filename\n");
1695 return 1;
1696 }
1697 Options.iframe_index_filename = *args++;
1698 } else if (!strcmp(arg, "--show-info")) {
1699 Options.show_info = true;
1700 } else if (!strcmp(arg, "--encryption-key")) {
1701 if (*args == NULL) {
1702 fprintf(stderr, "ERROR: --encryption-key requires an argument\n");
1703 return 1;
1704 }
1705 Options.encryption_key_hex = *args++;
1706 result = AP4_ParseHex(Options.encryption_key_hex, Options.encryption_key, 16);
1707 if (AP4_FAILED(result)) {
1708 fprintf(stderr, "ERROR: invalid hex key\n");
1709 return 1;
1710 }
1711 if (Options.encryption_mode == ENCRYPTION_MODE_NONE) {
1712 Options.encryption_mode = ENCRYPTION_MODE_AES_128;
1713 }
1714 } else if (!strcmp(arg, "--encryption-mode")) {
1715 if (*args == NULL) {
1716 fprintf(stderr, "ERROR: --encryption-mode requires an argument\n");
1717 return 1;
1718 }
1719 if (strncmp(*args, "AES-128", 7) == 0) {
1720 Options.encryption_mode = ENCRYPTION_MODE_AES_128;
1721 } else if (strncmp(*args, "SAMPLE-AES", 10) == 0) {
1722 Options.encryption_mode = ENCRYPTION_MODE_SAMPLE_AES;
1723 } else {
1724 fprintf(stderr, "ERROR: unknown encryption mode\n");
1725 return 1;
1726 }
1727 ++args;
1728 } else if (!strcmp(arg, "--encryption-iv-mode")) {
1729 if (*args == NULL) {
1730 fprintf(stderr, "ERROR: --encryption-iv-mode requires an argument\n");
1731 return 1;
1732 }
1733 if (strncmp(*args, "sequence", 8) == 0) {
1734 Options.encryption_iv_mode = ENCRYPTION_IV_MODE_SEQUENCE;
1735 } else if (strncmp(*args, "random", 6) == 0) {
1736 Options.encryption_iv_mode = ENCRYPTION_IV_MODE_RANDOM;
1737 } else if (strncmp(*args, "fps", 3) == 0) {
1738 Options.encryption_iv_mode = ENCRYPTION_IV_MODE_FPS;
1739 } else {
1740 fprintf(stderr, "ERROR: unknown encryption IV mode\n");
1741 return 1;
1742 }
1743 ++args;
1744 } else if (!strcmp(arg, "--encryption-key-uri")) {
1745 if (*args == NULL) {
1746 fprintf(stderr, "ERROR: --encryption-key-uri requires an argument\n");
1747 return 1;
1748 }
1749 Options.encryption_key_uri = *args++;
1750 } else if (!strcmp(arg, "--encryption-key-format")) {
1751 if (*args == NULL) {
1752 fprintf(stderr, "ERROR: --encryption-key-format requires an argument\n");
1753 return 1;
1754 }
1755 Options.encryption_key_format = *args++;
1756 } else if (!strcmp(arg, "--encryption-key-format-versions")) {
1757 if (*args == NULL) {
1758 fprintf(stderr, "ERROR: --encryption-key-format-versions requires an argument\n");
1759 return 1;
1760 }
1761 Options.encryption_key_format_versions = *args++;
1762 } else if (!strcmp(arg, "--encryption-key-line")) {
1763 if (*args == NULL) {
1764 fprintf(stderr, "ERROR: --encryption-key-line requires an argument\n");
1765 return 1;
1766 }
1767 Options.encryption_key_lines.Append(*args++);
1768 } else if (Options.input == NULL) {
1769 Options.input = arg;
1770 } else {
1771 fprintf(stderr, "ERROR: unexpected argument: %s\n", arg);
1772 return 1;
1773 }
1774 }
1775
1776 // check args
1777 if (Options.input == NULL) {
1778 fprintf(stderr, "ERROR: missing input file name\n");
1779 return 1;
1780 }
1781 if (Options.encryption_mode == ENCRYPTION_MODE_NONE && Options.encryption_key_lines.ItemCount() != 0) {
1782 fprintf(stderr, "ERROR: --encryption-key-line requires --encryption-key and --encryption-key-mode\n");
1783 return 1;
1784 }
1785 if (Options.encryption_mode != ENCRYPTION_MODE_NONE && Options.encryption_key_hex == NULL) {
1786 fprintf(stderr, "ERROR: no encryption key specified\n");
1787 return 1;
1788 }
1789 if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES && Options.hls_version > 0 && Options.hls_version < 5) {
1790 Options.hls_version = 5;
1791 fprintf(stderr, "WARNING: forcing version to 5 in order to support SAMPLE-AES encryption\n");
1792 }
1793 if (Options.iframe_index_filename && Options.hls_version > 0 && Options.hls_version < 4) {
1794 fprintf(stderr, "WARNING: forcing version to 4 in order to support I-FRAME-ONLY playlists\n");
1795 Options.hls_version = 4;
1796 }
1797 if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_NONE && Options.encryption_mode != ENCRYPTION_MODE_NONE) {
1798 if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
1799 // sequence-mode IVs don't work well with i-frame only playlists, use random instead
1800 Options.encryption_iv_mode = ENCRYPTION_IV_MODE_RANDOM;
1801 } else {
1802 Options.encryption_iv_mode = ENCRYPTION_IV_MODE_SEQUENCE;
1803 }
1804 }
1805 if ((Options.encryption_key_format || Options.encryption_key_format_versions) && Options.hls_version > 0 && Options.hls_version < 5) {
1806 Options.hls_version = 5;
1807 fprintf(stderr, "WARNING: forcing version to 5 in order to support KEYFORMAT and/or KEYFORMATVERSIONS\n");
1808 }
1809 if (Options.output_single_file && Options.hls_version > 0 && Options.hls_version < 4) {
1810 Options.hls_version = 4;
1811 fprintf(stderr, "WARNING: forcing version to 4 in order to support single file output\n");
1812 }
1813 if (Options.hls_version == 0) {
1814 // default version is 3 for cleartext or AES-128 encryption, and 5 for SAMPLE-AES
1815 if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
1816 Options.hls_version = 5;
1817 } else if (Options.output_single_file || Options.iframe_index_filename) {
1818 Options.hls_version = 4;
1819 } else {
1820 Options.hls_version = 3;
1821 }
1822 }
1823
1824 if (Options.verbose && Options.show_info) {
1825 fprintf(stderr, "WARNING: --verbose will be ignored because --show-info is selected\n");
1826 Options.verbose = false;
1827 }
1828
1829 // compute some derived values
1830 if (Options.iframe_index_filename == NULL) {
1831 if (Options.hls_version >= 4) {
1832 Options.iframe_index_filename = "iframes.m3u8";
1833 }
1834 }
1835 if (Options.audio_format == AUDIO_FORMAT_TS) {
1836 if (Options.segment_filename_template == NULL) {
1837 if (Options.output_single_file) {
1838 Options.segment_filename_template = "stream.ts";
1839 } else {
1840 Options.segment_filename_template = "segment-%d.ts";
1841 }
1842 }
1843 if (Options.segment_url_template == NULL) {
1844 if (Options.output_single_file) {
1845 Options.segment_url_template = "stream.ts";
1846 } else {
1847 Options.segment_url_template = "segment-%d.ts";
1848 }
1849 }
1850 }
1851
1852 if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_FPS) {
1853 if (AP4_StringLength(Options.encryption_key_hex) != 64) {
1854 fprintf(stderr, "ERROR: 'fps' IV mode requires a 32 byte key value (64 characters in hex)\n");
1855 return 1;
1856 }
1857 result = AP4_ParseHex(Options.encryption_key_hex+32, Options.encryption_iv, 16);
1858 if (AP4_FAILED(result)) {
1859 fprintf(stderr, "ERROR: invalid hex IV\n");
1860 return 1;
1861 }
1862 } else if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_RANDOM) {
1863 result = AP4_System_GenerateRandomBytes(Options.encryption_iv, sizeof(Options.encryption_iv));
1864 if (AP4_FAILED(result)) {
1865 fprintf(stderr, "ERROR: failed to get random IV (%d)\n", result);
1866 return 1;
1867 }
1868 }
1869
1870 // create the input stream
1871 AP4_ByteStream* input = NULL;
1872 result = AP4_FileByteStream::Create(Options.input, AP4_FileByteStream::STREAM_MODE_READ, input);
1873 if (AP4_FAILED(result)) {
1874 fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
1875 return 1;
1876 }
1877
1878 // open the file
1879 AP4_File* input_file = new AP4_File(*input, true);
1880
1881 // get the movie
1882 AP4_SampleDescription* sample_description;
1883 AP4_Movie* movie = input_file->GetMovie();
1884 if (movie == NULL) {
1885 fprintf(stderr, "ERROR: no movie in file\n");
1886 return 1;
1887 }
1888
1889 // get the audio and video tracks
1890 AP4_Track* audio_track = NULL;
1891 if (Options.audio_track_id == -1) {
1892 audio_track = movie->GetTrack(AP4_Track::TYPE_AUDIO);
1893 } else if (Options.audio_track_id > 0) {
1894 audio_track = movie->GetTrack((AP4_UI32)Options.audio_track_id);
1895 if (audio_track == NULL) {
1896 fprintf(stderr, "ERROR: audio track ID %d not found\n", Options.audio_track_id);
1897 return 1;
1898 }
1899 if (audio_track->GetType() != AP4_Track::TYPE_AUDIO) {
1900 fprintf(stderr, "ERROR: track ID %d is not an audio track\n", Options.audio_track_id);
1901 return 1;
1902 }
1903 }
1904 AP4_Track* video_track = NULL;
1905 if (Options.video_track_id == -1) {
1906 video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO);
1907 } else if (Options.video_track_id > 0) {
1908 video_track = movie->GetTrack((AP4_UI32)Options.video_track_id);
1909 if (video_track == NULL) {
1910 fprintf(stderr, "ERROR: video track ID %d not found\n", Options.video_track_id);
1911 return 1;
1912 }
1913 if (video_track->GetType() != AP4_Track::TYPE_VIDEO) {
1914 fprintf(stderr, "ERROR: track ID %d is not a video track\n", Options.video_track_id);
1915 return 1;
1916 }
1917 }
1918 if (audio_track == NULL && video_track == NULL) {
1919 fprintf(stderr, "ERROR: no suitable tracks found\n");
1920 delete input_file;
1921 input->Release();
1922 return 1;
1923 }
1924 if (Options.audio_format == AUDIO_FORMAT_PACKED && video_track != NULL) {
1925 if (audio_track == NULL) {
1926 fprintf(stderr, "ERROR: packed audio format requires an audio track\n");
1927 return 1;
1928 }
1929 fprintf(stderr, "WARNING: ignoring video track because of the packed audio format\n");
1930 video_track = NULL;
1931 }
1932 if (video_track == NULL) {
1933 Options.segment_duration_threshold = 0;
1934 }
1935
1936 // create the appropriate readers
1937 AP4_LinearReader* linear_reader = NULL;
1938 SampleReader* audio_reader = NULL;
1939 SampleReader* video_reader = NULL;
1940 if (movie->HasFragments()) {
1941 // create a linear reader to get the samples
1942 linear_reader = new AP4_LinearReader(*movie, input);
1943
1944 if (audio_track) {
1945 linear_reader->EnableTrack(audio_track->GetId());
1946 audio_reader = new FragmentedSampleReader(*linear_reader, audio_track->GetId());
1947 }
1948 if (video_track) {
1949 linear_reader->EnableTrack(video_track->GetId());
1950 video_reader = new FragmentedSampleReader(*linear_reader, video_track->GetId());
1951 }
1952 } else {
1953 if (audio_track) {
1954 audio_reader = new TrackSampleReader(*audio_track);
1955 }
1956 if (video_track) {
1957 video_reader = new TrackSampleReader(*video_track);
1958 }
1959 }
1960
1961 AP4_Mpeg2TsWriter* ts_writer = NULL;
1962 AP4_Mpeg2TsWriter::SampleStream* audio_stream = NULL;
1963 AP4_Mpeg2TsWriter::SampleStream* video_stream = NULL;
1964 AP4_UI08 nalu_length_size = 0;
1965 PackedAudioWriter* packed_writer = NULL;
1966 if (Options.audio_format == AUDIO_FORMAT_PACKED) {
1967 packed_writer = new PackedAudioWriter();
1968
1969 // figure out the file extensions if needed
1970 sample_description = audio_track->GetSampleDescription(0);
1971 if (sample_description == NULL) {
1972 fprintf(stderr, "ERROR: unable to parse audio sample description\n");
1973 goto end;
1974 }
1975 if (Options.segment_filename_template == NULL || Options.segment_url_template == NULL) {
1976 const char* default_stream_name = "stream.es";
1977 const char* default_stream_pattern = "segment-%d.es";
1978 if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
1979 AP4_MpegAudioSampleDescription* mpeg_audio_desc = AP4_DYNAMIC_CAST(AP4_MpegAudioSampleDescription, sample_description);
1980 if (mpeg_audio_desc == NULL ||
1981 !(mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG4_AUDIO ||
1982 mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_LC ||
1983 mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_MAIN)) {
1984 fprintf(stderr, "ERROR: only AAC audio is supported\n");
1985 return 1;
1986 }
1987 default_stream_name = "stream.aac";
1988 default_stream_pattern = "segment-%d.aac";
1989 } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
1990 default_stream_name = "stream.ac3";
1991 default_stream_pattern = "segment-%d.ac3";
1992 } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
1993 default_stream_name = "stream.ec3";
1994 default_stream_pattern = "segment-%d.ec3";
1995 } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_4) {
1996 default_stream_name = "stream.ac4";
1997 default_stream_pattern = "segment-%d.ac4";
1998 }
1999
2000 // override the segment names
2001 if (Options.segment_filename_template == NULL) {
2002 if (Options.output_single_file) {
2003 Options.segment_filename_template = default_stream_name;
2004 } else {
2005 Options.segment_filename_template = default_stream_pattern;
2006 }
2007 }
2008 if (Options.segment_url_template == NULL) {
2009 if (Options.output_single_file) {
2010 Options.segment_url_template = default_stream_name;
2011 } else {
2012 Options.segment_url_template = default_stream_pattern;
2013 }
2014 }
2015 }
2016 } else {
2017 // create an MPEG2 TS Writer
2018 ts_writer = new AP4_Mpeg2TsWriter(Options.pmt_pid);
2019
2020 // add the audio stream
2021 if (audio_track) {
2022 sample_description = audio_track->GetSampleDescription(0);
2023 if (sample_description == NULL) {
2024 fprintf(stderr, "ERROR: unable to parse audio sample description\n");
2025 goto end;
2026 }
2027
2028 unsigned int stream_type = 0;
2029 unsigned int stream_id = 0;
2030 if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
2031 if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
2032 stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ISO_IEC_13818_7;
2033 } else {
2034 stream_type = AP4_MPEG2_STREAM_TYPE_ISO_IEC_13818_7;
2035 }
2036 stream_id = AP4_MPEG2_TS_DEFAULT_STREAM_ID_AUDIO;
2037 } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
2038 if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
2039 stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ATSC_AC3;
2040 } else {
2041 stream_type = AP4_MPEG2_STREAM_TYPE_ATSC_AC3;
2042 }
2043 stream_id = AP4_MPEG2_TS_STREAM_ID_PRIVATE_STREAM_1;
2044 } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
2045 if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
2046 stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ATSC_EAC3;
2047 } else {
2048 stream_type = AP4_MPEG2_STREAM_TYPE_ATSC_EAC3;
2049 }
2050 stream_id = AP4_MPEG2_TS_STREAM_ID_PRIVATE_STREAM_1;
2051 } else {
2052 fprintf(stderr, "ERROR: audio codec not supported\n");
2053 return 1;
2054 }
2055 if (stream_type == AP4_MPEG2_STREAM_TYPE_ATSC_EAC3) {
2056 // E-AC-3 descriptor
2057 unsigned int number_of_channels = 0;
2058 AP4_String track_language;
2059 AP4_Dec3Atom* dec3 = AP4_DYNAMIC_CAST(AP4_Dec3Atom, sample_description->GetDetails().GetChild(AP4_ATOM_TYPE_DEC3));
2060 AP4_BitWriter bits(8);
2061 bits.Write(0xCC, 8);
2062 bits.Write(0x06, 8); // fixed value
2063 bits.Write(0xC0, 8); // reserved, bsid_flag, mainid_flag, asvc_flag, mixinfoexists, substream1_flag, substream2_flag and substream3_flag
2064 bits.Write(24, 5); // reserved, full_service_flag and service_type
2065 if (dec3->GetSubStreams()[0].acmod == 0) {
2066 number_of_channels = 1;
2067 } else if (dec3->GetSubStreams()[0].acmod == 1) {
2068 number_of_channels = 0;
2069 } else if (dec3->GetSubStreams()[0].acmod == 2) {
2070 number_of_channels = 2;
2071 } else {
2072 number_of_channels = 4;
2073 }
2074 if (dec3->GetSubStreams()[0].num_dep_sub > 0) {
2075 number_of_channels = 5;
2076 }
2077 bits.Write(number_of_channels, 3); // number_of_channels
2078 bits.Write(4, 3); // language_flag, language_flag_2, reserved
2079 bits.Write(dec3->GetSubStreams()[0].bsid, 5); // bsid
2080 track_language = audio_track->GetTrackLanguage();
2081 if (track_language.GetLength() == 3) {
2082 bits.Write(track_language.GetChars()[0], 8);
2083 bits.Write(track_language.GetChars()[1], 8);
2084 bits.Write(track_language.GetChars()[2], 8);
2085 } else {
2086 bits.Write(0x75, 8);
2087 bits.Write(0x6E, 8);
2088 bits.Write(0x64, 8);
2089 }
2090 // setup the audio stream
2091 result = ts_writer->SetAudioStream(audio_track->GetMediaTimeScale(),
2092 stream_type,
2093 stream_id,
2094 audio_stream,
2095 Options.audio_pid,
2096 bits.GetData(), 8,
2097 Options.pcr_offset);
2098 } else {
2099 // setup the audio stream
2100 result = ts_writer->SetAudioStream(audio_track->GetMediaTimeScale(),
2101 stream_type,
2102 stream_id,
2103 audio_stream,
2104 Options.audio_pid,
2105 NULL, 0,
2106 Options.pcr_offset);
2107 }
2108 if (AP4_FAILED(result)) {
2109 fprintf(stderr, "could not create audio stream (%d)\n", result);
2110 goto end;
2111 }
2112 }
2113
2114 // add the video stream
2115 if (video_track) {
2116 sample_description = video_track->GetSampleDescription(0);
2117 if (sample_description == NULL) {
2118 fprintf(stderr, "ERROR: unable to parse video sample description\n");
2119 goto end;
2120 }
2121
2122 // decide on the stream type
2123 unsigned int stream_type = 0;
2124 unsigned int stream_id = AP4_MPEG2_TS_DEFAULT_STREAM_ID_VIDEO;
2125 if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC1 ||
2126 sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC2 ||
2127 sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC3 ||
2128 sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC4 ||
2129 sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVAV ||
2130 sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVA1) {
2131 if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
2132 stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_AVC;
2133 AP4_AvcSampleDescription* avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, sample_description);
2134 if (avc_desc == NULL) {
2135 fprintf(stderr, "ERROR: not a proper AVC track\n");
2136 return 1;
2137 }
2138 nalu_length_size = avc_desc->GetNaluLengthSize();
2139 } else {
2140 stream_type = AP4_MPEG2_STREAM_TYPE_AVC;
2141 }
2142 } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_HEV1 ||
2143 sample_description->GetFormat() == AP4_SAMPLE_FORMAT_HVC1 ||
2144 sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVHE ||
2145 sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVH1) {
2146 stream_type = AP4_MPEG2_STREAM_TYPE_HEVC;
2147 } else {
2148 fprintf(stderr, "ERROR: video codec not supported\n");
2149 return 1;
2150 }
2151 if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
2152 if (stream_type != AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_AVC) {
2153 fprintf(stderr, "ERROR: AES-SAMPLE encryption can only be used with H.264 video\n");
2154 return 1;
2155 }
2156 }
2157
2158 // setup the video stream
2159 result = ts_writer->SetVideoStream(video_track->GetMediaTimeScale(),
2160 stream_type,
2161 stream_id,
2162 video_stream,
2163 Options.video_pid,
2164 NULL, 0,
2165 Options.pcr_offset);
2166 if (AP4_FAILED(result)) {
2167 fprintf(stderr, "could not create video stream (%d)\n", result);
2168 goto end;
2169 }
2170 }
2171 }
2172
2173 result = WriteSamples(ts_writer, packed_writer,
2174 audio_track, audio_reader, audio_stream,
2175 video_track, video_reader, video_stream,
2176 Options.segment_duration_threshold,
2177 nalu_length_size);
2178 if (AP4_FAILED(result)) {
2179 fprintf(stderr, "ERROR: failed to write samples (%d)\n", result);
2180 }
2181
2182 if (Options.show_info) {
2183 double average_segment_bitrate = 0.0;
2184 if (Stats.segments_total_duration != 0.0) {
2185 average_segment_bitrate = 8.0*(double)Stats.segments_total_size/Stats.segments_total_duration;
2186 }
2187 double average_iframe_bitrate = 0.0;
2188 if (Stats.segments_total_duration != 0.0) {
2189 average_iframe_bitrate = 8.0*(double)Stats.iframes_total_size/Stats.segments_total_duration;
2190 }
2191
2192 double frame_rate = 0.0;
2193 if (video_track && (Stats.segments_total_duration != 0.0)) {
2194 double sample_count = (double)video_track->GetSampleCount();
2195 double media_duration = (double)video_track->GetMediaDuration();
2196 double timescale = (double)video_track->GetMediaTimeScale();
2197 if (media_duration > 0.0) {
2198 frame_rate = sample_count/(media_duration/timescale);
2199 }
2200 }
2201
2202 printf(
2203 "{\n"
2204 );
2205 printf(
2206 " \"stats\": {\n"
2207 " \"duration\": %f,\n"
2208 " \"avg_segment_bitrate\": %f,\n"
2209 " \"max_segment_bitrate\": %f,\n"
2210 " \"avg_iframe_bitrate\": %f,\n"
2211 " \"max_iframe_bitrate\": %f,\n"
2212 " \"frame_rate\": %f\n"
2213 " }",
2214 (double)movie->GetDurationMs()/1000.0,
2215 average_segment_bitrate,
2216 Stats.max_segment_bitrate,
2217 average_iframe_bitrate,
2218 Stats.max_iframe_bitrate,
2219 frame_rate
2220 );
2221 if (audio_track) {
2222 AP4_String codec;
2223 AP4_SampleDescription* sdesc = audio_track->GetSampleDescription(0);
2224 if (sdesc) {
2225 sdesc->GetCodecString(codec);
2226 }
2227 printf(
2228 ",\n"
2229 " \"audio\": {\n"
2230 " \"codec\": \"%s\"\n"
2231 " }",
2232 codec.GetChars()
2233 );
2234 }
2235 if (video_track) {
2236 AP4_String codec;
2237 AP4_UI16 width = (AP4_UI16)(video_track->GetWidth()/65536.0);
2238 AP4_UI16 height = (AP4_UI16)(video_track->GetHeight()/65536.0);
2239 AP4_SampleDescription* sdesc = video_track->GetSampleDescription(0);
2240 if (sdesc) {
2241 sdesc->GetCodecString(codec);
2242 AP4_VideoSampleDescription* video_desc = AP4_DYNAMIC_CAST(AP4_VideoSampleDescription, sdesc);
2243 if (video_desc) {
2244 width = video_desc->GetWidth();
2245 height = video_desc->GetHeight();
2246 }
2247 }
2248 printf(
2249 ",\n"
2250 " \"video\": {\n"
2251 " \"codec\": \"%s\",\n"
2252 " \"width\": %d,\n"
2253 " \"height\": %d\n"
2254 " }",
2255 codec.GetChars(),
2256 width,
2257 height
2258 );
2259 }
2260 printf(
2261 "\n"
2262 "}\n"
2263 );
2264 }
2265
2266 end:
2267 delete ts_writer;
2268 delete packed_writer;
2269 delete input_file;
2270 input->Release();
2271 delete linear_reader;
2272 delete audio_reader;
2273 delete video_reader;
2274
2275 return result == AP4_SUCCESS?0:1;
2276 }
2277
2278