Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

H.264 levels higher than 4.1 aren't supported by TiVo hardware; detect and transcode if needed #16

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions plugins/video/transcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,9 @@ def tivo_compatible_video(vInfo, tsn, mime=''):
if not (is4k or codec == 'h264'):
message = (False, 'vCodec %s not compatible' % codec)

# According to https://code.google.com/p/streambaby/wiki/video_compatibility, level 4.1 is the maximum supported on Series 4.
if int(vInfo['vH264Level']) > 41:
message = (False, 'h264 level %s too high' % vInfo['vH264Level'])
break

if mime == 'video/bif':
Expand Down Expand Up @@ -738,6 +741,41 @@ def tivo_compatible(inFile, tsn='', mime=''):
message[1], inFile))
return message

def detect_h264_level(fname, vstream=0):
ffprobe_path = config.get_bin('ffprobe')
if not ffprobe_path:
return 0

cmd = [ffprobe_path, '-of', 'flat', '-show_streams', '-i', fname]
# Windows and other OS buffer 4096 and ffprobe can output more than that.
out_tmp = tempfile.TemporaryFile()
ffprobe = subprocess.Popen(cmd, stdout=out_tmp, stderr=subprocess.PIPE,
stdin=subprocess.PIPE)

# wait configured # of seconds: if ffprobe is not back give up
limit = config.getFFmpegWait()
if limit:
for i in xrange(limit * 20):
time.sleep(.05)
if not ffprobe.poll() == None:
break

if ffprobe.poll() == None:
kill(ffprobe)
return 0
else:
ffprobe.wait()

out_tmp.seek(0)
output = out_tmp.read()
out_tmp.close()
debug('ffprobe output=%s' % repr(output))

match = re.search(r'streams\.stream\.\d+\.level=(\d+)', output)
if match:
return int(match.group(1))
return 0

def video_info(inFile, cache=True):
vInfo = dict()
fname = unicode(inFile, 'utf-8')
Expand Down Expand Up @@ -881,6 +919,11 @@ def video_info(inFile, cache=True):
else:
vInfo['millisecs'] = 0

# get h264 level
vInfo['vH264Level'] = 0 # Assume it's OK unless proven otherwise
if vInfo['vCodec'] == 'h264':
vInfo['vH264Level'] = detect_h264_level(fname)

# get bitrate of source for tivo compatibility test.
rezre = re.compile(r'.*bitrate: (.+) (?:kb/s).*')
x = rezre.search(output)
Expand Down
18 changes: 16 additions & 2 deletions plugins/video/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,22 @@ def __total_items(self, full_path):
def __est_size(self, full_path, tsn='', mime=''):
# Size is estimated by taking audio and video bit rate adding 2%

if transcode.tivo_compatible(full_path, tsn, mime)[0]:
return os.path.getsize(unicode(full_path, 'utf-8'))
vInfo = transcode.video_info(full_path)
if not mime:
compat = False
mimetypes = ['video/x-tivo-mpeg']
if self.use_ts(tsn, full_path):
mimetypes = ['video/x-tivo-mpeg', 'video/x-tivo-mpeg-ts']

for mimet in mimetypes:
compat = transcode.tivo_compatible_video(vInfo, tsn, mimet)[0]
if compat:
break
else:
compat = transcode.tivo_compatible_video(vInfo, tsn, mime)[0]

if compat:
return int(os.path.getsize(unicode(full_path, 'utf-8'))*1.1)
else:
# Must be re-encoded
audioBPS = config.getMaxAudioBR(tsn) * 1000
Expand Down