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
, {
243 'http_headers': info_dict
.get('http_headers'),
247 down
, target_sanitized
= sanitize_open(target_filename
, 'rb')
248 down_data
= down
.read()
249 if not track_written
:
250 tfhd_data
= extract_box_data(down_data
, [b
'moof', b
'traf', b
'tfhd'])
251 info_dict
['_download_params']['track_id'] = u32
.unpack(tfhd_data
[4:8])[0]
252 write_piff_header(ctx
['dest_stream'], info_dict
['_download_params'])
254 ctx
['dest_stream'].write(down_data
)
256 segments_filenames
.append(target_sanitized
)
258 except compat_urllib_error
.HTTPError
as err
:
260 if count
<= fragment_retries
:
261 self
.report_retry_fragment(err
, segment_name
, count
, fragment_retries
)
262 if count
> fragment_retries
:
263 if skip_unavailable_fragments
:
264 self
.report_skip_fragment(segment_name
)
266 self
.report_error('giving up after %s fragment retries' % fragment_retries
)
269 self
._finish
_frag
_download
(ctx
)
271 for segment_file
in segments_filenames
:
272 os
.remove(encodeFilename(segment_file
))