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

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


Hi.

Sorry for this new message. I am not familiar with git send-mail command.

This patch is about a script Lua in order to allow reading Digital 
Cinema Packages.

Use : ./vlc path/to/dcp/directory

The function probe() returns true if a "ASSETMAP" file is found in the 
DCP Directory (input).
The script parses the XML files to retrieve audio and video files.
Then the script returns a playlist with the video file to play and the 
audio file as an input-slave.

This script has been developed as part of a project in an engineering 
school.
The supervisor is Nicolas BERTRAND.

Thanks.

> ---
>   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
> +




More information about the vlc-devel mailing list