Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Bluray Playlist (MPLS) Reader (and Chapter File Creator)


  • Please log in to reply
No replies to this topic
jmone1
  • Members
  • 33 posts
  • Last active: Oct 23 2015 06:29 AM
  • Joined: 14 Jun 2014

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