1 from __future__
import unicode_literals
8 from .fragment
import FragmentFD
9 from ..compat
import compat_urllib_error
12 u8
= struct
.Struct(b
'>B')
13 u88
= struct
.Struct(b
'>Bx')
14 u16
= struct
.Struct(b
'>H')
15 u1616
= struct
.Struct(b
'>Hxx')
16 u32
= struct
.Struct(b
'>I')
17 u64
= struct
.Struct(b
'>Q')
19 s88
= struct
.Struct(b
'>bx')
20 s16
= struct
.Struct(b
'>h')
21 s1616
= struct
.Struct(b
'>hxx')
22 s32
= struct
.Struct(b
'>i')
24 unity_matrix
= (s32
.pack(0x10000) + s32
.pack(0) * 3) * 2 + s32
.pack(0x40000000)
28 TRACK_IN_PREVIEW
= 0x4
33 def box(box_type
, payload
):
34 return u32
.pack(8 + len(payload
)) + box_type
+ payload
37 def full_box(box_type
, version
, flags
, payload
):
38 return box(box_type
, u8
.pack(version
) + u32
.pack(flags
)[1:] + payload
)
41 def write_piff_header(stream
, params
):
42 track_id
= params
['track_id']
43 fourcc
= params
['fourcc']
44 duration
= params
['duration']
45 timescale
= params
.get('timescale', 10000000)
46 language
= params
.get('language', 'und')
47 height
= params
.get('height', 0)
48 width
= params
.get('width', 0)
49 is_audio
= width
== 0 and height
== 0
50 creation_time
= modification_time
= int(time
.time())
52 ftyp_payload
= b
'isml' # major brand
53 ftyp_payload
+= u32
.pack(1) # minor version
54 ftyp_payload
+= b
'piff' + b
'iso2' # compatible brands
55 stream
.write(box(b
'ftyp', ftyp_payload
)) # File Type Box
57 mvhd_payload
= u64
.pack(creation_time
)
58 mvhd_payload
+= u64
.pack(modification_time
)
59 mvhd_payload
+= u32
.pack(timescale
)
60 mvhd_payload
+= u64
.pack(duration
)
61 mvhd_payload
+= s1616
.pack(1) # rate
62 mvhd_payload
+= s88
.pack(1) # volume
63 mvhd_payload
+= u16
.pack(0) # reserved
64 mvhd_payload
+= u32
.pack(0) * 2 # reserved
65 mvhd_payload
+= unity_matrix
66 mvhd_payload
+= u32
.pack(0) * 6 # pre defined
67 mvhd_payload
+= u32
.pack(0xffffffff) # next track id
68 moov_payload
= full_box(b
'mvhd', 1, 0, mvhd_payload
) # Movie Header Box
70 tkhd_payload
= u64
.pack(creation_time
)
71 tkhd_payload
+= u64
.pack(modification_time
)
72 tkhd_payload
+= u32
.pack(track_id
) # track id
73 tkhd_payload
+= u32
.pack(0) # reserved
74 tkhd_payload
+= u64
.pack(duration
)
75 tkhd_payload
+= u32
.pack(0) * 2 # reserved
76 tkhd_payload
+= s16
.pack(0) # layer
77 tkhd_payload
+= s16
.pack(0) # alternate group
78 tkhd_payload
+= s88
.pack(1 if is_audio
else 0) # volume
79 tkhd_payload
+= u16
.pack(0) # reserved
80 tkhd_payload
+= unity_matrix
81 tkhd_payload
+= u1616
.pack(width
)
82 tkhd_payload
+= u1616
.pack(height
)
83 trak_payload
= full_box(b
'tkhd', 1, TRACK_ENABLED | TRACK_IN_MOVIE | TRACK_IN_PREVIEW
, tkhd_payload
) # Track Header Box
85 mdhd_payload
= u64
.pack(creation_time
)
86 mdhd_payload
+= u64
.pack(modification_time
)
87 mdhd_payload
+= u32
.pack(timescale
)
88 mdhd_payload
+= u64
.pack(duration
)
89 mdhd_payload
+= u16
.pack(((ord(language
[0]) - 0x60) << 10) |
((ord(language
[1]) - 0x60) << 5) |
(ord(language
[2]) - 0x60))
90 mdhd_payload
+= u16
.pack(0) # pre defined
91 mdia_payload
= full_box(b
'mdhd', 1, 0, mdhd_payload
) # Media Header Box
93 hdlr_payload
= u32
.pack(0) # pre defined
94 hdlr_payload
+= b
'soun' if is_audio
else b
'vide' # handler type
95 hdlr_payload
+= u32
.pack(0) * 3 # reserved
96 hdlr_payload
+= (b
'Sound' if is_audio
else b
'Video') + b
'Handler\0' # name
97 mdia_payload
+= full_box(b
'hdlr', 0, 0, hdlr_payload
) # Handler Reference Box
100 smhd_payload
= s88
.pack(0) # balance
101 smhd_payload
= u16
.pack(0) # reserved
102 media_header_box
= full_box(b
'smhd', 0, 0, smhd_payload
) # Sound Media Header
104 vmhd_payload
= u16
.pack(0) # graphics mode
105 vmhd_payload
+= u16
.pack(0) * 3 # opcolor
106 media_header_box
= full_box(b
'vmhd', 0, 1, vmhd_payload
) # Video Media Header
107 minf_payload
= media_header_box
109 dref_payload
= u32
.pack(1) # entry count
110 dref_payload
+= full_box(b
'url ', 0, SELF_CONTAINED
, b
'') # Data Entry URL Box
111 dinf_payload
= full_box(b
'dref', 0, 0, dref_payload
) # Data Reference Box
112 minf_payload
+= box(b
'dinf', dinf_payload
) # Data Information Box
114 stsd_payload
= u32
.pack(1) # entry count
116 sample_entry_payload
= u8
.pack(0) * 6 # reserved
117 sample_entry_payload
+= u16
.pack(1) # data reference index
119 sample_entry_payload
+= u32
.pack(0) * 2 # reserved
120 sample_entry_payload
+= u16
.pack(params
.get('channels', 2))
121 sample_entry_payload
+= u16
.pack(params
.get('bits_per_sample', 16))
122 sample_entry_payload
+= u16
.pack(0) # pre defined
123 sample_entry_payload
+= u16
.pack(0) # reserved
124 sample_entry_payload
+= u1616
.pack(params
['sampling_rate'])
127 sample_entry_box
= box(b
'mp4a', sample_entry_payload
)
129 sample_entry_payload
= sample_entry_payload
130 sample_entry_payload
+= u16
.pack(0) # pre defined
131 sample_entry_payload
+= u16
.pack(0) # reserved
132 sample_entry_payload
+= u32
.pack(0) * 3 # pre defined
133 sample_entry_payload
+= u16
.pack(width
)
134 sample_entry_payload
+= u16
.pack(height
)
135 sample_entry_payload
+= u1616
.pack(0x48) # horiz resolution 72 dpi
136 sample_entry_payload
+= u1616
.pack(0x48) # vert resolution 72 dpi
137 sample_entry_payload
+= u32
.pack(0) # reserved
138 sample_entry_payload
+= u16
.pack(1) # frame count
139 sample_entry_payload
+= u8
.pack(0) * 32 # compressor name
140 sample_entry_payload
+= u16
.pack(0x18) # depth
141 sample_entry_payload
+= s16
.pack(-1) # pre defined
143 codec_private_data
= binascii
.unhexlify(params
['codec_private_data'])
144 if fourcc
in ('H264', 'AVC1'):
145 sps
, pps
= codec_private_data
.split(u32
.pack(1))[1:]
146 avcc_payload
= u8
.pack(1) # configuration version
147 avcc_payload
+= sps
[1:4] # avc profile indication + profile compatibility + avc level indication
148 avcc_payload
+= u8
.pack(0xfc |
(params
.get('nal_unit_length_field', 4) - 1)) # complete represenation (1) + reserved (11111) + length size minus one
149 avcc_payload
+= u8
.pack(1) # reserved (0) + number of sps (0000001)
150 avcc_payload
+= u16
.pack(len(sps
))
152 avcc_payload
+= u8
.pack(1) # number of pps
153 avcc_payload
+= u16
.pack(len(pps
))
155 sample_entry_payload
+= box(b
'avcC', avcc_payload
) # AVC Decoder Configuration Record
156 sample_entry_box
= box(b
'avc1', sample_entry_payload
) # AVC Simple Entry
157 stsd_payload
+= sample_entry_box
159 stbl_payload
= full_box(b
'stsd', 0, 0, stsd_payload
) # Sample Description Box
161 stts_payload
= u32
.pack(0) # entry count
162 stbl_payload
+= full_box(b
'stts', 0, 0, stts_payload
) # Decoding Time to Sample Box
164 stsc_payload
= u32
.pack(0) # entry count
165 stbl_payload
+= full_box(b
'stsc', 0, 0, stsc_payload
) # Sample To Chunk Box
167 stco_payload
= u32
.pack(0) # entry count
168 stbl_payload
+= full_box(b
'stco', 0, 0, stco_payload
) # Chunk Offset Box
170 minf_payload
+= box(b
'stbl', stbl_payload
) # Sample Table Box
172 mdia_payload
+= box(b
'minf', minf_payload
) # Media Information Box
174 trak_payload
+= box(b
'mdia', mdia_payload
) # Media Box
176 moov_payload
+= box(b
'trak', trak_payload
) # Track Box
178 mehd_payload
= u64
.pack(duration
)
179 mvex_payload
= full_box(b
'mehd', 1, 0, mehd_payload
) # Movie Extends Header Box
181 trex_payload
= u32
.pack(track_id
) # track id
182 trex_payload
+= u32
.pack(1) # default sample description index
183 trex_payload
+= u32
.pack(0) # default sample duration
184 trex_payload
+= u32
.pack(0) # default sample size
185 trex_payload
+= u32
.pack(0) # default sample flags
186 mvex_payload
+= full_box(b
'trex', 0, 0, trex_payload
) # Track Extends Box
188 moov_payload
+= box(b
'mvex', mvex_payload
) # Movie Extends Box
189 stream
.write(box(b
'moov', moov_payload
)) # Movie Box
192 def extract_box_data(data
, box_sequence
):
193 data_reader
= io
.BytesIO(data
)
195 box_size
= u32
.unpack(data_reader
.read(4))[0]
196 box_type
= data_reader
.read(4)
197 if box_type
== box_sequence
[0]:
198 box_data
= data_reader
.read(box_size
- 8)
199 if len(box_sequence
) == 1:
201 return extract_box_data(box_data
, box_sequence
[1:])
202 data_reader
.seek(box_size
- 8, 1)
205 class IsmFD(FragmentFD
):
207 Download segments in a ISM manifest
212 def real_download(self
, filename
, info_dict
):
213 segments
= info_dict
['fragments'][:1] if self
.params
.get(
214 'test', False) else info_dict
['fragments']
217 'filename': filename
,
218 'total_frags': len(segments
),
221 self
._prepare
_and
_start
_frag
_download
(ctx
)
223 fragment_retries
= self
.params
.get('fragment_retries', 0)
224 skip_unavailable_fragments
= self
.params
.get('skip_unavailable_fragments', True)
226 track_written
= False
228 for i
, segment
in enumerate(segments
):
230 if frag_index
<= ctx
['fragment_index']:
233 while count
<= fragment_retries
:
235 success
, frag_content
= self
._download
_fragment
(ctx
, segment
['url'], info_dict
)
238 if not track_written
:
239 tfhd_data
= extract_box_data(frag_content
, [b
'moof', b
'traf', b
'tfhd'])
240 info_dict
['_download_params']['track_id'] = u32
.unpack(tfhd_data
[4:8])[0]
241 write_piff_header(ctx
['dest_stream'], info_dict
['_download_params'])
243 self
._append
_fragment
(ctx
, frag_content
)
245 except compat_urllib_error
.HTTPError
as err
:
247 if count
<= fragment_retries
:
248 self
.report_retry_fragment(err
, frag_index
, count
, fragment_retries
)
249 if count
> fragment_retries
:
250 if skip_unavailable_fragments
:
251 self
.report_skip_fragment(frag_index
)
253 self
.report_error('giving up after %s fragment retries' % fragment_retries
)
256 self
._finish
_frag
_download
(ctx
)