In working on a project over at JRiver Media Center Guide: Chapterfy (Particlise Anything - aka Manage Music Videos Like CDs) I needed to be able to reliably extract Chapter Details (the # and their Start/Stop Times), but MediaInfo CLI has a bug with disks with more than one M2TS file per MPLS you only get the Chapter Info for the fist part.
So I wrote a parser that will:
- Dump and decode much of the MPLS to a Temp_Hex.txt file
- Create a temp.chapters.txt file (in the format used by ChapterDB and others)
My references were:
- https://en.wikibooks...ser:Bdinfo/mplsbut it was missing the spec for the " Play Item entries"
- MPLS Structure which has the info and a pic for this part in a thread by the Author of BDEDit
Limitations:
- I still don't have the whole spec so some bits in the PlayItem Section are not decoded in my prog (lang for each track etc)
- You don't get real Chapter Names as these are not stored in the MPLS anyway
; ---------------------------------------------------------------------------------------------------------------------- ; Name .........: MPLS Reader ; Description ..: Parses the Blu Ray MPLS File outputing a text file of the Contents of the MPLS and a Chapters Files. ; Author .......: JMONE ; Version ......: 20151016 ; ---------------------------------------------------------------------------------------------------------------------- #NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases. ; #Warn ; Enable warnings to assist with detecting common errors. SendMode Input ; Recommended for new scripts due to its superior speed and reliability. SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory. ;-------Read content of the MPLS and dump to Temp_hex.txt ------------- gosub, FileToHex Length := StrLen(Hexfile) FileAppend, Full Dump`n %Hexfile%, Temp_Hex.txt FileAppend, `n`nDetails of the MPLS - Length is %Length%`n`n, Temp_Hex.txt Progress, b1 w300, Parsing Main Section `n Please Wait ;-------Parse Main Section and dump values and details to Temp_hex.txt ------------- Temp := % Hex2Text(SubStr(Hexfile,1,2)) Hex2Text(SubStr(Hexfile,3,2)) Hex2Text(SubStr(Hexfile,5,2)) Hex2Text(SubStr(Hexfile,7,2)) TempHex := SubStr(Hexfile,1,8) FileAppend, Header: %Temp% Hex: %TempHex%`n, Temp_Hex.txt Temp := % Hex2Text(SubStr(Hexfile,9,2)) Hex2Text(SubStr(Hexfile,11,2)) Hex2Text(SubStr(Hexfile,13,2)) Hex2Text(SubStr(Hexfile,15,2)) TempHex := SubStr(Hexfile,9,8) FileAppend, Version: %Temp% Hex: %TempHex%`n, Temp_Hex.txt PlaylistSectionA := % hexToDecimal(SubStr(Hexfile,17,8)) TempHex := SubStr(Hexfile,17,8) FileAppend, Playlist Section start Byte #: %PlaylistSectionA% Hex: %TempHex%`n, Temp_Hex.txt PlaylistMarkSectionA:= % hexToDecimal(SubStr(Hexfile,25,8)) TempHex := SubStr(Hexfile,25,8) FileAppend, Playlist Mark Section start Byte #: %PlaylistMarkSectionA% Hex: %TempHex%`n, Temp_Hex.txt ExtensionDataA:= % hexToDecimal(SubStr(Hexfile,33,8)) TempHex := SubStr(Hexfile,33,8) FileAppend, Extension Data start Byte #: %ExtensionDataA% Hex: %TempHex%`n, Temp_Hex.txt Temp := % hexToDecimal(SubStr(Hexfile,41,40)) TempHex := SubStr(Hexfile,41,40) FileAppend, Reserved: %Temp% Hex: %TempHex%`n, Temp_Hex.txt Temp := % hexToDecimal(SubStr(Hexfile,81,8)) TempHex := SubStr(Hexfile,81,8) FileAppend, Application Info Playlist length: %Temp% Hex: %TempHex%`n, Temp_Hex.txt Temp := % hexToDecimal(SubStr(Hexfile,89,2)) TempHex := SubStr(Hexfile,89,2) FileAppend, Reserved: %Temp% Hex: %TempHex%`n, Temp_Hex.txt Temp := % hexToDecimal(SubStr(Hexfile,91,2)) TempHex := SubStr(Hexfile,91,2) FileAppend, Playlist type: %Temp% Hex: %TempHex%`n, Temp_Hex.txt Temp := % hexToDecimal(SubStr(Hexfile,93,4)) TempHex := SubStr(Hexfile,93,4) FileAppend, Playlist count: %Temp% Hex: %TempHex%`n, Temp_Hex.txt Temp := % hexToDecimal(SubStr(Hexfile,97,16)) TempHex := SubStr(Hexfile,97,16) FileAppend, User operation mask table: %Temp% Hex: %TempHex%`n, Temp_Hex.txt Temp := % hexToDecimal(SubStr(Hexfile,113,2)) TempHex := SubStr(Hexfile,113,2) FileAppend, Miscellaneous flags: %Temp% Hex: %TempHex%`n, Temp_Hex.txt Temp := % hexToDecimal(SubStr(Hexfile,115,2)) TempHex := SubStr(Hexfile,115,2) FileAppend, Reserved: %Temp% Hex: %TempHex%`n, Temp_Hex.txt Progress, b1 w300, Parsing Playlist Section `n Please Wait ;-------Parse Playlist Section and dump values and details to Temp_hex.txt ------------- PlaylistSectionA := PlaylistSectionA*2+1 FileAppend, `nDetails of the Playlist Section staring at Position:%PlaylistSectionA%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistSectionA,8) Temp := % hexToDecimal(TempHex) FileAppend, Length: %Temp% Hex: %TempHex%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistSectionA+8,4) Temp := % hexToDecimal(TempHex) FileAppend, Reserved: %Temp% Hex: %TempHex%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistSectionA+12,4) NumberofPlayItems := % hexToDecimal(TempHex) FileAppend, Number of Play Items: %NumberofPlayItems% Hex: %TempHex%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistSectionA+16,4) Temp := % hexToDecimal(TempHex) FileAppend, Number of Subpaths: %Temp% Hex: %TempHex%`n, Temp_Hex.txt ;----------Loop PlayItem in Playlist Section and dump values and details to Temp_hex.txt ---- PlayItemA := PlaylistSectionA+20 Loop, %NumberofPlayItems% { FileAppend, `nDetails of the PlayItem:%A_Index%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlayItemA,4) PlayItemLength := % hexToDecimal(TempHex) FileAppend, Length of PlayItem: %PlayItemLength% Hex: %TempHex%`n, Temp_Hex.txt Temp := % Hex2Text(SubStr(Hexfile,PlayItemA+4,2)) Hex2Text(SubStr(Hexfile,PlayItemA+6,2)) Hex2Text(SubStr(Hexfile,PlayItemA+8,2)) Hex2Text(SubStr(Hexfile,PlayItemA+10,2)) Hex2Text(SubStr(Hexfile,PlayItemA+12,2)) Hex2Text(SubStr(Hexfile,PlayItemA+14,2)) Hex2Text(SubStr(Hexfile,PlayItemA+16,2)) Hex2Text(SubStr(Hexfile,PlayItemA+18,2)) Hex2Text(SubStr(Hexfile,PlayItemA+20,2)) TempHex := SubStr(Hexfile,PlayItemA+4,18) FileAppend, PlayItem: %Temp% Hex: %TempHex%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlayItemA+22,4) Temp := % hexToDecimal(TempHex) FileAppend, Connection Condition: %Temp% Hex: %TempHex%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlayItemA+26,2) Temp := % hexToDecimal(TempHex) FileAppend, Reserved: %Temp% Hex: %TempHex%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlayItemA+28,8) Temp := % hexToDecimal(TempHex) TempSec := Temp/45000 PlayItemInSec%A_Index% := TempSec FileAppend, Time In: %Temp% Hex: %TempHex% In Seconds: %TempSec%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlayItemA+36,8) Temp := % hexToDecimal(TempHex) TempSec := Temp/45000 PlayItemOutSec%A_Index% := TempSec FileAppend, Time Out: %Temp% Hex: %TempHex% Out Seconds: %TempSec%`n, Temp_Hex.txt PlayItemA := PlayItemA+PlayItemLength*2+4 } ;---- note there is some unparsed values in this PlayItem Section post the chapter marks that I don't know what they are Progress, b1 w300, Parsing Playlist Mark Section `n Please Wait ;-------Parse Playlist Mark Section and dump values and details to Temp_hex.txt ------------- PlaylistMarkSectionA := PlaylistMarkSectionA*2+1 FileAppend, `nDetails of the Playlist Mark Section staring at Position:%PlaylistMarkSectionA%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistMarkSectionA,8) Temp := % hexToDecimal(TempHex) FileAppend, Length: %Temp% Hex: %TempHex%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistMarkSectionA+8,4) NumberofPlaylistMarks := % hexToDecimal(TempHex) FileAppend, Number of Playlist Marks: %NumberofPlaylistMarks% Hex: %TempHex%`n, Temp_Hex.txt ;-------Parse Playlist Mark Entry and dump values and details to Temp_hex.txt ------------- PlaylistMarkSectionEntryA := PlaylistMarkSectionA+12 FileAppend, `nDetails of the Playlist Mark Entry staring at Position:%PlaylistMarkSectionEntryA%`n, Temp_Hex.txt Loop, %NumberofPlaylistMarks% { FileAppend, `nDetails of Playlist Mark Entry #:%A_Index%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistMarkSectionEntryA,2) Temp := % hexToDecimal(TempHex) FileAppend, Reserved: %Temp% Hex: %TempHex%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistMarkSectionEntryA+2,2) Temp := % hexToDecimal(TempHex) FileAppend, Mark Type: %Temp% Hex: %TempHex%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistMarkSectionEntryA+4,4) PlayItemID := % hexToDecimal(TempHex) FileAppend, Play Item ID: %PlayItemID% Hex: %TempHex%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistMarkSectionEntryA+8,8) Temp := % hexToDecimal(TempHex) TempSec := Temp/45000 FileAppend, Time Stamp: %Temp% Hex: %TempHex% Seconds: %TempSec%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistMarkSectionEntryA+16,4) Temp := % hexToDecimal(TempHex) FileAppend, Entry ESPID: %Temp% Hex: %TempHex%`n, Temp_Hex.txt TempHex := SubStr(HexFile,PlaylistMarkSectionEntryA+20,8) Temp := % hexToDecimal(TempHex) FileAppend, Duration: %Temp% Hex: %TempHex%`n, Temp_Hex.txt ; -------Calculate the Chapter Details and dump values and details to temp.chatpers.txt ------------- temp := PlayItemID+1 ; increment PlayItemID to match PlayItemInSec that start at 0 instead of 1 Offset := -PlayItemInSec%temp% IF (PlayItemID > 0) Offset := PlayItemOutSec%PlayItemID%-PlayItemInSec%PlayItemID%-PlayItemInSec%temp% ChapStartTime := TempSec+Offset ChapStartTime = % SecToChapTime(ChapStartTime) Chap_N = %A_Index% If A_Index < 10 Chap_N = 0%A_Index% FileAppend, CHAPTER%Chap_N%=%ChapStartTime%`n, temp.chapters.txt FileAppend, CHAPTER%Chap_N%NAME=Chapter %Chap_N%`n, temp.chapters.txt PlaylistMarkSectionEntryA := PlaylistMarkSectionEntryA+28 } Progress, off Msgbox, Finish ExitApp ;================ PROCEDURES and CALLS ===================================== ;=========================================================================== ;------------------ Change Sec to Chapter Time --------- SecToChapTime(decsec) { hrs := floor(decsec/60/60) if hrs < 10 hrs = 0%hrs% min := floor(decsec/60 - hrs*60) if min < 10 min = 0%min% sec := decsec - hrs*60*60 - min*60 if sec < 10 sec = 0%sec% StringLeft, sec, sec, 6 Return Hrs ":" Min ":" Sec } ;------------------ Hex to ASCII Converter --------- ; Thanks to users at: http://www.autohotkey.com/board/topic/76561-ascii-to-hex-to-ascii-again-unicode/ Hex2Text(Hex) { startpos:=1 Loop % StrLen(Hex)/2 { n .= Chr( "0x" . SubStr(Hex, StartPos+2 , 2) . SubStr(Hex, StartPos , 2) ) startpos +=4 } Return n } ;------------------ Hex to Decimal Converter --------- ; Thanks to users at: http://ahkscript.org/boards/viewtopic.php?t=3607 hexToDecimal(str){ static _0:=0,_1:=1,_2:=2,_3:=3,_4:=4,_5:=5,_6:=6,_7:=7,_8:=8,_9:=9,_a:=10,_b:=11,_c:=12,_d:=13,_e:=14,_f:=15 str:=ltrim(str,"0x `t`n`r"), len := StrLen(str), ret:=0 Loop,Parse,str ret += _%A_LoopField%*(16**(len-A_Index)) return ret } ;------------------ FileToHex --------- ; Thanks to users at: http://ahkscript.org/boards/viewtopic.php?t=1242 FileToHex: FileSelectFile, sFilePath, 3,, Select the file to convert..., Bluray Playlist (*.mpls) If ( !sFilePath ) Return FileRead, cBuf, *C %sFilePath% ; Read the file. ToHex(sHex, cBuf) ; Transform its content in Hexadecimal. HexFile := sHex Return ; Thanks to Laszlo: http://www.autohotkey.com/forum/viewtopic.php?p=131700#131700. ToHex(ByRef sHex, ByRef cBuf, nSz:=-1) { nBz := VarSetCapacity(cBuf) adr := &cBuf f := A_FormatInteger SetFormat, Integer, Hex Loop % nSz < 0 ? nBz : nSz sHex .= *adr++ SetFormat, Integer, %f% sHex := RegExReplace(sHex, "S)x(?=.0x|.$)|0x(?=..0x|..$)") } Return