[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