1 from __future__ 
import unicode_literals
 
   9 from .fragment 
import FragmentFD
 
  10 from ..compat 
import compat_urllib_error
 
  17 u8 
= struct
.Struct(b
'>B') 
  18 u88 
= struct
.Struct(b
'>Bx') 
  19 u16 
= struct
.Struct(b
'>H') 
  20 u1616 
= struct
.Struct(b
'>Hxx') 
  21 u32 
= struct
.Struct(b
'>I') 
  22 u64 
= struct
.Struct(b
'>Q') 
  24 s88 
= struct
.Struct(b
'>bx') 
  25 s16 
= struct
.Struct(b
'>h') 
  26 s1616 
= struct
.Struct(b
'>hxx') 
  27 s32 
= struct
.Struct(b
'>i') 
  29 unity_matrix 
= (s32
.pack(0x10000) + s32
.pack(0) * 3) * 2 + s32
.pack(0x40000000) 
  33 TRACK_IN_PREVIEW 
= 0x4 
  38 def box(box_type
, payload
): 
  39     return u32
.pack(8 + len(payload
)) + box_type 
+ payload
 
  42 def full_box(box_type
, version
, flags
, payload
): 
  43     return box(box_type
, u8
.pack(version
) + u32
.pack(flags
)[1:] + payload
) 
  46 def write_piff_header(stream
, params
): 
  47     track_id 
= params
['track_id'] 
  48     fourcc 
= params
['fourcc'] 
  49     duration 
= params
['duration'] 
  50     timescale 
= params
.get('timescale', 10000000) 
  51     language 
= params
.get('language', 'und') 
  52     height 
= params
.get('height', 0) 
  53     width 
= params
.get('width', 0) 
  54     is_audio 
= width 
== 0 and height 
== 0 
  55     creation_time 
= modification_time 
= int(time
.time()) 
  57     ftyp_payload 
= b
'isml'  # major brand 
  58     ftyp_payload 
+= u32
.pack(1)  # minor version 
  59     ftyp_payload 
+= b
'piff' + b
'iso2'  # compatible brands 
  60     stream
.write(box(b
'ftyp', ftyp_payload
))  # File Type Box 
  62     mvhd_payload 
= u64
.pack(creation_time
) 
  63     mvhd_payload 
+= u64
.pack(modification_time
) 
  64     mvhd_payload 
+= u32
.pack(timescale
) 
  65     mvhd_payload 
+= u64
.pack(duration
) 
  66     mvhd_payload 
+= s1616
.pack(1)  # rate 
  67     mvhd_payload 
+= s88
.pack(1)  # volume 
  68     mvhd_payload 
+= u16
.pack(0)  # reserved 
  69     mvhd_payload 
+= u32
.pack(0) * 2  # reserved 
  70     mvhd_payload 
+= unity_matrix
 
  71     mvhd_payload 
+= u32
.pack(0) * 6  # pre defined 
  72     mvhd_payload 
+= u32
.pack(0xffffffff)  # next track id 
  73     moov_payload 
= full_box(b
'mvhd', 1, 0, mvhd_payload
)  # Movie Header Box 
  75     tkhd_payload 
= u64
.pack(creation_time
) 
  76     tkhd_payload 
+= u64
.pack(modification_time
) 
  77     tkhd_payload 
+= u32
.pack(track_id
)  # track id 
  78     tkhd_payload 
+= u32
.pack(0)  # reserved 
  79     tkhd_payload 
+= u64
.pack(duration
) 
  80     tkhd_payload 
+= u32
.pack(0) * 2  # reserved 
  81     tkhd_payload 
+= s16
.pack(0)  # layer 
  82     tkhd_payload 
+= s16
.pack(0)  # alternate group 
  83     tkhd_payload 
+= s88
.pack(1 if is_audio 
else 0)  # volume 
  84     tkhd_payload 
+= u16
.pack(0)  # reserved 
  85     tkhd_payload 
+= unity_matrix
 
  86     tkhd_payload 
+= u1616
.pack(width
) 
  87     tkhd_payload 
+= u1616
.pack(height
) 
  88     trak_payload 
= full_box(b
'tkhd', 1, TRACK_ENABLED | TRACK_IN_MOVIE | TRACK_IN_PREVIEW
, tkhd_payload
)  # Track Header Box 
  90     mdhd_payload 
= u64
.pack(creation_time
) 
  91     mdhd_payload 
+= u64
.pack(modification_time
) 
  92     mdhd_payload 
+= u32
.pack(timescale
) 
  93     mdhd_payload 
+= u64
.pack(duration
) 
  94     mdhd_payload 
+= u16
.pack(((ord(language
[0]) - 0x60) << 10) | 
((ord(language
[1]) - 0x60) << 5) | 
(ord(language
[2]) - 0x60)) 
  95     mdhd_payload 
