1 /*****************************************************************************
2  * qtl.c: QuickTime Media Link Importer
3  *****************************************************************************
4  * Copyright (C) 2006 VLC authors and VideoLAN
5  * $Id: d24e8b9ea5de6a2d260d68212a9146cad70e189c $
6  *
7  * Authors: Antoine Cellerier <dionoea -@t- videolan -Dot- org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23 
24 /*
25 See
26 http://developer.apple.com/documentation/QuickTime/QT6WhatsNew/Chap1/chapter_1_section_54.html
27 and
28 http://developer.apple.com/documentation/QuickTime/WhatsNewQT5/QT5NewChapt1/chapter_1_section_39.html
29 
30 autoplay - true/false
31 controller - true/false
32 fullscreen - normal/double/half/current/full
33 href - url
34 kioskmode - true/false
35 loop - true/false/palindrome
36 movieid - integer
37 moviename - string
38 playeveryframe - true/false
39 qtnext - url
40 quitwhendone - true/false
41 src - url (required)
42 type - mime type
43 volume - 0 (mute) - 100 (max)
44 
45 */
46 
47 /*****************************************************************************
48  * Preamble
49  *****************************************************************************/
50 
51 #ifdef HAVE_CONFIG_H
52 # include "config.h"
53 #endif
54 
55 #include <vlc_common.h>
56 #include <vlc_access.h>
57 
58 #include "playlist.h"
59 #include <vlc_xml.h>
60 #include <vlc_strings.h>
61 
62 typedef enum { FULLSCREEN_NORMAL,
63                FULLSCREEN_DOUBLE,
64                FULLSCREEN_HALF,
65                FULLSCREEN_CURRENT,
66                FULLSCREEN_FULL } qtl_fullscreen_t;
67 const char* ppsz_fullscreen[] = { "normal", "double", "half", "current", "full" };
68 typedef enum { LOOP_TRUE,
69                LOOP_FALSE,
70                LOOP_PALINDROME } qtl_loop_t;
71 const char* ppsz_loop[] = { "true", "false", "palindrome" };
72 
73 #define ROOT_NODE_MAX_DEPTH 2
74 
75 /*****************************************************************************
76  * Local prototypes
77  *****************************************************************************/
78 static int ReadDir( stream_t *, input_item_node_t * );
79 
80 /*****************************************************************************
81  * Import_QTL: main import function
82  *****************************************************************************/
Import_QTL(vlc_object_t * p_this)83 int Import_QTL( vlc_object_t *p_this )
84 {
85     stream_t *p_demux = (stream_t *)p_this;
86 
87     CHECK_FILE(p_demux);
88     if( !stream_HasExtension( p_demux, ".qtl" ) )
89         return VLC_EGENERIC;
90 
91     p_demux->pf_readdir = ReadDir;
92     p_demux->pf_control = access_vaDirectoryControlHelper;
93     msg_Dbg( p_demux, "using QuickTime Media Link reader" );
94 
95     return VLC_SUCCESS;
96 }
97 
ReadDir(stream_t * p_demux,input_item_node_t * p_subitems)98 static int ReadDir( stream_t *p_demux, input_item_node_t *p_subitems )
99 {
100     xml_reader_t *p_xml_reader;
101     input_item_t *p_input;
102     int i_ret = -1;
103 
104     /* List of all possible attributes. The only required one is "src" */
105     bool b_autoplay = false;
106     bool b_controller = true;
107     qtl_fullscreen_t fullscreen = false;
108     char *psz_href = NULL;
109     bool b_kioskmode = false;
110     qtl_loop_t loop = LOOP_FALSE;
111     int i_movieid = -1;
112     char *psz_moviename = NULL;
113     bool b_playeveryframe = false;
114     char *psz_qtnext = NULL;
115     bool b_quitwhendone = false;
116     char *psz_src = NULL;
117     char *psz_mimetype = NULL;
118     int i_volume = 100;
119 
120     p_xml_reader = xml_ReaderCreate( p_demux, p_demux->p_source );
121     if( !p_xml_reader )
122         goto error;
123 
124     for( int i = 0;; ++i ) /* locate root node */
125     {
126         const char *node;
127         if( i == ROOT_NODE_MAX_DEPTH ||
128             xml_ReaderNextNode( p_xml_reader, &node ) != XML_READER_STARTELEM )
129         {
130             msg_Err( p_demux, "unable to locate root-node" );
131             goto error;
132         }
133 
134         if( strcmp( node, "embed" ) == 0 )
135             break; /* found it */
136 
137         msg_Dbg( p_demux, "invalid root node <%s>, trying next (%d / %d)",
138                            node, i + 1, ROOT_NODE_MAX_DEPTH );
139     }
140 
141     const char *attrname, *value;
142     while( (attrname = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
143     {
144         if( !strcmp( attrname, "autoplay" ) )
145             b_autoplay = !strcmp( value, "true" );
146         else if( !strcmp( attrname, "controller" ) )
147             b_controller = !strcmp( attrname, "false" );
148         else if( !strcmp( attrname, "fullscreen" ) )
149         {
150             if( !strcmp( value, "double" ) )
151                 fullscreen = FULLSCREEN_DOUBLE;
152             else if( !strcmp( value, "half" ) )
153                 fullscreen = FULLSCREEN_HALF;
154             else if( !strcmp( value, "current" ) )
155                 fullscreen = FULLSCREEN_CURRENT;
156             else if( !strcmp( value, "full" ) )
157                 fullscreen = FULLSCREEN_FULL;
158             else
159                 fullscreen = FULLSCREEN_NORMAL;
160         }
161         else if( !strcmp( attrname, "href" ) )
162         {
163             free( psz_href );
164             psz_href = strdup( value );
165         }
166         else if( !strcmp( attrname, "kioskmode" ) )
167             b_kioskmode = !strcmp( value, "true" );
168         else if( !strcmp( attrname, "loop" ) )
169         {
170             if( !strcmp( value, "true" ) )
171                 loop = LOOP_TRUE;
172             else if( !strcmp( value, "palindrome" ) )
173                 loop = LOOP_PALINDROME;
174             else
175                 loop = LOOP_FALSE;
176         }
177         else if( !strcmp( attrname, "movieid" ) )
178             i_movieid = atoi( value );
179         else if( !strcmp( attrname, "moviename" ) )
180         {
181             free( psz_moviename );
182             psz_moviename = strdup( value );
183         }
184         else if( !strcmp( attrname, "playeveryframe" ) )
185             b_playeveryframe = !strcmp( value, "true" );
186         else if( !strcmp( attrname, "qtnext" ) )
187         {
188             free( psz_qtnext );
189             psz_qtnext = strdup( value );
190         }
191         else if( !strcmp( attrname, "quitwhendone" ) )
192             b_quitwhendone = !strcmp( value, "true" );
193         else if( !strcmp( attrname, "src" ) )
194         {
195             free( psz_src );
196             psz_src = strdup( value );
197         }
198         else if( !strcmp( attrname, "mimetype" ) )
199         {
200             free( psz_mimetype );
201             psz_mimetype = strdup( value );
202         }
203         else if( !strcmp( attrname, "volume" ) )
204             i_volume = atoi( value );
205         else
206             msg_Dbg( p_demux, "Attribute %s with value %s isn't valid",
207                      attrname, value );
208     }
209 
210     msg_Dbg( p_demux, "autoplay: %s (unused by VLC)",
211              b_autoplay ? "true": "false" );
212     msg_Dbg( p_demux, "controller: %s (unused by VLC)",
213              b_controller ? "true": "false" );
214     msg_Dbg( p_demux, "fullscreen: %s (unused by VLC)",
215              ppsz_fullscreen[fullscreen] );
216     msg_Dbg( p_demux, "href: %s", psz_href );
217     msg_Dbg( p_demux, "kioskmode: %s (unused by VLC)",
218              b_kioskmode ? "true":"false" );
219     msg_Dbg( p_demux, "loop: %s (unused by VLC)", ppsz_loop[loop] );
220     msg_Dbg( p_demux, "movieid: %d (unused by VLC)", i_movieid );
221     msg_Dbg( p_demux, "moviename: %s", psz_moviename );
222     msg_Dbg( p_demux, "playeverframe: %s (unused by VLC)",
223              b_playeveryframe ? "true":"false" );
224     msg_Dbg( p_demux, "qtnext: %s", psz_qtnext );
225     msg_Dbg( p_demux, "quitwhendone: %s (unused by VLC)",
226              b_quitwhendone ? "true":"false" );
227     msg_Dbg( p_demux, "src: %s", psz_src );
228     msg_Dbg( p_demux, "mimetype: %s", psz_mimetype );
229     msg_Dbg( p_demux, "volume: %d (unused by VLC)", i_volume );
230 
231 
232     if( !psz_src )
233     {
234         msg_Err( p_demux, "Mandatory attribute 'src' not found" );
235     }
236     else
237     {
238         p_input = input_item_New( psz_src, psz_moviename );
239 #define SADD_INFO( type, field ) if( field ) { input_item_AddInfo( \
240                     p_input, "QuickTime Media Link", type, "%s", field ) ; }
241         SADD_INFO( "href", psz_href );
242         SADD_INFO( _("Mime"), psz_mimetype );
243         input_item_node_AppendItem( p_subitems, p_input );
244         input_item_Release( p_input );
245         if( psz_qtnext )
246         {
247             vlc_xml_decode( psz_qtnext );
248             p_input = input_item_New( psz_qtnext, NULL );
249             input_item_node_AppendItem( p_subitems, p_input );
250             input_item_Release( p_input );
251         }
252     }
253 
254     i_ret = 0; /* Needed for correct operation of go back */
255 
256 error:
257     if( p_xml_reader )
258         xml_ReaderDelete( p_xml_reader );
259 
260     free( psz_href );
261     free( psz_moviename );
262     free( psz_qtnext );
263     free( psz_src );
264     free( psz_mimetype );
265     return i_ret;
266 }
267