Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Packaging multi codec DASH and HLS with cenc and cbcs encryption for widevine, playready, and fairplay w/ shaka & bento4
def package_local_targets(input_dir, output_dir):
trace = 'package_local_targets'
os.chdir(input_dir)
try:
print(f"{trace}: create directory {output_dir}")
os.makedirs(output_dir)
except FileExistsError:
print(f"delete existing {output_dir}")
shutil.rmtree(output_dir)
print(f"deleted directory {output_dir}")
os.makedirs(output_dir)
print(f"created directory {output_dir}")
package_dash(input_dir=input_dir, output_dir=output_dir)
package_hls(input_dir=input_dir, output_dir=output_dir)
def package_dash(input_dir, output_dir):
trace = 'package_local_targets_dash'
os.chdir(input_dir)
try:
print(f"{trace}: create directory {output_dir}")
os.makedirs(output_dir)
except FileExistsError:
print(f"{trace}: found existing {output_dir}")
pkg_commands = []
packaging_cmd_to_execute = 'packager '
seperator = ' '
video_audio_hevc_avc_transcode_targets = glob.glob(f'{input_dir}/*.mp4')
sidecar_targets = glob.glob(f'{input_dir}/*.vtt')
packager_video_inputs_outputs_hevc_targets = []
mpd_generator_paths_hevc = []
packager_video_inputs_outputs_avc_targets = []
mpd_generator_paths_avc = []
packager_audio_inputs_outputs_targets = []
mpd_generator_paths_audio = []
packager_subtitle_inputs_outputs_webvtt_targets = []
mpd_generator_paths_subtitle = []
for mp4_file_name in video_audio_hevc_avc_transcode_targets:
print(f"{trace}: mp4 {mp4_file_name}")
if ('hev1' in mp4_file_name or 'h265' in mp4_file_name) and '.vtt' not in mp4_file_name:
mpd_generator_paths_hevc.append(f"{output_dir}/{Path(mp4_file_name).stem}"+"_cenc_protected.mp4.media_info")
packager_video_inputs_outputs_hevc_targets.append(format_packager_in_target_audio_video(mp4_file_name, 'video', output_dir, 'MEDIA'))
elif ('avc1' in mp4_file_name or 'h264' in mp4_file_name) and ('.vtt' not in mp4_file_name and '_600_' not in mp4_file_name):
mpd_generator_paths_avc.append(f"{output_dir}/{Path(mp4_file_name).stem}"+"_cenc_protected.mp4.media_info")
packager_video_inputs_outputs_avc_targets.append(format_packager_in_target_audio_video(mp4_file_name, 'video', output_dir, 'MEDIA'))
elif 'audio' in mp4_file_name and '.vtt' not in mp4_file_name:
mpd_generator_paths_audio.append(f"{output_dir}/{Path(mp4_file_name).stem}"+"_cenc_protected.mp4.media_info")
packager_audio_inputs_outputs_targets.append(format_packager_in_target_audio_video(mp4_file_name, 'audio', output_dir, 'MEDIA'))
for sidecar in sidecar_targets:
##WHEN WE GET TO THE POINT WE ACTUALY KNOW THE LANGUAGE VIA PROVIDER OR ANALYZING PROPERLY WE JUST ADD ANOTHER PARAM
##FOR NOW WE WILL JUST ASSUME AND DEFAULT ENGLISH
mpd_generator_paths_subtitle.append(f"{output_dir}/{Path(sidecar).stem}"+".vtt.media_info")
packager_subtitle_inputs_outputs_webvtt_targets.append(format_packager_in_target_text(sidecar, 'text', output_dir))
packager_audio_videoh264_videoh265_inputs_outputs = seperator.join(packager_video_inputs_outputs_avc_targets) + " " + seperator.join(packager_video_inputs_outputs_hevc_targets) + " " + seperator.join(packager_audio_inputs_outputs_targets) + " " + seperator.join(packager_subtitle_inputs_outputs_webvtt_targets)
##APPEND ALL INPUTS TO THE PACKAGING COMMAND
packaging_cmd_input_options = packaging_cmd_to_execute + packager_audio_videoh264_videoh265_inputs_outputs
#print(f"{trace}: {packaging_cmd_to_execute}")
##LAST BUT NOT LEAST WE ADD IN THE ENCRYPTION KEYS TO APPLY DRM TO THE RESULTING PACKAING
enable_media_info_output = '--output_media_info'
flag_raw_encryption = '--enable_raw_key_encryption'
key_and_keyid = f"--keys label={DRM_LABEL_DEFAULT}:key_id={KEY_ID}:key={KEY}"
protections = '--protection_systems Widevine,PlayReady'
fragment_duration = ''
mpd_out = f"--mpd_output {output_dir}/master-avc-hevc.mpd"
packaging_cmd_to_execute = f"{packaging_cmd_input_options} {enable_media_info_output} {fragment_duration} {flag_raw_encryption} {key_and_keyid} {protections} {mpd_out}"
pkg_commands.append(packaging_cmd_to_execute)
#### DUE TO THE NEED FOR DIFFERENT MANIFEST SFOR ALL TYPES OF PLAYER WE WILL GO AHEAD AND REFERENCE THE SAME ENCRYPTED MEDIA BUT
#### A FEW DIFFERENT VAARIENTS ON THE PLAYLISTS
manifest_seperator = ","
infiles_hevc = manifest_seperator.join(mpd_generator_paths_hevc)
infiles_avc = manifest_seperator.join(mpd_generator_paths_avc)
infiles_audio = manifest_seperator.join(mpd_generator_paths_audio)
infiles_text = manifest_seperator.join(mpd_generator_paths_subtitle)
build_manifest_h264 = f"/packager/utils/mpd_generator --input {infiles_avc},{infiles_audio},{infiles_text} --output {output_dir}/master.mpd"
pkg_commands.append(build_manifest_h264)
build_manifest_h265 = f"/packager/utils/mpd_generator --input {infiles_hevc},{infiles_audio},{infiles_text} --output {output_dir}/master-hevc.mpd"
pkg_commands.append(build_manifest_h265)
for pkg_cmd in pkg_commands:
try:
##EXECUTE THE PACKAING AND ENCRYPTION COMMAND
print(f"{trace}: package_cmd to be executed {pkg_cmd}")
with subprocess.Popen(pkg_cmd, env=my_env, shell=True, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True) as p:
for line in p.stdout:
print(f"{trace}: {line}", end='')
except (OSError) as exception:
print('Exception occured: ' + str(exception))
print(f'{trace} failed')
return False
# no exception was raised
print(f'{trace} - all packaging finished')
return True
def package_hls(input_dir, output_dir):
trace = 'package_hls'
seperator = ' '
video_audio_hevc_avc_transcode_targets = glob.glob(f'{input_dir}/*.mp4')
subtitle_targets = glob.glob(f'{input_dir}/*.vtt')
video_transcode_targets_hevc = []
video_transcode_targets_avc = []
audio_transcode_targets = []
for mp4_file_name in video_audio_hevc_avc_transcode_targets:
print(f"{trace}: mp4 {mp4_file_name}")
if 'hev1' in mp4_file_name or 'h265' in mp4_file_name and '.vtt' not in mp4_file_name:
video_transcode_targets_hevc.append(mp4_file_name)
elif ('avc1' in mp4_file_name or 'h264' in mp4_file_name) and '.vtt' not in mp4_file_name:
video_transcode_targets_avc.append(mp4_file_name)
elif 'audio' in mp4_file_name and '.vtt' not in mp4_file_name:
audio_transcode_targets.append(mp4_file_name)
video_hevc_for_packaging = seperator.join(video_transcode_targets_hevc)
print(f"{trace}: video_hevc_for_packaging {video_hevc_for_packaging}")
video_avc_for_packaging = seperator.join(video_transcode_targets_avc)
print(f"{trace}: video_avc_for_packaging {video_avc_for_packaging}")
audio_for_packaging = seperator.join(audio_transcode_targets)
print(f"{trace}: audio_for_packaging {audio_for_packaging}")
enriched_subtitle_data = enrich_media_inputs(subtitle_targets, 'webvtt', 'eng')
subtitle_targets_for_packaging = seperator.join(enriched_subtitle_data)
print(f"{trace}: subtitle_targets_for_packaging {subtitle_targets_for_packaging}")
pkg_commands = []
package_cmd_hls_all_codecs = f"python3 /opt/bento4/utils/mp4-dash.py --hls --no-split --use-segment-timeline \
--encryption-cenc-scheme=cbcs --encryption-key={KEY_ID}:{KEY}:{KEY_ID} \
--fairplay-key-uri skd://{KEY_ID} --widevine --widevine-header=#BASE64PSSH \
--playready-version=4.3 --playready --playready-header=LA_URL=http://pr-keyos.licensekeyserver.com/core/rightsmanager.asmx \
--output-dir {output_dir} --mpd-name master-cbcs.mpd --hls-master-playlist-name master.m3u8 \
--language-map=und:eng --media-prefix cbcs_protected --verbose --subtitles --debug -f \
--profiles on-demand {video_avc_for_packaging} {video_hevc_for_packaging} {audio_for_packaging}"
pkg_commands.append(package_cmd_hls_all_codecs)
print(f"{trace}: package_cmds to be executed")
for pkg_cmd in pkg_commands:
try:
## MAKE THE LAST MINUTE CONCATS FOR SUBTITLES IF WE HAD THEM ORIGINALLY
if subtitle_targets:
pkg_cmd = pkg_cmd + f" {subtitle_targets_for_packaging}"
##EXECUTE THE PACKAING AND ENCRYPTION COMMAND
print(f"{trace}: package_cmd to be executed {pkg_cmd}")
with subprocess.Popen(pkg_cmd, env=my_env, shell=True, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True) as p:
for line in p.stdout:
print(f"{trace}: {line}")
except (OSError) as exception:
print('Exception occured: ' + str(exception))
print(f'{trace} failed')
return False
# no exception was raised
print(f'{trace} - all packaging finished')
return True
def enrich_media_inputs(input_paths, input_format, language):
enriched_inputs = []
attribute_tag = f"\[+format={input_format},+language={language}\]"
for media_input in input_paths:
enriched_media_input = attribute_tag + media_input
enriched_inputs.append(enriched_media_input)
return enriched_inputs
def format_packager_in_target_audio_video(target_path, target_format, target_output_path, target_drm_label):
##BUILD COMMAND OUT FOR HOW SHAKA PACKAGER EXPECTS IT PER MEDIA
print(f"{format_packager_in_target_audio_video}: {target_path} {target_format} {target_output_path} {target_drm_label}")
defined_input_for_target = f'in={target_path},stream={target_format},output={target_output_path}/{Path(target_path).stem}_cenc_protected.mp4,drm_label={target_drm_label}'
return defined_input_for_target
def format_packager_in_target_text(target_path, target_format, target_output_path, target_lang_label = 'eng'):
##BUILD COMMAND OUT FOR HOW SHAKA PACKAGER EXPECTS IT PER MEDIA
print(f"{format_packager_in_target_text}: {target_path} {target_format} {target_output_path} {target_lang_label}")
defined_input_for_target = f'in={target_path},stream={target_format},output={target_output_path}/{Path(target_path).stem}.vtt,lang={target_lang_label}'
return defined_input_for_target
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment