[vlc-devel] [PATCH] dcp: Creation of LUA script to read DCP

Pierre Villard pierre.villard.fr at gmail.com
Tue Mar 5 17:13:02 CET 2013


---
 share/lua/playlist/dcp.lua |  330 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 330 insertions(+)
 create mode 100644 share/lua/playlist/dcp.lua

diff --git a/share/lua/playlist/dcp.lua b/share/lua/playlist/dcp.lua
new file mode 100644
index 0000000..55bfa4d
--- /dev/null
+++ b/share/lua/playlist/dcp.lua
@@ -0,0 +1,330 @@
+--[[
+ $Id$
+
+ Copyright © 2013 VideoLAN and AUTHORS
+
+ Author:
+ - Pierre VILLARD <pierre dot villard dot fr at gmail dot com>
+
+ This script has been developped as part of a project with an
+ engineering school (ENSEEIHT : www.enseeiht.fr). The aim is to
+ provide a solution to read Digital Cinema Packages. The project group
+ is composed of Claire Etienne, Aurélie Sbinné, Julien Puyobro,
+ Samuel Kerjose and Pierre Villard.
+
+ To be used, the user has to provide a directory as input data to VLC :
+ ./vlc DCP/directory/path
+
+ The probe function checks if there is a "ASSETMAP" file in the direcotry.
+
+ TODO : handle subtitles
+ TODO : add a solution to handle multiple audio files
+ FIXME : "/" and "\" conflict according to operating systems
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+--]]
+
+-- Function to check if a file exists or not
+-- We open a handler on the file, if the handler is null, the file
+-- does not exist. If the file exists, we close the handler.
+-- @param path Path to the file
+-- @return true if the file exists, false otherwise
+function is_file( path )
+    local handle = io.open( path )
+    local result = not ( nil == handle ) -- true if the file exists
+    if( result ) then -- if the file exists
+        io.close( handle )
+    end
+    return result
+end
+
+-- Function to add a slash at the end of the path if necessary
+-- /dcp/path/ => /dcp/path/
+-- /dcp/path => /dcp/path/
+-- @param path Path
+-- @return Path with a single slash at the end
+-- FIXME May be it works only on Linux operating systems ("/" and "\")
+function add_slash( path )
+    local reverse = string.reverse( path )
+    if( string.find( reverse, "/" ) == 1 ) then
+       return path
+    else
+       return path .. "/"
+    end
+end
+
+-- Probe function : this function is mandatory to make this script
+-- working with VLC. If the function returns true, it means this script
+-- is appropriate te be used in order to parse input data given by VLC.
+-- For DCP, we return true is there is an "ASSETMAP" file in the directory
+-- @return true if there is an "ASSETMAP" file in the directory
+function probe()
+    local full_path = add_slash( vlc.path )
+
+    -- possibilites to write "ASSETMAP" file name
+    -- there is no accurate specification
+    local path_lower_xml = full_path .. "assetmap.xml"
+    local path_lower_xml_upper = full_path .. "assetmap.XML"
+    local path_lower_notxml = full_path .. "assetmap"
+    local path_upper_xml = full_path .. "ASSETMAP.xml"
+    local path_upper_xml_upper = full_path .. "ASSETMAP.XML"
+    local path_upper_notxml = full_path .. "ASSETMAP"
+
+    -- Debug purpose
+    -- vlc.msg.dbg( "DCP Probe function with : " .. vlc.path )
+
+    local res = is_file( path_lower_xml ) or
+           is_file( path_lower_xml_upper ) or
+           is_file( path_lower_notxml ) or
+           is_file( path_upper_xml ) or
+           is_file( path_upper_xml_upper ) or
+           is_file( path_upper_notxml )
+
+    -- Debug purpose
+    -- if res then
+    --     vlc.msg.dbg( "Probe function : OK" )
+    -- else
+    --     vlc.msg.dbg( "Probe function : KO (you should consider the option --demux=any or modify your vlcrc file)" )
+    -- end
+
+    return res
+end
+
+-- Function to get the path to the ASSETMAP file
+-- @param path Path of the DCP directory
+-- @return Path to the ASSETMAP file
+function get_assetmap( path )
+    -- possibilites to write "ASSETMAP" file name
+    -- there is no accurate specification
+    local path_lower_xml = path .. "assetmap.xml"
+    local path_lower_xml_upper = path .. "assetmap.XML"
+    local path_lower_notxml = path .. "assetmap"
+    local path_upper_xml = path .. "ASSETMAP.xml"
+    local path_upper_xml_upper = path .. "ASSETMAP.XML"
+    local path_upper_notxml = path .. "ASSETMAP"
+
+    if( is_file( path_lower_xml ) ) then
+        return path_lower_xml
+    elseif( is_file( path_lower_xml_upper ) ) then
+        return path_lower_xml_upper
+    elseif( is_file( path_lower_notxml ) ) then
+        return path_lower_notxml
+    elseif( is_file( path_upper_xml ) ) then
+        return path_upper_xml
+    elseif( is_file( path_upper_xml_upper ) ) then
+        return path_upper_xml_upper
+    else
+        return path_upper_notxml
+    end
+end
+
+-- Function used to remove all the spaces in the line before a
+-- given tag. Ex : ("       <Id>foo</Id>", "<Id>") => "<Id>foo</Id>"
+-- This function has been created to prevent deletion of spaces in
+-- file names. Ex : "      <Path>my video.mxf</Path>"
+-- @param line Text to clear
+-- @param start Tag delimiter
+-- @return Text cleared of spaces before the tag
+function remove_spaces_before( line, start )
+    local nb = string.find( line, start )
+    local text, occ = string.gsub( line, "%s", "", nb - 1 )
+    return text
+end
+
+-- Function used to parse ASSETMAP file
+-- We parse the file and we store in a table information
+-- about the files described in the ASSETMAP file.
+-- We only take into account .xml files (CPL and PKL files)
+-- and .mxf files (Video, Audio and subtitles [TODO : handle subtitles])
+-- @param path Path to the ASSETMAP file
+-- @return Table with information about files (ID, Path and Type=PKL|CPL|MXF)
+-- @return Name of the CPL file
+function parse_assetmap( path )
+    assetlist = false
+    pklfile = false
+    cpl_path = nil
+    files = {}
+
+    -- we parse the file line by line
+    -- this operation closes the file at the end of the for statement
+    for line in io.lines( path ) do
+
+       -- we only consider files between <AssetList> and </AssetList>
+        if( string.match( line, "AssetList" ) ) then
+            assetlist = not( assetlist )
+        end
+
+        if( assetlist ) then
+
+            -- This block determines if we currently are collection
+            -- information about the PKL file : because this file is the
+            -- only one to be described with <PackingList>true</PackingList>
+            if( string.match( line, "PackingList" ) ) then
+                pklfile = true
+            end
+            if( string.match( line, "Asset" ) and pklfile ) then
+                pklfile = false
+            end
+
+            -- This block determines the ID of the file
+            if( string.match( line, "<Id>" ) ) then
+                text = remove_spaces_before( line, "<Id>" )
+                id_file = string.sub( text, 5, -6 )
+            end
+
+            -- This block determines the Path of the file
+            if( string.match( line, "<Path>" ) ) then
+                text = remove_spaces_before( line, "<Path>" )
+                path_file = string.sub( text, 7, -8 )
+
+                -- If the file is a XML file
+                if( string.match( path_file, ".xml" ) or string.match( path_file, ".XML" ) ) then
+                    if( pklfile ) then -- If it is the PKL file (for the type)
+                        table.insert( files, { type_file = "PKL",
+                                               path = path_file,
+                                               id = id_file } )
+                    else -- otherwise it is the CPL file (for the type)
+                        table.insert( files, { type_file = "CPL",
+                                               path = path_file,
+                                               id = id_file } )
+                        cpl_path = path_file
+                    end
+                -- If the file is a MXF file
+                elseif( string.match( path_file, ".mxf" ) or string.match( path_file, ".MXF" ) ) then
+                    table.insert( files, { type_file = "MXF",
+                                           path = path_file,
+                                           id = id_file } )
+                else -- otherwise Error Message with ID and Path
+                    vlc.msg.err( id_file .. " - " .. path_file )
+                end
+            end
+
+        end
+    end
+    return files, cpl_path -- return table of information and CPL file name
+end
+
+-- Function used to parse CPL file
+-- We parse the file in order to get the ID of the Video and the Audio
+-- files (TODO : handle subtitles). We also get the title of the content which
+-- will be displayed by VLC. Video file is described between <MainPicture> and
+-- </MainPicture> and Audio file is described between <MainSound> and </MainSound>
+-- Title is given between <ContentTitleText> and </ContentTitleText>
+-- @param path Path to the CPL file
+-- @return ID of the video file
+-- @return ID of the audio file
+-- @return Title to display for the content
+function parse_cplfile( cpl_path )
+    is_main_picture = false
+    is_main_sound = false
+    main_picture_id = nil
+    main_sound_id = nil
+    title = nil
+
+    -- We parse the file line by line
+    for line in io.lines( cpl_path ) do
+
+        -- This block determines if we are between <MainPicture> and </MainPicture>
+        if( string.match( line, "MainPicture" ) ) then
+            is_main_picture = not ( is_main_picture )
+        end
+
+        -- This block determines if we are between <MainSound> and </MainSound>
+        if( string.match( line, "MainSound" ) ) then
+            is_main_sound = not ( is_main_sound )
+        end
+
+        -- We get the ID of the file if it is about audio or video file
+        if( string.match( line, "<Id>" ) and ( is_main_sound or is_main_picture ) ) then
+            -- we remove spaces in the line
+            text = remove_spaces_before( line, "<Id>" )
+            id_file = string.sub( text, 5, -6 )
+            if( is_main_picture ) then
+                main_picture_id = id_file
+            else
+                main_sound_id = id_file
+            end
+        end
+
+        -- We get the title to display
+        if( string.match( line, "ContentTitleText" ) ) then
+            text = remove_spaces_before( line, "<ContentTitleText>" )
+            title = string.sub( text, 19, -20 )
+        end
+    end
+    return main_picture_id, main_sound_id, title
+    -- return ID of video file and audio file and title to display
+end
+
+-- Function to get the Name information corresponding to the given ID
+-- @param files Tables of information
+-- @param id ID to find in the table
+-- @return Name of the file corresponding to the given ID
+function get_name_for_id( files, id )
+    for v = 1, table.getn( files ), 1 do
+        id_table =  files[v].id
+        path_table = files[v].path
+        if( id_table == id ) then
+            return path_table
+        end
+    end
+end
+
+-- Parse function : this function is mandatory to make this script
+-- working with VLC. This function parses input data to return the
+-- playlist VLC has to play with the correct parameters. In this
+-- case we return one track, the video, with a parameter, the audio
+-- file as an input-slave. TODO : handle subtitles
+-- @return Playlist to play
+function parse()
+    -- We get the full path (with a "/" at the end)
+    local full_path = add_slash( vlc.path )
+
+    -- We get the path to the ASSETMAP file
+    local assetmap = get_assetmap( full_path )
+
+    -- We parse the file to get information table about the files
+    -- and to get the CPL file name
+    files, cpl_name = parse_assetmap( assetmap )
+
+    -- We get the path to the CPL file
+    cpl_path = full_path .. cpl_name
+
+    -- We parse the CPL file to get IDs of Video and Audio files, and title to display
+    main_picture_id, main_sound_id, title_to_display = parse_cplfile( cpl_path )
+
+    -- We retrieve file names for Audio and Video in the table
+    main_picture_name = get_name_for_id( files, main_picture_id )
+    main_sound_name = get_name_for_id( files, main_sound_id )
+
+    -- Debug purpose
+    vlc.msg.dbg( "Video file path = " .. full_path .. main_picture_name )
+    vlc.msg.dbg( "Audio file path = " .. full_path .. main_sound_name )
+
+    -- We get the paths to video and audio files
+    local video = "file://" .. full_path .. main_picture_name
+    local audio = "file://" .. full_path .. main_sound_name
+
+    -- We create the playlist to play
+    -- With a specific option 'input-slave' for the audio
+    local tracks = {}
+            table.insert( tracks, { path = video,
+                                    title = title_to_display,
+                                    arturl = nil,
+                                    options = {':input-slave=' .. audio} } )
+
+    return tracks -- we return the playlist
+end
+
-- 
1.7.9.5




More information about the vlc-devel mailing list