VideoHelp Forum



Support our site by donate $5 directly to us Thanks!!!

Try StreamFab Downloader and download streaming video from Netflix, Amazon!



+ Reply to Thread
Results 1 to 15 of 15
  1. Hi! As the title suggests, I was wondering if there was a way to get the PSSH from init mp4 file without using mp4dump.exe/axiomtools website just using pure python and bento4 sdk somehow. I tried reading their code but I could not understand an easy way to use mp4_dump.py or any other python scripts directly to get init.mp4's KID for the video, which in turn can be used with get_pssh.py file to convert KID into pssh.

    Edit: Solution: https://forum.videohelp.com/threads/405001-How-to-get-the-widevine-pssh-from-init-mp4-...pt#post2650501
    Last edited by ancientbanana; 4th Mar 2022 at 14:13. Reason: Thanks zackmark29 for the solution!
    Quote Quote  
  2. In case someone here is still wondering, for windows this is how I figured to do it:

    Download the zip file, replace the mp4dump.exe if needed, make changes on do.py file as necessary (if at all needed), place the init mp4 files into init folder, and then run the do.py file. As far as I remember, you won't need to install any dependencies for running the python script. Please let me know if you have any questions.
    Image Attached Files
    Quote Quote  
  3. Originally Posted by ancientbanana View Post
    Please let me know if you have any questions.
    I have one: why are you trying to reinvent the wheel ?

    This is more steps than just running mp4dump - which is one of your dependencies, lol.
    Quote Quote  
  4. Originally Posted by codehound View Post
    Originally Posted by ancientbanana View Post
    Please let me know if you have any questions.
    I have one: why are you trying to reinvent the wheel ?

    This is more steps than just running mp4dump - which is one of your dependencies, lol.
    Currently, while using mp4dump, for certain init mp4s, we just get default KID out of it. And once, we do get the KID, we need to remove the spaces and then use the axiomtools website so as to generate the PSSH corresponding to it. And so, that may be a hassle for some of us who are trying to automate most of the things, and therefore I wanted to have an easier solution for guys like me who like "one touch" solutions to their problems
    However, if you do have an idea that's simpler than this, please do let me know, I would definitely love to learn something new!
    Sample init which does not have pssh data already:
    Image Attached Files
    Quote Quote  
  5. Originally Posted by ancientbanana View Post
    Originally Posted by codehound View Post
    Originally Posted by ancientbanana View Post
    Please let me know if you have any questions.
    I have one: why are you trying to reinvent the wheel ?

    This is more steps than just running mp4dump - which is one of your dependencies, lol.
    Currently, while using mp4dump, for certain init mp4s, we just get default KID out of it. And once, we do get the KID, we need to remove the spaces and then use the axiomtools website so as to generate the PSSH corresponding to it. And so, that may be a hassle for some of us who are trying to automate most of the things, and therefore I wanted to have an easier solution for guys like me who like "one touch" solutions to their problems
    However, if you do have an idea that's simpler than this, please do let me know, I would definitely love to learn something new!
    Sample init which does not have pssh data already:
    Since you tried it first, here's the simplest way without using mp4dump


    Code:
    def find_wv_pssh_offset(raw: bytes) -> str:
        print('Searching pssh offset')
        offset = raw.rfind(b'pssh')
        return raw[offset - 4:offset - 4 + raw[offset - 1]]
    
    
    def to_pssh(content: bytes) -> str:
        wv_offset = find_wv_pssh_offset(content)
        return base64.b64encode(wv_offset).decode()
    
    
    def from_file(file_path: str) -> str:
        print('Extracting PSSH from init file:', file_path)
        return to_pssh(Path(file_path).read_bytes())
    You might wondering why it has separated functions?
    Well you can use that to_pssh for like downloading the init url and reading the response content.

    So same as "from_file" function

    Code:
    def from_init_url(init_url: str) -> str:
       # your code here
       # and then when returning, use to_pssh(response.content)
    Good luck
    Last edited by zackmark29; 4th Mar 2022 at 14:25.
    Quote Quote  
  6. Originally Posted by zackmark29 View Post
    Originally Posted by ancientbanana View Post
    Originally Posted by codehound View Post
    Originally Posted by ancientbanana View Post
    Please let me know if you have any questions.
    I have one: why are you trying to reinvent the wheel ?

    This is more steps than just running mp4dump - which is one of your dependencies, lol.
    Currently, while using mp4dump, for certain init mp4s, we just get default KID out of it. And once, we do get the KID, we need to remove the spaces and then use the axiomtools website so as to generate the PSSH corresponding to it. And so, that may be a hassle for some of us who are trying to automate most of the things, and therefore I wanted to have an easier solution for guys like me who like "one touch" solutions to their problems
    However, if you do have an idea that's simpler than this, please do let me know, I would definitely love to learn something new!
    Sample init which does not have pssh data already:
    Since you tried it first, here's the simples way without using mp4dump


    Code:
    def find_wv_pssh_offset(raw: bytes) -> str:
        print('Searching pssh offset')
        offset = raw.rfind(b'pssh')
        return raw[offset - 4:offset - 4 + raw[offset - 1]]
    
    
    def to_pssh(content: bytes) -> str:
        wv_offset = find_wv_pssh_offset(content)
        return base64.b64encode(wv_offset).decode()
    
    
    def from_file(file_path: str) -> str:
        print('Extracting PSSH from init file:', file_path)
        return to_pssh(Path(file_path).read_bytes())
    You might wondering why it has separated functions?
    Well you can use that to_pssh for like downloading the init url and reading the response content.

    So same as "from_file" function

    Code:
    def from_init_url(init_url: str) -> str:
       # your code here
       # and then when returning, use to_pssh(response.content)
    Good luck
    Thank you so much! It works perfectly awesome, I really appreciate your help with this! Have a good day!
    Quote Quote  
  7. Init to pssh is smaller than eme logger pssh. Only eme logger pssh gives the key. So why init to pssh gives smaller pssh.

    https://cdn.class101.net/videos/447d9e6d-3dae-4d3b-b7e1-c1a3f541168e/cmaf/video/avc1/1/init.mp4

    Pssh from init to pssh :- AAAAIHBzc2gAAAAAKXAf5DzHSjSMW66Qx0OaRwAAAAA=

    Pssh from eme logger:- AAAAbHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAEwSEDQGzo/zboUj5s5B5NpAMb8aDGlua2FlbnR3b3JrcyIkNDQ3ZDllNmQtM 2RhZS00ZDNiLWI3ZTEtYzFhM2Y1NDExNjhlSPPGiZsG
    Quote Quote  
  8. Originally Posted by Sadomasochist View Post
    Init to pssh is smaller than eme logger pssh. Only eme logger pssh gives the key. So why init to pssh gives smaller pssh.

    https://cdn.class101.net/videos/447d9e6d-3dae-4d3b-b7e1-c1a3f541168e/cmaf/video/avc1/1/init.mp4

    Pssh from init to pssh :- AAAAIHBzc2gAAAAAKXAf5DzHSjSMW66Qx0OaRwAAAAA=

    Pssh from eme logger:- AAAAbHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAEwSEDQGzo/zboUj5s5B5NpAMb8aDGlua2FlbnR3b3JrcyIkNDQ3ZDllNmQtM 2RhZS00ZDNiLWI3ZTEtYzFhM2Y1NDExNjhlSPPGiZsG
    put them both into https://tools.axinom.com/decoders/PsshBox and see for yourself

    the longer one is a widevine pssh and it contains both kid and content id in it - correct.

    the short one is not a widevine pssh and doesnt look complete either.
    after looking at the init.mp4 you pasted, it contains 3 different psshs - widevine, playready and fairplay and the script seems to be incorrectly grabbing the fairplay pssh data.

    the correct widevine pssh can be grabbed from that init.mp4 file
    Quote Quote  
  9. I need a script which shows all 3 scripts, do you have one? I am writing a script which will decrypt and download, if I can get the proper pssh. It would be helpful!
    Quote Quote  
  10. thanks zackmark29,
    changeing a little to getting the all pssh's from init and filter with characters limit 70 to 190
    and write just filtered in pssh.txt file

    Code:
    import base64
    from pathlib import Path
    
    def find_wv_pssh_offsets(raw: bytes) -> list:
        print('Searching pssh offsets')
        offsets = []
        offset = 0
        while True:
            offset = raw.find(b'pssh', offset)
            if offset == -1:
                break
            size = int.from_bytes(raw[offset-4:offset], byteorder='big')
            pssh_offset = offset - 4
            offsets.append(raw[pssh_offset:pssh_offset+size])
            offset += size
        return offsets
    
    def to_pssh(content: bytes) -> list:
        wv_offsets = find_wv_pssh_offsets(content)
        return [base64.b64encode(wv_offset).decode() for wv_offset in wv_offsets]
    
    def from_file(file_path: str) -> list:
        print('Extracting PSSHs from init file:', file_path)
        return to_pssh(Path(file_path).read_bytes())
    
    pssh_list = from_file('init.mp4')
    target_pssh = None
    for pssh in pssh_list:
        if 70 < len(pssh) < 190:
            target_pssh = pssh
    print(f'\n{target_pssh}\n')
    print(target_pssh, file=open("pssh.txt", "w"))
    Image
    [Attachment 70739 - Click to enlarge]
    Quote Quote  
  11. Thanks for this! but init.mp4 should be in the folder for it to work! It is good enough! I will find a way for it to extract init.mp4 from the site and find the pssh.
    Quote Quote  
  12. Member
    Join Date
    Dec 2020
    Location
    Croatia
    Search PM
    I usually download a veeeery small test file using yt-dlps --test option and then use this on it:


    Code:
    def construct_pssh():
        mymp4dump = subprocess.Popen(
            [const.mp4dumpexe, '--verbosity', '3', '--format', 'json', 'temp/test.mp4'],
            stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
        dump = (mymp4dump.stdout.read())
        json_info = json.loads(dump.decode("utf-8"))
    
        for temp in json_info:
            if (temp['name'] == 'moov'):
                for child in temp['children']:
                    if ('system_id' in child and child['system_id'] == '[ed ef 8b a9 79 d6 4a ce a3 c8 27 dc d5 1d 21 ed]'):
                        pssh_data = child['data']
                        pssh_data_size_dec = child['data_size']
                        break
                break
    
        pssh_data = pssh_data.replace('[', '').replace(']', '').replace(' ', '')
        pssh_size_dec = pssh_data_size_dec + 32
        pssh_data_size_hex = (str(hex(pssh_data_size_dec))).replace('0x', '')
        pssh_size_hex = (str(hex(pssh_size_dec))).replace('0x', '')
    
        pssh_hex = f'{pssh_size_hex:0>8}7073736800000000edef8ba979d64acea3c827dcd51d21ed{pssh_data_size_hex:0>8}{pssh_data}'
    
        return (base64.b64encode(bytes.fromhex(pssh_hex))).decode('utf-8')
    Quote Quote  
  13. Originally Posted by ancientbanana View Post
    In case someone here is still wondering, for windows this is how I figured to do it:

    Download the zip file, replace the mp4dump.exe if needed, make changes on do.py file as necessary (if at all needed), place the init mp4 files into init folder, and then run the do.py file. As far as I remember, you won't need to install any dependencies for running the python script. Please let me know if you have any questions.
    its not working for me, ill do with the mp4 you put here, but is not working, any idea? shoul i change the go.py?how?
    Quote Quote  
  14. Originally Posted by sk8ordi3 View Post
    thanks zackmark29,
    changeing a little to getting the all pssh's from init and filter with characters limit 70 to 190
    and write just filtered in pssh.txt file

    Code:
    import base64
    from pathlib import Path
    
    def find_wv_pssh_offsets(raw: bytes) -> list:
        print('Searching pssh offsets')
        offsets = []
        offset = 0
        while True:
            offset = raw.find(b'pssh', offset)
            if offset == -1:
                break
            size = int.from_bytes(raw[offset-4:offset], byteorder='big')
            pssh_offset = offset - 4
            offsets.append(raw[pssh_offset:pssh_offset+size])
            offset += size
        return offsets
    
    def to_pssh(content: bytes) -> list:
        wv_offsets = find_wv_pssh_offsets(content)
        return [base64.b64encode(wv_offset).decode() for wv_offset in wv_offsets]
    
    def from_file(file_path: str) -> list:
        print('Extracting PSSHs from init file:', file_path)
        return to_pssh(Path(file_path).read_bytes())
    
    pssh_list = from_file('init.mp4')
    target_pssh = None
    for pssh in pssh_list:
        if 70 < len(pssh) < 190:
            target_pssh = pssh
    print(f'\n{target_pssh}\n')
    print(target_pssh, file=open("pssh.txt", "w"))
    Image
    [Attachment 70739 - Click to enlarge]
    should i have mp4dump.exe inside the folder?
    Quote Quote  
  15. Feels Good Man 2nHxWW6GkN1l916N3ayz8HQoi's Avatar
    Join Date
    Jan 2024
    Location
    Pepe Island
    Search Comp PM
    Originally Posted by sk8ordi3 View Post
    thanks zackmark29,
    changeing a little to getting the all pssh's from init and filter with characters limit 70 to 190
    and write just filtered in pssh.txt file

    Code:
    ...snipped...
    Nice script. Can it be adapted for playready pssh?
    --[----->+<]>.++++++++++++.---.--------.
    [*drm mass downloader: widefrog*]~~~~~~~~~~~[*how to make your own mass downloader: guide*]
    Quote Quote  



Similar Threads

Visit our sponsor! Try DVDFab and backup Blu-rays!