%PDF- %PDF-
| Direktori : /home/waritko/build/Bento4/Source/Python/utils/ |
| Current File : //home/waritko/build/Bento4/Source/Python/utils/mp4-hls.py |
#!/usr/bin/env python
__author__ = 'Gilles Boccon-Gibod (bok@bok.net)'
__copyright__ = 'Copyright 2011-2015 Axiomatic Systems, LLC.'
###
# NOTE: this script needs Bento4 command line binaries to run
# You must place the 'mp4info' 'mp4dump', and 'mp42hls' binaries
# in a directory named 'bin/<platform>' at the same level as where
# this script is.
# <platform> depends on the platform you're running on:
# Mac OSX --> platform = macosx
# Linux x86 --> platform = linux-x86
# Windows --> platform = win32
from optparse import OptionParser
import shutil
import xml.etree.ElementTree as xml
from xml.dom.minidom import parseString
import tempfile
import fractions
import re
import platform
import sys
from mp4utils import *
from subtitles import *
# setup main options
VERSION = "1.1.0"
SDK_REVISION = '623'
SCRIPT_PATH = path.abspath(path.dirname(__file__))
sys.path += [SCRIPT_PATH]
#############################################
def CreateSubtitlesPlaylist(playlist_filename, webvtt_filename, duration):
playlist = open(playlist_filename, 'wb+')
playlist.write('#EXTM3U\r\n')
playlist.write('#EXT-X-TARGETDURATION:%d\r\n' % (duration))
playlist.write('#EXT-X-VERSION:3\r\n')
playlist.write('#EXT-X-MEDIA-SEQUENCE:0\r\n')
playlist.write('#EXT-X-PLAYLIST-TYPE:VOD\r\n')
playlist.write('#EXTINF:%d,\r\n' % (duration))
playlist.write(webvtt_filename+'\r\n')
playlist.write('#EXT-X-ENDLIST\r\n')
#############################################
def ComputeCodecName(codec_family):
name = codec_family
if codec_family == 'mp4a':
name = 'aac'
elif codec_family == 'ac-3':
name = 'ac3'
elif codec_family == 'ec-3':
name = 'ec3'
return name
#############################################
def SplitArgs(args):
try:
pairs = args.split('#')
fields = {}
for pair in pairs:
name, value = pair.split(':', 1)
fields[name] = value
return fields
except:
raise Exception('invalid syntax for argument')
#############################################
def ComputeWidevineKeyLine(params):
json_param = '{ "provider": "%(provider)s", "content_id": "%(content_id)s", "key_ids": ["%(kid)s"] }' % params
key_line = 'URI="data:text/plain;base64,'+json_param.encode('base64').replace('\n','')+'",KEYFORMAT="com.widevine",KEYFORMATVERSIONS="1"'
return key_line
#############################################
def ComputeFairplayKeyLine(params):
# start with a '!' to specify we want to skip the IV (since it is not needed on the key line for Fairplay)
return '!URI="'+params['uri']+'",KEYFORMAT="com.apple.streamingkeydelivery",KEYFORMATVERSIONS="1"'
#############################################
def AnalyzeSources(options, media_sources):
# parse the media files
mp4_files = {}
for media_source in media_sources:
if media_source.format != 'mp4': continue
media_file = media_source.filename
# check if we have already parsed this file
if media_file in mp4_files:
media_source.mp4_file = mp4_files[media_file]
continue
# parse the file
if not os.path.exists(media_file):
PrintErrorAndExit('ERROR: media file ' + media_file + ' does not exist')
# get the file info
print 'Parsing media file', media_file
mp4_file = Mp4File(Options, media_source)
media_source.mp4_file = mp4_file
# remember we have parsed this file
mp4_files[media_file] = mp4_file
# analyze the media sources
for media_source in media_sources:
track_id = media_source.spec['track']
track_type = media_source.spec['type']
track_language = media_source.spec['language']
tracks = []
if media_source.format != 'mp4':
if track_id or track_type:
PrintErrorAndExit('ERROR: track ID and track type selections only apply to MP4 media sources')
continue
if track_id and track_type:
PrintErrorAndExit('ERROR: track ID and track type selections are mutually exclusive')
if track_id:
tracks = [media_source.mp4_file.find_track_by_id(track_id)]
if not tracks:
PrintErrorAndExit('ERROR: track id not found for media file '+media_source.name)
if track_type:
tracks = media_source.mp4_file.find_tracks_by_type(track_type)
if not tracks:
PrintErrorAndExit('ERROR: no ' + track_type + ' found for media file '+media_source.name)
if not tracks:
for track in media_source.mp4_file.tracks.values():
language = LanguageCodeMap.get(track.language, track.language)
if track_language and track_language != language and track_language != track.language:
continue
tracks.append(track)
# remember if this media source has a video or audio track
for track in tracks:
if track.type == 'video':
media_source.has_video = True
if track.type == 'audio':
media_source.has_audio = True
media_source.tracks = tracks
#############################################
def SelectAudioTracks(options, media_sources):
# select tracks grouped by codec
audio_tracks = {}
for media_source in media_sources:
# pre-process the track metadata
for track in media_source.tracks:
# track group
track.group_id = ComputeCodecName(track.codec_family)
# track language
remap_language = media_source.spec.get('+language')
if remap_language:
track.language = remap_language
language_name = LanguageNames.get(track.language, track.language)
track.language_name = media_source.spec.get('+language_name', language_name).decode('utf-8')
# process audio tracks
for track in [t for t in media_source.tracks if t.type == 'audio']:
group_id = track.group_id
group = audio_tracks.get(group_id, [])
audio_tracks[group_id] = group
if len([x for x in group if x.language == track.language]):
continue # only accept one track for each language per group
group.append(track)
return audio_tracks
#############################################
def ProcessSource(options, media_info, out_dir):
if options.verbose:
print 'Processing', media_info['source'].filename
file_extension = media_info.get('file_extension', 'ts')
kwargs = {
'index_filename': path.join(out_dir, options.media_playlist_name),
'segment_filename_template': path.join(out_dir, 'segment-%d.'+file_extension),
'segment_url_template': 'segment-%d.'+file_extension,
'show_info': True
}
if options.base_url != "":
kwargs["segment_url_template"] = options.base_url+media_info["dir"]+'/'+'segment-%d.'+file_extension
if options.hls_version != 3:
kwargs['hls_version'] = str(options.hls_version)
if options.hls_version >= 4:
kwargs['iframe_index_filename'] = path.join(out_dir, options.iframe_playlist_name)
if options.output_single_file:
kwargs['segment_filename_template'] = path.join(out_dir, 'media.'+file_extension)
kwargs['segment_url_template'] = 'media.'+file_extension
kwargs['output_single_file'] = True
if 'audio_format' in media_info and media_info.get('audio_track_id') != 0:
kwargs['audio_format'] = media_info['audio_format']
for option in ['encryption_mode', 'encryption_key', 'encryption_iv_mode', 'encryption_key_uri', 'encryption_key_format', 'encryption_key_format_versions']:
if getattr(options, option):
kwargs[option] = getattr(options, option)
key_lines = []
# Fairplay
if options.fairplay:
key_lines.append(ComputeFairplayKeyLine(options.fairplay))
# Widevine
if options.widevine:
key_lines.append(ComputeWidevineKeyLine(options.widevine))
if len(key_lines):
kwargs['encryption_key_line'] = key_lines
# deal with track IDs
if 'audio_track_id' in media_info:
kwargs['audio_track_id'] = str(media_info['audio_track_id'])
if 'video_track_id' in media_info:
kwargs['video_track_id'] = str(media_info['video_track_id'])
# other options
if options.segment_duration:
kwargs['segment_duration'] = options.segment_duration
# convert to HLS/TS
json_info = Mp42Hls(options,
media_info['source'].filename,
**kwargs)
media_info['info'] = json.loads(json_info, strict=False)
if options.verbose:
print json_info
# output the encryption key if needed
if options.output_encryption_key:
open(path.join(out_dir, 'key.bin'), 'wb+').write(options.encryption_key.decode('hex')[:16])
#############################################
def OutputHls(options, media_sources):
mp4_sources = [media_source for media_source in media_sources if media_source.format == 'mp4']
# analyze the media sources
AnalyzeSources(options, media_sources)
# select audio tracks
audio_media = []
audio_tracks = SelectAudioTracks(options, [media_source for media_source in mp4_sources if not media_source.spec.get('+audio_fallback')])
# check if this is an audio-only presentation
audio_only = True
for media_source in mp4_sources:
if media_source.has_video:
audio_only = False
break
# check if the video has muxed audio
video_has_muxed_audio = False
for media_source in mp4_sources:
if media_source.has_video and media_source.has_audio:
video_has_muxed_audio = True
break
# audio-only presentations don't need alternate audio tracks
if audio_only:
audio_tracks = {}
# we only need alternate audio tracks if there are more than one or if the audio and video are not muxed
if video_has_muxed_audio and not audio_only and len(audio_tracks) == 1 and len(audio_tracks.values()[0]) == 1:
audio_tracks = {}
# process main media sources
total_duration = 0
main_media = []
for media_source in mp4_sources:
if not audio_only and not media_source.spec.get('+audio_fallback') and not media_source.has_video:
continue
media_index = 1+len(main_media)
media_info = {
'source': media_source,
'dir': 'media-'+str(media_index)
}
if audio_only:
media_info['video_track_id'] = 0
if options.audio_format == 'packed':
source_audio_tracks = media_source.mp4_file.find_tracks_by_type('audio')
if len(source_audio_tracks):
media_info['audio_format'] = options.audio_format
if options.audio_format == 'packed':
media_info['file_extension'] = ComputeCodecName(source_audio_tracks[0].codec_family)
# no audio if there's a type filter for video
if media_source.spec.get('type') == 'video':
media_info['audio_track_id'] = 0
# deal with audio-fallback streams
if media_source.spec.get('+audio_fallback') == 'yes':
media_info['video_track_id'] = 0
# process the source
out_dir = path.join(options.output_dir, media_info['dir'])
MakeNewDir(out_dir)
ProcessSource(options, media_info, out_dir)
# update the duration
duration_s = int(media_info['info']['stats']['duration'])
if duration_s > total_duration:
total_duration = duration_s
main_media.append(media_info)
# process audio tracks
if len(audio_tracks):
MakeNewDir(path.join(options.output_dir, 'audio'))
for group_id in audio_tracks:
group = audio_tracks[group_id]
MakeNewDir(path.join(options.output_dir, 'audio', group_id))
for audio_track in group:
audio_track.media_info = {
'source': audio_track.parent.media_source,
'audio_format': options.audio_format,
'dir': 'audio/'+group_id+'/'+audio_track.language,
'language': audio_track.language,
'language_name': audio_track.language_name,
'audio_track_id': audio_track.id,
'video_track_id': 0
}
if options.audio_format == 'packed':
audio_track.media_info['file_extension'] = ComputeCodecName(audio_track.codec_family)
# process the source
out_dir = path.join(options.output_dir, 'audio', group_id, audio_track.language)
MakeNewDir(out_dir)
ProcessSource(options, audio_track.media_info, out_dir)
# start the master playlist
master_playlist = open(path.join(options.output_dir, options.master_playlist_name), "wb+")
master_playlist.write("#EXTM3U\r\n")
master_playlist.write('# Created with Bento4 mp4-hls.py version '+VERSION+'r'+SDK_REVISION+'\r\n')
if options.hls_version >= 4:
master_playlist.write('\r\n')
master_playlist.write('#EXT-X-VERSION:'+str(options.hls_version)+'\r\n')
# optional session key
if options.signal_session_key:
ext_x_session_key_line = '#EXT-X-SESSION-KEY:METHOD='+options.encryption_mode+',URI="'+options.encryption_key_uri+'"'
if options.encryption_key_format:
ext_x_session_key_line += ',KEYFORMAT="'+options.encryption_key_format+'"'
if options.encryption_key_format_versions:
ext_x_session_key_line += ',KEYFORMATVERSIONS="'+options.encryption_key_format_versions+'"'
master_playlist.write(ext_x_session_key_line+'\r\n')
# process subtitles sources
subtitles_files = [SubtitlesFile(options, media_source) for media_source in media_sources if media_source.format in ['ttml', 'webvtt']]
if len(subtitles_files):
master_playlist.write('\r\n')
master_playlist.write('# Subtitles\r\n')
MakeNewDir(path.join(options.output_dir, 'subtitles'))
for subtitles_file in subtitles_files:
out_dir = path.join(options.output_dir, 'subtitles', subtitles_file.language)
MakeNewDir(out_dir)
media_filename = path.join(out_dir, subtitles_file.media_name)
shutil.copyfile(subtitles_file.media_source.filename, media_filename)
relative_url = 'subtitles/'+subtitles_file.language+'/subtitles.m3u8'
playlist_filename = path.join(out_dir, 'subtitles.m3u8')
CreateSubtitlesPlaylist(playlist_filename, subtitles_file.media_name, total_duration)
master_playlist.write('#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subtitles",NAME="%s",LANGUAGE="%s",URI="%s"\r\n' % (subtitles_file.language_name, subtitles_file.language, relative_url))
# process audio sources
audio_groups = []
if len(audio_tracks):
master_playlist.write('\r\n')
master_playlist.write('# Audio\r\n')
for group_id in audio_tracks:
group = audio_tracks[group_id]
group_name = 'audio_'+group_id
group_codec = group[0].codec
default = True
group_avg_segment_bitrate = 0
group_max_segment_bitrate = 0
for audio_track in group:
avg_segment_bitrate = int(audio_track.media_info['info']['stats']['avg_segment_bitrate'])
max_segment_bitrate = int(audio_track.media_info['info']['stats']['max_segment_bitrate'])
if avg_segment_bitrate > group_avg_segment_bitrate:
group_avg_segment_bitrate = avg_segment_bitrate
if max_segment_bitrate > group_max_segment_bitrate:
group_max_segment_bitrate = max_segment_bitrate
extra_info = 'AUTOSELECT=YES,'
if default:
extra_info += 'DEFAULT=YES,'
default = False
master_playlist.write(('#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="%s",NAME="%s",LANGUAGE="%s",%sURI="%s"\r\n' % (
group_name,
audio_track.media_info['language_name'],
audio_track.media_info['language'],
extra_info,
options.base_url+audio_track.media_info['dir']+'/'+options.media_playlist_name)).encode('utf-8'))
audio_groups.append({
'name': group_name,
'codec': group_codec,
'avg_segment_bitrate': group_avg_segment_bitrate,
'max_segment_bitrate': group_max_segment_bitrate
})
if options.debug:
print 'Audio Groups:'
print audio_groups
else:
audio_groups = [{
'name': None,
'codec': None,
'avg_segment_bitrate': 0,
'max_segment_bitrate': 0
}]
# media playlists
master_playlist.write('\r\n')
master_playlist.write('# Media Playlists\r\n')
for media in main_media:
media_info = media['info']
for group_info in audio_groups:
group_name = group_info['name']
group_codec = group_info['codec']
# stream inf
codecs = []
if 'video' in media_info:
codecs.append(media_info['video']['codec'])
if 'audio' in media_info:
codecs.append(media_info['audio']['codec'])
elif group_name and group_codec:
codecs.append(group_codec)
ext_x_stream_inf = '#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=%d,BANDWIDTH=%d,CODECS="%s"' % (
int(media_info['stats']['avg_segment_bitrate'])+group_info['avg_segment_bitrate'],
int(media_info['stats']['max_segment_bitrate'])+group_info['max_segment_bitrate'],
','.join(codecs))
if 'video' in media_info:
ext_x_stream_inf += ',RESOLUTION='+str(int(media_info['video']['width']))+'x'+str(int(media_info['video']['height']))
# audio info
if group_name:
ext_x_stream_inf += ',AUDIO="'+group_name+'"'
# subtitles info
if len(subtitles_files):
ext_x_stream_inf += ',SUBTITLES="subtitles"'
master_playlist.write(ext_x_stream_inf+'\r\n')
master_playlist.write(options.base_url+media['dir']+'/'+options.media_playlist_name+'\r\n')
# write the I-FRAME playlist info
if not audio_only and options.hls_version >= 4:
master_playlist.write('\r\n')
master_playlist.write('# I-Frame Playlists\r\n')
for media in main_media:
media_info = media['info']
if not 'video' in media_info: continue
ext_x_i_frame_stream_inf = '#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH=%d,BANDWIDTH=%d,CODECS="%s",RESOLUTION=%dx%d,URI="%s"' % (
int(media_info['stats']['avg_iframe_bitrate']),
int(media_info['stats']['max_iframe_bitrate']),
media_info['video']['codec'],
int(media_info['video']['width']),
int(media_info['video']['height']),
options.base_url+media['dir']+'/'+options.iframe_playlist_name)
master_playlist.write(ext_x_i_frame_stream_inf+'\r\n')
#############################################
Options = None
def main():
# determine the platform binary name
host_platform = ''
if platform.system() == 'Linux':
if platform.processor() == 'x86_64':
host_platform = 'linux-x86_64'
else:
host_platform = 'linux-x86'
elif platform.system() == 'Darwin':
host_platform = 'macosx'
elif platform.system() == 'Windows':
host_platform = 'win32'
default_exec_dir = path.join(SCRIPT_PATH, 'bin', host_platform)
if not path.exists(default_exec_dir):
default_exec_dir = path.join(SCRIPT_PATH, 'bin')
if not path.exists(default_exec_dir):
default_exec_dir = path.join(SCRIPT_PATH, '..', 'bin')
# parse options
parser = OptionParser(usage="%prog [options] <media-file> [<media-file> ...]",
description="Each <media-file> is the path to an MP4 file, optionally prefixed with a stream selector delimited by [ and ]. The same input MP4 file may be repeated, provided that the stream selector prefixes select different streams. Version " + VERSION + " r" + SDK_REVISION)
parser.add_option('-v', '--verbose', dest="verbose", action='store_true', default=False,
help="Be verbose")
parser.add_option('-d', '--debug', dest="debug", action='store_true', default=False,
help="Print out debugging information")
parser.add_option('-o', '--output-dir', dest="output_dir",
help="Output directory", metavar="<output-dir>", default='output')
parser.add_option('-f', '--force', dest="force_output", action="store_true", default=False,
help="Allow output to an existing directory")
parser.add_option('', '--hls-version', dest="hls_version", type="int", metavar="<version>", default=4,
help="HLS Version (default: 4)")
parser.add_option('', '--master-playlist-name', dest="master_playlist_name", metavar="<filename>", default='master.m3u8',
help="Master Playlist name")
parser.add_option('', '--media-playlist-name', dest="media_playlist_name", metavar="<name>", default='stream.m3u8',
help="Media Playlist name")
parser.add_option('', '--iframe-playlist-name', dest="iframe_playlist_name", metavar="<name>", default='iframes.m3u8',
help="I-frame Playlist name")
parser.add_option('', '--output-single-file', dest="output_single_file", action='store_true', default=False,
help="Store segment data in a single output file per input file")
parser.add_option('', '--audio-format', dest="audio_format", default='packed',
help="Format for audio segments (packed or ts) (default: packed)")
parser.add_option('', '--segment-duration', dest="segment_duration",
help="Segment duration (default: 6)")
parser.add_option('', '--encryption-mode', dest="encryption_mode", metavar="<mode>",
help="Encryption mode (only used when --encryption-key is specified). AES-128 or SAMPLE-AES (default: AES-128)")
parser.add_option('', '--encryption-key', dest="encryption_key", metavar="<key>",
help="Encryption key in hexadecimal (default: no encryption)")
parser.add_option('', '--encryption-iv-mode', dest="encryption_iv_mode", metavar="<mode>",
help="Encryption IV mode: 'sequence', 'random' or 'fps' (Fairplay Streaming) (default: sequence). When the mode is 'fps', the encryption key must be 32 bytes: 16 bytes for the key followed by 16 bytes for the IV.")
parser.add_option('', '--encryption-key-uri', dest="encryption_key_uri", metavar="<uri>", default="key.bin",
help="Encryption key URI (may be a relative or absolute URI). (default: key.bin)")
parser.add_option('', '--encryption-key-format', dest="encryption_key_format", metavar="<format>",
help="Encryption key format. (default: 'identity')")
parser.add_option('', '--encryption-key-format-versions', dest="encryption_key_format_versions", metavar="<versions>",
help="Encryption key format versions.")
parser.add_option('', '--signal-session-key', dest='signal_session_key', action='store_true', default=False,
help="Signal an #EXT-X-SESSION-KEY tag in the master playlist")
parser.add_option('', '--fairplay', dest="fairplay", metavar="<fairplay-parameters>", help="Enable Fairplay Key Delivery. The <fairplay-parameters> argument is one or more <name>:<value> pair(s) (separated by '#' if more than one). Names include 'uri' [string] (required)")
parser.add_option('', '--widevine', dest="widevine", metavar="<widevine-parameters>", help="Enable Widevine Key Delivery. The <widevine-parameters> argument is one or more <name>:<value> pair(s) (separated by '#' if more than one). Names include 'provider' [string] (required), 'content_id' [byte array in hex] (optional), 'kid' [16-byte array in hex] (required)")
parser.add_option('', '--output-encryption-key', dest="output_encryption_key", action="store_true", default=False,
help="Output the encryption key to a file (default: don't output the key). This option is only valid when the encryption key format is 'identity'")
parser.add_option('', "--exec-dir", metavar="<exec_dir>", dest="exec_dir", default=default_exec_dir,
help="Directory where the Bento4 executables are located")
parser.add_option('', "--base-url", metavar="<base_url>", dest="base_url", default="",
help="The base URL for the Media Playlists and TS files listed in the playlists. This is the prefix for the files.")
(options, args) = parser.parse_args()
if len(args) == 0:
parser.print_help()
sys.exit(1)
global Options
Options = options
# set some mandatory options that utils rely upon
options.min_buffer_time = 0.0
if not path.exists(Options.exec_dir):
PrintErrorAndExit('Executable directory does not exist ('+Options.exec_dir+'), use --exec-dir')
# check options
if options.output_encryption_key:
if options.encryption_key_uri != "key.bin":
sys.stderr.write("WARNING: the encryption key will not be output because a non-default key URI was specified\n")
options.output_encryption_key = False
if not options.encryption_key:
sys.stderr.write("ERROR: --output-encryption-key requires --encryption-key to be specified\n")
sys.exit(1)
if options.encryption_key_format != None and options.encryption_key_format != 'identity':
sys.stderr.write("ERROR: --output-encryption-key requires --encryption-key-format to be omitted or set to 'identity'\n")
sys.exit(1)
# Fairplay option
if options.fairplay:
if not options.encryption_key_format:
options.encryption_key_format = 'com.apple.streamingkeydelivery'
if not options.encryption_key_format_versions:
options.encryption_key_format_versions = '1'
if options.encryption_iv_mode:
if options.encryption_iv_mode != 'fps':
sys.stderr.write("ERROR: --fairplay requires --encryption-iv-mode to be 'fps'\n")
sys.exit(1)
else:
options.encryption_iv_mode = 'fps'
if not options.encryption_key:
sys.stderr.write("ERROR: --fairplay requires --encryption-key to be specified\n")
sys.exit(1)
if options.encryption_mode:
if options.encryption_mode != 'SAMPLE-AES':
sys.stderr.write('ERROR: --fairplay option incompatible with '+options.encryption_mode+' encryption mode\n')
sys.exit(1)
else:
options.encryption_mode = 'SAMPLE-AES'
options.fairplay = SplitArgs(options.fairplay)
if 'uri' not in options.fairplay:
sys.stderr.write('ERROR: --fairplay option requires a "uri" parameter (ex: skd://xxx)\n')
sys.exit(1)
options.signal_session_key = True
# Widevine option
if options.widevine:
if not options.encryption_key:
sys.stderr.write("ERROR: --widevine requires --encryption-key to be specified\n")
sys.exit(1)
if options.encryption_mode:
if options.encryption_mode != 'SAMPLE-AES':
sys.stderr.write('ERROR: --widevine option incompatible with '+options.encryption_mode+' encryption mode\n')
sys.exit(1)
else:
options.encryption_mode = 'SAMPLE-AES'
options.widevine = SplitArgs(options.widevine)
if 'kid' not in options.widevine:
sys.stderr.write('ERROR: --widevine option requires a "kid" parameter\n')
sys.exit(1)
if len(options.widevine['kid']) != 32:
sys.stderr.write('ERROR: --widevine option "kid" must be 32 hex characters\n')
sys.exit(1)
if 'provider' not in options.widevine:
sys.stderr.write('ERROR: --widevine option requires a "provider" parameter\n')
sys.exit(1)
if 'content_id' in options.widevine:
options.widevine['content_id'] = options.widevine['content_id'].decode('hex')
else:
options.widevine['content_id'] = '*'
# defaults
if options.encryption_key and not options.encryption_mode:
options.encryption_mode = 'AES-128'
if options.encryption_mode == 'SAMPLE-AES':
options.hls_version = 5
# parse media sources syntax
media_sources = [MediaSource(source) for source in args]
for media_source in media_sources:
media_source.has_audio = False
media_source.has_video = False
# create the output directory
severity = 'ERROR'
if options.force_output: severity = None
MakeNewDir(dir=options.output_dir, exit_if_exists = not options.force_output, severity=severity)
# output the media playlists
OutputHls(options, media_sources)
###########################
if sys.version_info[0] != 2:
sys.stderr.write("ERROR: This tool must be run with Python 2.x\n")
sys.stderr.write("You are running Python version: "+sys.version+"\n")
exit(1)
if __name__ == '__main__':
try:
main()
except Exception, err:
if Options and Options.debug:
raise
else:
PrintErrorAndExit('ERROR: %s\n' % str(err))