HMS

Home Media Server for Roku Players
git clone https://www.brianlane.com/git/HMS
Log | Files | Refs | README | LICENSE

commit 573933e03ed05858d7196472f1b0a1c5732d3aeb
parent 9f46aaa458398ddf1953a4a7be38609c0deb7e06
Author: Brian C. Lane <bcl@brianlane.com>
Date:   Sat, 19 Feb 2011 08:16:01 -0800

Pulled makebif.py from v1.0 tag

Diffstat:
Ascripts/makebif.py | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 172 insertions(+), 0 deletions(-)

diff --git a/scripts/makebif.py b/scripts/makebif.py @@ -0,0 +1,172 @@ +#!/bin/env python +""" +Create .bif files for Roku video streaming +Copyright 2009 by Brian C. Lane <bcl@brianlane.com> +All Rights Reserved + + +makebif.py --help for arguments + +Requires ffmpeg to be in the path + +After creating the .bif files run the staticvideo.py script to regenerate XML with the +URLs for the bif files included. + +NOTE: The jpg image sizes are set to the values posted by bbefilms in the Roku + development forums. They may or may not be correct for your video aspect ratio. + They don't look right for me when I set the video height to 480 +""" +import os +import sys +import tempfile +from subprocess import Popen, PIPE +import struct +import array +import shutil +from optparse import OptionParser + +# for mode 0, 1, 2, 3 +videoSizes = [(240,180), (320,240), (240,136), (320,180)] + +# Extension to add to the file for mode 0, 1, 2, 3 +modeExtension = ['SD', 'HD', 'SD', 'HD'] + + + +def getMP4Info(filename): + """ + Get mp4 info about the video + """ + details = { 'type':"", 'length':0, 'bitrate':1500, 'format':"", 'size':""} + cmd = "mp4info %s" % (filename) + p = Popen( cmd, shell=True, stdout=PIPE, stderr=PIPE, stdin=PIPE ) + (stdout, stderr) = p.communicate() + # Parse the results + for line in stdout.split('\n'): + fields = line.split(None, 2) + try: + if fields[1] == 'video': + # parse the video info + # MPEG-4 Simple @ L3, 5706.117 secs, 897 kbps, 712x480 @ 23.9760 24 fps + videoFields = fields[2].split(',') + details['type'] = videoFields[0] + details['length'] = float(videoFields[1].split()[0]) + details['bitrate'] = float(videoFields[2].split()[0]) + details['format'] = videoFields[3] + details['size'] = videoFields[3].split('@')[0].strip() + except: + pass + + return details + + +def extractImages( videoFile, directory, interval, mode=0 ): + """ + Extract images from the video at 'interval' seconds + + @param mode 0=SD 4:3 1=HD 4:3 2=SD 16:9 3=HD 16:9 + @param directory Directory to write images into + @param interval interval to extract images at, in seconds + + """ + size = "x".join([str(i) for i in videoSizes[mode]]) + cmd = "ffmpeg -i %s -ss 7 -r %0.2f -s %s %s/%%08d.jpg" % (videoFile, interval/100.0, size, directory) + print cmd + p = Popen( cmd, shell=True, stdout=PIPE, stdin=PIPE) + (stdout, stderr) = p.communicate() + print stderr + + +def makeBIF( filename, directory, interval ): + """ + Build a .bif file for the Roku Player Tricks Mode + + @param filename name of .bif file to create + @param directory Directory of image files 00000001.jpg + @param interval Time, in seconds, between the images + """ + magic = [0x89,0x42,0x49,0x46,0x0d,0x0a,0x1a,0x0a] + version = 0 + + files = os.listdir("%s" % (directory)) + images = [] + for image in files: + if image[-4:] == '.jpg': + images.append(image) + images.sort() + images = images[1:] + + f = open(filename, "wb") + array.array('B', magic).tofile(f) + f.write(struct.pack("<I1", version)) + f.write(struct.pack("<I1", len(images))) + f.write(struct.pack("<I1", 1000 * interval)) + array.array('B', [0x00 for x in xrange(20,64)]).tofile(f) + + bifTableSize = 8 + (8 * len(images)) + imageIndex = 64 + bifTableSize + timestamp = 0 + + # Get the length of each image + for image in images: + statinfo = os.stat("%s/%s" % (directory, image)) + f.write(struct.pack("<I1", timestamp)) + f.write(struct.pack("<I1", imageIndex)) + + timestamp += 1 + imageIndex += statinfo.st_size + + f.write(struct.pack("<I1", 0xffffffff)) + f.write(struct.pack("<I1", imageIndex)) + + # Now copy the images + for image in images: + data = open("%s/%s" % (directory, image), "rb").read() + f.write(data) + + f.close() + + +def main(): + """ + Extract jpg images from the video and create a .bif file + """ + parser = OptionParser() + parser.add_option( "-m", "--mode", dest="mode", type='int', default=0, + help="(0=SD) 4:3 1=HD 4:3 2=SD 16:9 3=HD 16:9") + parser.add_option( "-i", "--interval", dest="interval", type='int', default=10, + help="Interval between images in seconds (default is 10)") + + (options, args) = parser.parse_args() + + # Get the video file to operate on + videoFile = args[0] + print "Creating .BIF file for %s" % (videoFile) + + # This may be useful for determining the video format + # Get info about the video file + videoInfo = getMP4Info(videoFile) + if videoInfo["size"]: + size = videoInfo["size"].split("x") + aspectRatio = float(size[0]) / float(size[1]) + width, height = videoSizes[options.mode] + height = int(width / aspectRatio) + videoSizes[options.mode] = (width, height) + + tmpDirectory = tempfile.mkdtemp() + + # Extract jpg images from the video file + extractImages( videoFile, tmpDirectory, options.interval, options.mode ) + + bifFile = "%s-%s.bif" % (os.path.basename(videoFile).rsplit('.',1)[0], modeExtension[options.mode]) + + # Create the BIF file + makeBIF( bifFile, tmpDirectory, options.interval ) + + # Clean up the temporary directory + shutil.rmtree(tmpDirectory) + + +if __name__ == '__main__': + main() +