I have a python function encoding a mp3 to m3u8. This function allows me to generate a m3u8 file along with its ts chunks.
I can read this "playlist" using the native player on iOs. Unfortunately I can't achieve to do the using the android-mediaplayer (I got the error MEDIA_ERROR_MALFORMED).
The catch is, if in python, I use the openssl via subprocess, it works. But spawning a new process is too expensive and I want to avoid this :
cmd = ["openssl",
"aes-128-cbc",
"-e",
"-in", path,
"-out", dest_path+".openssl.ts",
"-iv", ("%d" % iv_counter).zfill(32),
"-K", keyHex]
subprocess.check_call(cmd)
Using openssl or my implementation produces the same m3u8 file, the same numbers of ts files and these ts files have exactly the same weight.
The only explanation I could find is that my implementation is wrong. I know this may be hard to debug, but maybe something will jump at you at first reading. Here is the function doing the encryption :
from Crypto import Random
from Crypto.Cipher import AES
def encrypt(manifest, chunks, enc_dir):
os.makedirs(enc_dir)
# Get a random key
key = Random.new().read(16)
keyHex = key.encode('hex')
# Encrypt each chunk
for iv_counter, (_, path) in enumerate(chunks):
with open(path, "rb") as chunk:
chunk_data = chunk.read()
# PKCS#7 padding
pad = 16 - (len(chunk_data) % 16)
chunk_data += chr(pad) * pad
print "crypting using %s" % ("%d" % iv_counter).zfill(32)
# AES encryption
aes = AES.new(key, AES.MODE_CBC, "%16X" % iv_counter)
chunk_data = aes.encrypt(chunk_data)
dest_path = os.path.join(enc_dir, os.path.basename(path))
#cmd = ["openssl",
# "aes-128-cbc",
# "-e",
# "-in", path,
# "-out", dest_path+".openssl.ts",
# "-iv", ("%d" % iv_counter).zfill(32),
# "-K", keyHex]
#subprocess.check_call(cmd)
with open(dest_path, "wb") as chunk:
chunk.write(chunk_data)
# Write the key to a file
key_file = os.path.join(enc_dir, os.path.splitext(os.path.basename(manifest))[0] + ".key")
with open(key_file, "w") as keyf:
keyf.write(key)
key_url = os.path.basename(key_file) #"file://" + os.path.abspath(key_file)
# Write the new manifest
dest_manifest = os.path.join(enc_dir, os.path.basename(manifest))
with open(dest_manifest, "w") as manifest:
manifest.write("#EXTM3U\n")
manifest.write("#EXT-X-VERSION:3\n")
manifest.write("#EXT-X-MEDIA-SEQUENCE:0\n")
manifest.write("#EXT-X-ALLOW-CACHE:YES\n")
manifest.write("#EXT-X-TARGETDURATION:6\n")
manifest.write("#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"\n" % key_url)
for extinf, path in chunks:
manifest.write("%s\n%s\n" % (extinf, os.path.basename(path)))
manifest.write("#EXT-X-ENDLIST\n")
EDIT, if it can helps : we have coded a small JAVA function to decrypt a ts file produced by openssl and our homemade code. The file produced by openssl is fine but we got a bad padding exception for the one produced by our python code.
The problem is coming from your initial vector (iv). OpenSSL is waiting for a number parameter (in hexadecimal), but in a string format of 16 characters.
Your code just returns the number in hexadecimal, but in ASCII format:
>>> iv_counter = 11111111
>>> print("%16X" % iv_counter)
' A98AC7'
However, the expected value is:
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa9\x8a\xc7'
To achieve it, you must replace that with:
>>> print(("%032X" % iv_counter).decode("hex"))
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa9\x8a\xc7'