1 from __future__
import unicode_literals
7 from .fragment
import FragmentFD
14 u8
= compat_Struct('>B')
15 u88
= compat_Struct('>Bx')
16 u16
= compat_Struct('>H')
17 u1616
= compat_Struct('>Hxx')
18 u32
= compat_Struct('>I')
19 u64
= compat_Struct('>Q')
21 s88
= compat_Struct('>bx')
22 s16
= compat_Struct('>h')
23 s1616
= compat_Struct('>hxx')
24 s32
= compat_Struct('>i')
26 unity_matrix
= (s32
.pack(0x10000) + s32
.pack(0) * 3) * 2 + s32
.pack(0x40000000)
30 TRACK_IN_PREVIEW
= 0x4
35 def box(box_type
, payload
):
36 return u32
.pack(8 + len(payload
)) + box_type
+ payload
39 def full_box(box_type
, version
, flags
, payload
):
40 return box(box_type
, u8
.pack(version
) + u32
.pack(flags
)[1:] + payload
)
43 def write_piff_header(stream
, params
):
44 track_id
= params
['track_id']
45 fourcc
= params
['fourcc']
46 duration
= params
['duration']
47 timescale
= params
.get('timescale', 10000000)
48 language
= params
.get('language', 'und')
49 height
= params
.get('height', 0)
50 width
= params
.get('width', 0)
51 is_audio
= width
== 0 and height
== 0
52 creation_time
= modification_time
= int(time
.time())
54 ftyp_payload
= b
'isml' # major brand
55 ftyp_payload
+= u32
.pack(1) # minor version
56 ftyp_payload
+= b
'piff' + b
'iso2' # compatible brands
57 stream
.write(box(b
'ftyp', ftyp_payload
)) # File Type Box
59 mvhd_payload
= u64
.pack(creation_time
)
60 mvhd_payload
+= u64
.pack(modification_time
)
61 mvhd_payload
+= u32
.pack(timescale
)
62 mvhd_payload
+= u64
.pack(duration
)
63 mvhd_payload
+= s1616
.pack(1) # rate
64 mvhd_payload
+= s88
.pack(1) # volume
65 mvhd_payload
+= u16
.pack(0) # reserved
66 mvhd_payload
+= u32
.pack(0) * 2 # reserved
67 mvhd_payload
+= unity_matrix
68 mvhd_payload
+= u32
.pack(0) * 6 # pre defined
69 mvhd_payload
+= u32
.pack(0xffffffff) # next track id
70 moov_payload
= full_box(b
'mvhd', 1, 0, mvhd_payload
) # Movie Header Box
72 tkhd_payload
= u64
.pack(creation_time
)
73 tkhd_payload
+= u64
.pack(modification_time
)
74 tkhd_payload
+= u32
.pack(track_id
) # track id
75 tkhd_payload
+= u32
.pack(0) # reserved
76 tkhd_payload
+= u64
.pack(duration
)
77 tkhd_payload
+= u32
.pack(0) * 2 # reserved
78 tkhd_payload
+= s16
.pack(0) # layer
79 tkhd_payload
+= s16
.pack(0) # alternate group
80 tkhd_payload
+= s88
.pack(1 if is_audio
else 0) # volume
81 tkhd_payload
+= u16
.pack(0) # reserved
82 tkhd_payload
+= unity_matrix
83 tkhd_payload
+= u1616
.pack(width
)
84 tkhd_payload
+= u1616
.pack(height
)
85 trak_payload
= full_box(b
'tkhd', 1, TRACK_ENABLED | TRACK_IN_MOVIE | TRACK_IN_PREVIEW
, tkhd_payload
) # Track Header Box
87 mdhd_payload
= u64
.pack(creation_time
)
88 mdhd_payload
+= u64
.pack(modification_time
)
89 mdhd_payload
+= u32
.pack(timescale
)
90 mdhd_payload
+= u64
.pack(duration
)
91 mdhd_payload
+= u16
.pack(((ord(language
[0]) - 0x60) << 10) |
((ord(language
[1]) - 0x60) << 5) |
(ord(language
[2]) - 0x60))
92 mdhd_payload
+= u16
.pack(0) # pre defined
93 mdia_payload
= full_box(b
'mdhd', 1, 0, mdhd_payload
) # Media Header Box
95 hdlr_payload
= u32
.pack(0) # pre defined
96 hdlr_payload
+= b
'soun' if is_audio
else b
'vide' # handler type
97 hdlr_payload
+= u32
.pack(0) * 3 # reserved
98 hdlr_payload
+= (b
'Sound' if is_audio
else b
'Video') + b
'Handler\0' # name
99 mdia_payload
+= full_box(b
'hdlr', 0, 0, hdlr_payload
) # Handler Reference Box
102 smhd_payload
= s88
.pack(0) # balance
103 smhd_payload
+= u16
.pack(0) # reserved
104 media_header_box
= full_box(b
'smhd', 0, 0, smhd_payload
) # Sound Media Header
106 vmhd_payload
= u16
.pack(0) # graphics mode
107 vmhd_payload
+= u16
.pack(0) * 3 # opcolor
108 media_header_box
= full_box(b
'vmhd', 0, 1, vmhd_payload
) # Video Media Header
109 minf_payload
= media_header_box
111 dref_payload
= u32
.pack(1) # entry count
112 dref_payload
+= full_box(b
'url ', 0, SELF_CONTAINED
, b
'') # Data Entry URL Box
113 dinf_payload
= full_box(b
'dref', 0, 0, dref_payload
) # Data Reference Box
114 minf_payload
+= box(b
'dinf', dinf_payload
) # Data Information Box
116 stsd_payload
= u32
.pack(1) # entry count
118 sample_entry_payload
= u8
.pack(0) * 6 # reserved
119 sample_entry_payload
+= u16
.pack(1) # data reference index
121 sample_entry_payload
+= u32
.pack(0) * 2 # reserved
122 sample_entry_payload
+= u16
.pack(params
.get('channels', 2))
123 sample_entry_payload
+= u16
.pack(params
.get('bits_per_sample', 16))
124 sample_entry_payload
+= u16
.pack(0) # pre defined
125 sample_entry_payload
+= u16
.pack(0) # reserved
126 sample_entry_payload
+= u1616
.pack(params
['sampling_rate'])
129 sample_entry_box
= box(b
'mp4a', sample_entry_payload
)
131 sample_entry_payload
+= u16
.pack(0) # pre defined
132 sample_entry_payload
+= u16
.pack(0) # reserved
133 sample_entry_payload
+= u32
.pack(0) * 3 # pre defined
134 sample_entry_payload
+= u16
.pack(width
)
135 sample_entry_payload
+= u16
.pack(height
)
136 sample_entry_payload
+= u1616
.pack(0x48) # horiz resolution 72 dpi
137 sample_entry_payload
+= u1616
.pack(0x48) # vert resolution 72 dpi
138 sample_entry_payload
+= u32
.pack(0) # reserved
139 sample_entry_payload
+= u16
.pack(1) # frame count
140 sample_entry_payload
+= u8
.pack(0) * 32 # compressor name
141 sample_entry_payload
+= u16
.pack(0x18) # depth
142 sample_entry_payload
+= s16
.pack(-1) # pre defined
144 codec_private_data
= binascii
.unhexlify(params
['codec_private_data'].encode('utf-8'))
145 if fourcc
in ('H264', 'AVC1'):
146 sps
, pps
= codec_private_data
.split(u32
.pack(1))[1:]
147 avcc_payload
= u8
.pack(1) # configuration version
148 avcc_payload
+= sps
[1:4] # avc profile indication + profile compatibility + avc level indication
149 avcc_payload
+= u8
.pack(0xfc |
(params
.get('nal_unit_length_field', 4) - 1)) # complete representation (1) + reserved (11111) + length size minus one
150 avcc_payload
+= u8
.pack(1) # reserved (0) + number of sps (0000001)
151 avcc_payload
+= u16
.pack(len(sps
))
153 avcc_payload
+= u8
.pack(1) # number of pps
154 avcc_payload
+= u16
.pack(len(pps
))
156 sample_entry_payload
+= box(b
'avcC', avcc_payload
) # AVC Decoder Configuration Record
157 sample_entry_box
= box(b
'avc1', sample_entry_payload
) # AVC Simple Entry
158 stsd_payload
+= sample_entry_box
160 stbl_payload
= full_box(b
'stsd', 0, 0, stsd_payload
) # Sample Description Box
162 stts_payload
= u32
.pack(0) # entry count
163 stbl_payload
+= full_box(b
'stts', 0, 0, stts_payload
) # Decoding Time to Sample Box
165 stsc_payload
= u32
.pack(0) # entry count
166 stbl_payload
+= full_box(b
'stsc', 0, 0, stsc_payload
) # Sample To Chunk Box
168 stco_payload
= u32
.pack(0) # entry count
169 stbl_payload
+= full_box(b
'stco', 0, 0, stco_payload
) # Chunk Offset Box
171 minf_payload
+= box(b
'stbl', stbl_payload
) # Sample Table Box
173 mdia_payload
+= box(b
'minf', minf_payload
) # Media Information Box
175 trak_payload
+= box(b
'mdia', mdia_payload
) # Media Box
177 moov_payload
+= box(b
'trak', trak_payload
) # Track Box
179 mehd_payload
= u64
.pack(duration
)
180 mvex_payload
= full_box(b
'mehd', 1, 0, mehd_payload
) # Movie Extends Header Box
182 trex_payload
= u32
.pack(track_id
) # track id
183 trex_payload
+= u32
.pack(1) # default sample description index
184 trex_payload
+= u32
.pack(0) # default sample duration
185 trex_payload
+= u32
.pack(0) # default sample size
186 trex_payload
+= u32
.pack(0) # default sample flags
187 mvex_payload
+= full_box(b
'trex', 0, 0, trex_payload
) # Track Extends Box
189 moov_payload
+= box(b
'mvex', mvex_payload
) # Movie Extends Box
190 stream
.write(box(b
'moov', moov_payload
)) # Movie Box
193 def extract_box_data(data
, box_sequence
):
194 data_reader
= io
.BytesIO(data
)
196 box_size
= u32
.unpack(data_reader
.read(4))[0]
197 box_type
= data_reader
.read(4)
198 if box_type
== box_sequence
[0]:
199 box_data
= data_reader
.read(box_size
- 8)
200 if len(box_sequence
) == 1:
202 return extract_box_data(box_data
, box_sequence
[1:])
203 data_reader
.seek(box_size
- 8, 1)
206 class IsmFD(FragmentFD
):
208 Download segments in a ISM manifest
213 def real_download(self
, filename
, info_dict
):
214 segments
= info_dict
['fragments'][:1] if self
.params
.get(
215 'test', False) else info_dict
['fragments']
218 'filename': filename
,
219 'total_frags': len(segments
),
222 self
._prepare
_and
_start
_frag
_download
(ctx
)
224 fragment_retries
= self
.params
.get('fragment_retries', 0)
225 skip_unavailable_fragments
= self
.params
.get('skip_unavailable_fragments', True)
227 track_written
= False
229 for i
, segment
in enumerate(segments
):
231 if frag_index
<= ctx
['fragment_index']:
234 while count
<= fragment_retries
:
236 success
, frag_content
= self
._download
_fragment
(ctx
, segment
['url'], info_dict
)
239 if not track_written
:
240 tfhd_data
= extract_box_data(frag_content
, [b
'moof', b
'traf', b
'tfhd'])
241 info_dict
['_download_params']['track_id'] = u32
.unpack(tfhd_data
[4:8])[0]
242 write_piff_header(ctx
['dest_stream'], info_dict
['_download_params'])
244 self
._append
_fragment
(ctx
, frag_content
)
246 except compat_urllib_error
.HTTPError
as err
:
248 if count
<= fragment_retries
:
249 self
.report_retry_fragment(err
, frag_index
, count
, fragment_retries
)
250 if count
> fragment_retries
:
251 if skip_unavailable_fragments
:
252 self
.report_skip_fragment(frag_index
)
254 self
.report_error('giving up after %s fragment retries' % fragment_retries
)
257 self
._finish
_frag
_download
(ctx
)