|
|
|
@ -23,26 +23,38 @@ from ..utils import (
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DataTruncatedError(Exception):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FlvReader(io.BytesIO):
|
|
|
|
|
"""
|
|
|
|
|
Reader for Flv files
|
|
|
|
|
The file format is documented in https://www.adobe.com/devnet/f4v.html
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def read_bytes(self, n):
|
|
|
|
|
data = self.read(n)
|
|
|
|
|
if len(data) < n:
|
|
|
|
|
raise DataTruncatedError(
|
|
|
|
|
'FlvReader error: need %d bytes while only %d bytes got' % (
|
|
|
|
|
n, len(data)))
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
# Utility functions for reading numbers and strings
|
|
|
|
|
def read_unsigned_long_long(self):
|
|
|
|
|
return compat_struct_unpack('!Q', self.read(8))[0]
|
|
|
|
|
return compat_struct_unpack('!Q', self.read_bytes(8))[0]
|
|
|
|
|
|
|
|
|
|
def read_unsigned_int(self):
|
|
|
|
|
return compat_struct_unpack('!I', self.read(4))[0]
|
|
|
|
|
return compat_struct_unpack('!I', self.read_bytes(4))[0]
|
|
|
|
|
|
|
|
|
|
def read_unsigned_char(self):
|
|
|
|
|
return compat_struct_unpack('!B', self.read(1))[0]
|
|
|
|
|
return compat_struct_unpack('!B', self.read_bytes(1))[0]
|
|
|
|
|
|
|
|
|
|
def read_string(self):
|
|
|
|
|
res = b''
|
|
|
|
|
while True:
|
|
|
|
|
char = self.read(1)
|
|
|
|
|
char = self.read_bytes(1)
|
|
|
|
|
if char == b'\x00':
|
|
|
|
|
break
|
|
|
|
|
res += char
|
|
|
|
@ -53,18 +65,18 @@ class FlvReader(io.BytesIO):
|
|
|
|
|
Read a box and return the info as a tuple: (box_size, box_type, box_data)
|
|
|
|
|
"""
|
|
|
|
|
real_size = size = self.read_unsigned_int()
|
|
|
|
|
box_type = self.read(4)
|
|
|
|
|
box_type = self.read_bytes(4)
|
|
|
|
|
header_end = 8
|
|
|
|
|
if size == 1:
|
|
|
|
|
real_size = self.read_unsigned_long_long()
|
|
|
|
|
header_end = 16
|
|
|
|
|
return real_size, box_type, self.read(real_size - header_end)
|
|
|
|
|
return real_size, box_type, self.read_bytes(real_size - header_end)
|
|
|
|
|
|
|
|
|
|
def read_asrt(self):
|
|
|
|
|
# version
|
|
|
|
|
self.read_unsigned_char()
|
|
|
|
|
# flags
|
|
|
|
|
self.read(3)
|
|
|
|
|
self.read_bytes(3)
|
|
|
|
|
quality_entry_count = self.read_unsigned_char()
|
|
|
|
|
# QualityEntryCount
|
|
|
|
|
for i in range(quality_entry_count):
|
|
|
|
@ -85,7 +97,7 @@ class FlvReader(io.BytesIO):
|
|
|
|
|
# version
|
|
|
|
|
self.read_unsigned_char()
|
|
|
|
|
# flags
|
|
|
|
|
self.read(3)
|
|
|
|
|
self.read_bytes(3)
|
|
|
|
|
# time scale
|
|
|
|
|
self.read_unsigned_int()
|
|
|
|
|
|
|
|
|
@ -119,7 +131,7 @@ class FlvReader(io.BytesIO):
|
|
|
|
|
# version
|
|
|
|
|
self.read_unsigned_char()
|
|
|
|
|
# flags
|
|
|
|
|
self.read(3)
|
|
|
|
|
self.read_bytes(3)
|
|
|
|
|
|
|
|
|
|
self.read_unsigned_int() # BootstrapinfoVersion
|
|
|
|
|
# Profile,Live,Update,Reserved
|
|
|
|
@ -374,7 +386,17 @@ class F4mFD(FragmentFD):
|
|
|
|
|
down.close()
|
|
|
|
|
reader = FlvReader(down_data)
|
|
|
|
|
while True:
|
|
|
|
|
try:
|
|
|
|
|
_, box_type, box_data = reader.read_box_info()
|
|
|
|
|
except DataTruncatedError:
|
|
|
|
|
if test:
|
|
|
|
|
# In tests, segments may be truncated, and thus
|
|
|
|
|
# FlvReader may not be able to parse the whole
|
|
|
|
|
# chunk. If so, write the segment as is
|
|
|
|
|
# See https://github.com/rg3/youtube-dl/issues/9214
|
|
|
|
|
dest_stream.write(down_data)
|
|
|
|
|
break
|
|
|
|
|
raise
|
|
|
|
|
if box_type == b'mdat':
|
|
|
|
|
dest_stream.write(box_data)
|
|
|
|
|
break
|
|
|
|
|