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