Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save krzemienski/59ada206f0763db17bf33f5f8702eeb7 to your computer and use it in GitHub Desktop.
Save krzemienski/59ada206f0763db17bf33f5f8702eeb7 to your computer and use it in GitHub Desktop.
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