+= u16
.pack(0)  # pre defined 
  96     mdia_payload 
= full_box(b
'mdhd', 1, 0, mdhd_payload
)  # Media Header Box 
  98     hdlr_payload 
= u32
.pack(0)  # pre defined 
  99     hdlr_payload 
+= b
'soun' if is_audio 
else b
'vide'  # handler type 
 100     hdlr_payload 
+= u32
.pack(0) * 3  # reserved 
 101     hdlr_payload 
+= (b
'Sound' if is_audio 
else b
'Video') + b
'Handler\0'  # name 
 102     mdia_payload 
+= full_box(b
'hdlr', 0, 0, hdlr_payload
)  # Handler Reference Box 
 105         smhd_payload 
= s88
.pack(0)  # balance 
 106         smhd_payload 
= u16
.pack(0)  # reserved 
 107         media_header_box 
= full_box(b
'smhd', 0, 0, smhd_payload
)  # Sound Media Header 
 109         vmhd_payload 
= u16
.pack(0)  # graphics mode 
 110         vmhd_payload 
+= u16
.pack(0) * 3  # opcolor 
 111         media_header_box 
= full_box(b
'vmhd', 0, 1, vmhd_payload
)  # Video Media Header 
 112     minf_payload 
= media_header_box
 
 114     dref_payload 
= u32
.pack(1)  # entry count 
 115     dref_payload 
+= full_box(b
'url ', 0, SELF_CONTAINED
, b
'')  # Data Entry URL Box 
 116     dinf_payload 
= full_box(b
'dref', 0, 0, dref_payload
)  # Data Reference Box 
 117     minf_payload 
+= box(b
'dinf', dinf_payload
)  # Data Information Box 
 119     stsd_payload 
= u32
.pack(1)  # entry count 
 121     sample_entry_payload 
= u8
.pack(0) * 6  # reserved 
 122     sample_entry_payload 
+= u16
.pack(1)  # data reference index 
 124         sample_entry_payload 
+= u32
.pack(0) * 2  # reserved 
 125         sample_entry_payload 
+= u16
.pack(params
.get('channels', 2)) 
 126         sample_entry_payload 
+= u16
.pack(params
.get('bits_per_sample', 16)) 
 127         sample_entry_payload 
+= u16
.pack(0)  # pre defined 
 128         sample_entry_payload 
+= u16
.pack(0)  # reserved 
 129         sample_entry_payload 
+= u1616
.pack(params
['sampling_rate']) 
 132             sample_entry_box 
= box(b
'mp4a', sample_entry_payload
) 
 134         sample_entry_payload 
= sample_entry_payload
 
 135         sample_entry_payload 
+= u16
.pack(0)  # pre defined 
 136         sample_entry_payload 
+= u16
.pack(0)  # reserved 
 137         sample_entry_payload 
+= u32
.pack(0) * 3  # pre defined 
 138         sample_entry_payload 
+= u16
.pack(width
) 
 139         sample_entry_payload 
+= u16
.pack(height
) 
 140         sample_entry_payload 
+= u1616
.pack(0x48)  # horiz resolution 72 dpi 
 141         sample_entry_payload 
+= u1616
.pack(0x48)  # vert resolution 72 dpi 
 142         sample_entry_payload 
+= u32
.pack(0)  # reserved 
 143         sample_entry_payload 
+= u16
.pack(1)  # frame count 
 144         sample_entry_payload 
+= u8
.pack(0) * 32  # compressor name 
 145         sample_entry_payload 
+= u16
.pack(0x18)  # depth 
 146         sample_entry_payload 
+= s16
.pack(-1)  # pre defined 
 148         codec_private_data 
= binascii
.unhexlify(params
['codec_private_data']) 
 149         if fourcc 
in ('H264', 'AVC1'): 
 150             sps
, pps 
= codec_private_data
.split(u32
.pack(1))[1:] 
 151             avcc_payload 
= u8
.pack(1)  # configuration version 
 152             avcc_payload 
+= sps
[1:4]  # avc profile indication + profile compatibility + avc level indication 
 153             avcc_payload 
+= u8
.pack(0xfc | 
(params
.get('nal_unit_length_field', 4) - 1))  # complete represenation (1) + reserved (11111) + length size minus one 
 154             avcc_payload 
+= u8
.pack(1)  # reserved (0) + number of sps (0000001) 
 155             avcc_payload 
+= u16
.pack(len(sps
)) 
 157             avcc_payload 
+= u8
.pack(1)  # number of pps 
 158             avcc_payload 
+= u16
.pack(len(pps
)) 
 160             sample_entry_payload 
+= box(b
'avcC', avcc_payload
)  # AVC Decoder Configuration Record 
 161             sample_entry_box 
= box(b
'avc1', sample_entry_payload
)  # AVC Simple Entry 
 162     stsd_payload 
+= sample_entry_box
 
 164     stbl_payload 
= full_box(b
'stsd', 0, 0, stsd_payload
)  # Sample Description Box 
 166     stts_payload 
= u32
.pack(0)  # entry count 
 167     stbl_payload 
+= full_box(b
'stts', 0, 0, stts_payload
)  # Decoding Time to Sample Box 
 169     stsc_payload 
= u32
.pack(0)  # entry count 
 170     stbl_payload 
+= full_box(b
'stsc', 0, 0, stsc_payload
)  # Sample To Chunk Box 
 172     stco_payload 
= u32
.pack(0)  # entry count 
 173     stbl_payload 
+= full_box(b
'stco', 0, 0, stco_payload
)  # Chunk Offset Box 
 175     minf_payload 
+= box(b
'stbl', stbl_payload
)  # Sample Table Box 
 177     mdia_payload 
+= box(b
'minf', minf_payload
)  # Media Information Box 
 179     trak_payload 
+= box(b
'mdia', mdia_payload
)  # Media Box 
 181     moov_payload 
+= box(b
'trak', trak_payload
)  # Track Box 
 183     mehd_payload 
= u64
.pack(duration
) 
 184     mvex_payload 
= full_box(b
'mehd', 1, 0, mehd_payload
)  # Movie Extends Header Box 
 186     trex_payload 
= u32
.pack(track_id
)  # track id 
 187     trex_payload 
+= u32
.pack(1)  # default sample description index 
 188     trex_payload 
+= u32
.pack(0)  # default sample duration 
 189     trex_payload 
+= u32
.pack(0)  # default sample size 
 190     trex_payload 
+= u32
.pack(0)  # default sample flags 
 191     mvex_payload 
+= full_box(b
'trex', 0, 0, trex_payload
)  # Track Extends Box 
 193     moov_payload 
+= box(b
'mvex', mvex_payload
)  # Movie Extends Box 
 194     stream
.write(box(b
'moov', moov_payload
))  # Movie Box 
 197 def extract_box_data(data
, box_sequence
): 
 198     data_reader 
= io
.BytesIO(data
) 
 200         box_size 
= u32
.unpack(data_reader
.read(4))[0] 
 201         box_type 
= data_reader
.read(4) 
 202         if box_type 
== box_sequence
[0]: 
 203             box_data 
= data_reader
.read(box_size 
- 8) 
 204             if len(box_sequence
) == 1: 
 206             return extract_box_data(box_data
, box_sequence
[1:]) 
 207         data_reader
.seek(box_size 
- 8, 1) 
 210 class IsmFD(FragmentFD
): 
 212     Download segments in a ISM manifest 
 217     def real_download(self
, filename
, info_dict
): 
 218         segments 
= info_dict
['fragments'][:1] if self
.params
.get( 
 219             'test', False) else info_dict
['fragments'] 
 222             'filename': filename
, 
 223             'total_frags': len(segments
), 
 226         self
._prepare
_and
_start
_frag
_download
(ctx
) 
 228         segments_filenames 
= [] 
 230         fragment_retries 
= self
.params
.get('fragment_retries', 0) 
 231         skip_unavailable_fragments 
= self
.params
.get('skip_unavailable_fragments', True) 
 233         track_written 
= False 
 234         for i
, segment 
in enumerate(segments
): 
 235             segment_url 
= segment
['url'] 
 236             segment_name 
= 'Frag%d' % i
 
 237             target_filename 
= '%s-%s' % (ctx
['tmpfilename'], segment_name
) 
 239             while count 
<= fragment_retries
: 
 241                     success 
= ctx
['dl'].download(target_filename
, {'url': segment_url
}) 
 244                     down
, target_sanitized 
= sanitize_open(target_filename
, 'rb') 
 245                     down_data 
= down
.read() 
 246                     if not track_written
: 
 247                         tfhd_data 
= extract_box_data(down_data
, [b
'moof', b
'traf', b
'tfhd']) 
 248                         info_dict
['_download_params']['track_id'] = u32
.unpack(tfhd_data
[4:8])[0] 
 249                         write_piff_header(ctx
['dest_stream'], info_dict
['_download_params']) 
 251                     ctx
['dest_stream'].write(down_data
) 
 253                     segments_filenames
.append(target_sanitized
) 
 255                 except compat_urllib_error
.HTTPError 
as err
: 
 257                     if count 
<= fragment_retries
: 
 258                         self
.report_retry_fragment(err
, segment_name
, count
, fragment_retries
) 
 259             if count 
> fragment_retries
: 
 260                 if skip_unavailable_fragments
: 
 261                     self
.report_skip_fragment(segment_name
) 
 263                 self
.report_error('giving up after %s fragment retries' % fragment_retries
) 
 266         self
._finish
_frag
_download
(ctx
) 
 268         for segment_file 
in segments_filenames
: 
 269             os
.remove(encodeFilename(segment_file
))