1 /*****************************************************************************
2  * stream_extractor.c
3  *****************************************************************************
4  * Copyright (C) 2016 VLC authors and VideoLAN
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20 
21 #ifndef STREAM_EXTRACTOR_H
22 #define STREAM_EXTRACTOR_H
23 
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27 
28 #include <vlc_common.h>
29 #include <vlc_stream.h>
30 #include <vlc_stream_extractor.h>
31 #include <vlc_modules.h>
32 #include <vlc_url.h>
33 #include <vlc_memstream.h>
34 #include <libvlc.h>
35 #include <assert.h>
36 
37 #include "stream.h"
38 #include "mrl_helpers.h"
39 
40 /**
41  * \defgroup stream_extractor_Internals Stream Extractor Internals
42  * \ingroup stream_extractor
43  * \internal
44  * @{
45  * \file
46  **/
47 
48 struct stream_extractor_private {
49     union {
50         stream_extractor_t extractor;
51         stream_directory_t directory;
52     };
53 
54     /**
55      * Callback to handle initialization
56      *
57      * \ref pf_init will be called after successful module probing to initialize
58      * the relevant members of the underlying stream-extractor object, as well
59      * as the wrapping stream.
60      **/
61     int (*pf_init)( struct stream_extractor_private*, stream_t* );
62 
63     /**
64      * Callback to handle clean-up
65      *
66      * \ref pf_clean, unless NULL, will be called when the stream-extractor is to
67      * be destroyed, and shall be used to clean-up resources (acquired during
68      * initialization, see \ref pf_init).
69      */
70     void (*pf_clean)( struct stream_extractor_private* );
71 
72     stream_t* wrapper; /**< the wrapping \ref stream_t used to access the
73                             underlying stream-extractor */
74 
75     stream_t* source; /**< the source stream consumed by the stream-extractor */
76     module_t* module; /**< the stream-extractor module */
77 
78     vlc_object_t* object; /**< the underlying stream-extractor object */
79 };
80 
81 /**
82  * Create an MRL for a specific sub-entry
83  *
84  * This internal function is used to create an MRL that refers to \subentry
85  * within \ref base, see \ref mrl_helpers for further information.
86  **/
87 
88 static char*
StreamExtractorCreateMRL(char const * base,char const * subentry)89 StreamExtractorCreateMRL( char const* base, char const* subentry )
90 {
91     struct vlc_memstream buffer;
92     char* escaped;
93 
94     if( mrl_EscapeFragmentIdentifier( &escaped, subentry ) )
95         return NULL;
96 
97     if( vlc_memstream_open( &buffer ) )
98     {
99         free( escaped );
100         return NULL;
101     }
102 
103     vlc_memstream_puts( &buffer, base );
104 
105     if( !strstr( base, "#" ) )
106         vlc_memstream_putc( &buffer, '#' );
107 
108     vlc_memstream_printf( &buffer, "!/%s", escaped );
109 
110     free( escaped );
111     return vlc_memstream_close( &buffer ) ? NULL : buffer.ptr;
112 }
113 
114 /**
115  * Release the private data associated with a stream-extractor
116  * \param priv pointer to the private section
117  */
se_Release(struct stream_extractor_private * priv)118 static void se_Release( struct stream_extractor_private* priv )
119 {
120     if( priv->pf_clean )
121         priv->pf_clean( priv );
122 
123     if( priv->module )
124     {
125         module_unneed( priv->object, priv->module );
126 
127         if( priv->source )
128             vlc_stream_Delete( priv->source );
129     }
130 
131     vlc_object_release( priv->object );
132 }
133 
134 /**
135  * \name Callbacks to forward work to the underlying stream-extractor
136  *
137  * @{
138  */
139 
140 static void
se_StreamDelete(stream_t * stream)141 se_StreamDelete( stream_t* stream )
142 {
143     se_Release( stream->p_sys );
144 }
145 
146 static ssize_t
se_StreamRead(stream_t * stream,void * buf,size_t len)147 se_StreamRead( stream_t* stream, void* buf, size_t len )
148 {
149     struct stream_extractor_private* priv = stream->p_sys;
150     return priv->extractor.pf_read( &priv->extractor, buf, len );
151 }
152 
153 static block_t*
se_StreamBlock(stream_t * stream,bool * eof)154 se_StreamBlock( stream_t* stream, bool* eof )
155 {
156     struct stream_extractor_private* priv = stream->p_sys;
157     return priv->extractor.pf_block( &priv->extractor, eof );
158 }
159 
160 static int
se_StreamSeek(stream_t * stream,uint64_t offset)161 se_StreamSeek( stream_t* stream, uint64_t offset )
162 {
163     struct stream_extractor_private* priv = stream->p_sys;
164     return priv->extractor.pf_seek( &priv->extractor, offset );
165 }
166 
167 static int
se_ReadDir(stream_t * stream,input_item_node_t * node)168 se_ReadDir( stream_t* stream, input_item_node_t* node )
169 {
170     struct stream_extractor_private* priv = stream->p_sys;
171     return priv->directory.pf_readdir( &priv->directory, node );
172 }
173 
174 static int
se_StreamControl(stream_t * stream,int req,va_list args)175 se_StreamControl( stream_t* stream, int req, va_list args )
176 {
177     struct stream_extractor_private* priv = stream->p_sys;
178     return priv->extractor.pf_control( &priv->extractor, req, args );
179 }
180 
181 static int
se_DirControl(stream_t * stream,int req,va_list args)182 se_DirControl( stream_t* stream, int req, va_list args )
183 {
184     (void)stream;
185     (void)args;
186 
187     if( req == STREAM_IS_DIRECTORY )
188         return VLC_SUCCESS;
189 
190     return VLC_EGENERIC;
191 }
192 
193 /**
194  * @}
195  **/
196 
197 /**
198  * \name stream-extractor resource handlers
199  * \ingroup stream_extractor
200  * @{
201  */
202 
203 static int
se_InitStream(struct stream_extractor_private * priv,stream_t * s)204 se_InitStream( struct stream_extractor_private* priv, stream_t* s )
205 {
206     if( priv->extractor.pf_read ) s->pf_read = se_StreamRead;
207     else                          s->pf_block = se_StreamBlock;
208 
209     s->pf_seek = se_StreamSeek;
210     s->pf_control = se_StreamControl;
211     s->psz_url = StreamExtractorCreateMRL( priv->extractor.source->psz_url,
212                                            priv->extractor.identifier );
213     if( unlikely( !s->psz_url ) )
214         return VLC_ENOMEM;
215 
216     return VLC_SUCCESS;
217 }
218 
219 static void
se_CleanStream(struct stream_extractor_private * priv)220 se_CleanStream( struct stream_extractor_private* priv )
221 {
222     free( (char*)priv->extractor.identifier );
223 }
224 
225 static int
se_InitDirectory(struct stream_extractor_private * priv,stream_t * s)226 se_InitDirectory( struct stream_extractor_private* priv, stream_t* s )
227 {
228     stream_directory_t* directory = &priv->directory;
229 
230     s->pf_readdir = se_ReadDir;
231     s->pf_control = se_DirControl;
232     s->psz_url = strdup( directory->source->psz_url );
233 
234     if( unlikely( !s->psz_url ) )
235         return VLC_EGENERIC;
236 
237     return VLC_SUCCESS;
238 }
239 
240 /**
241  * @}
242  **/
243 
244 /**
245  * Create the public stream_t that wraps a stream-extractor
246  *
247  * This initializes the relevant data-members of the public stream_t which is
248  * used to read from the underlying stream-extractor.
249  *
250  * \param priv the private section of the stream_extractor_t
251  * \param source the source stream which the stream_extractor_t should
252  *        will read from
253  * \return VLC_SUCCESS on success, an error-code on failure.
254  **/
255 static int
se_AttachWrapper(struct stream_extractor_private * priv,stream_t * source)256 se_AttachWrapper( struct stream_extractor_private* priv, stream_t* source )
257 {
258     stream_t* s = vlc_stream_CommonNew( source->obj.parent, se_StreamDelete );
259 
260     if( unlikely( !s ) )
261         return VLC_ENOMEM;
262 
263     if( priv->pf_init( priv, s ) )
264     {
265         stream_CommonDelete( s );
266         return VLC_EGENERIC;
267     }
268 
269     priv->wrapper = s;
270     priv->wrapper->p_input = source->p_input;
271     priv->wrapper->p_sys = priv;
272 
273     priv->source = source;
274 
275     if( priv->wrapper->pf_read )
276         priv->wrapper = stream_FilterChainNew( priv->wrapper, "cache_read" );
277     else if( priv->wrapper->pf_block )
278         priv->wrapper = stream_FilterChainNew( priv->wrapper, "cache_block" );
279 
280     return VLC_SUCCESS;
281 }
282 
283 static int
StreamExtractorAttach(stream_t ** source,char const * identifier,char const * module_name)284 StreamExtractorAttach( stream_t** source, char const* identifier,
285     char const* module_name )
286 {
287     const bool extractor = identifier != NULL;
288     char const* capability = extractor ? "stream_extractor"
289                                        : "stream_directory";
290 
291     struct stream_extractor_private* priv = vlc_custom_create(
292         (*source)->obj.parent, sizeof( *priv ), capability );
293 
294     if( unlikely( !priv ) )
295         return VLC_ENOMEM;
296 
297     if( extractor )
298     {
299         priv->object = VLC_OBJECT( &priv->extractor );
300 
301         priv->pf_init = se_InitStream;
302         priv->pf_clean = se_CleanStream;
303 
304         priv->extractor.source = *source;
305         priv->extractor.identifier = strdup( identifier );
306 
307         if( unlikely( !priv->extractor.identifier ) )
308             goto error;
309     }
310     else
311     {
312         priv->object = VLC_OBJECT( &priv->directory );
313 
314         priv->pf_init = se_InitDirectory;
315         priv->pf_clean = NULL;
316 
317         priv->directory.source = *source;
318     }
319 
320     priv->module = module_need( priv->object, capability, module_name, true );
321 
322     if( !priv->module || se_AttachWrapper( priv, *source ) )
323         goto error;
324 
325     *source = priv->wrapper;
326     return VLC_SUCCESS;
327 
328 error:
329     se_Release( priv );
330     return VLC_EGENERIC;
331 }
332 
333 int
vlc_stream_directory_Attach(stream_t ** source,char const * module_name)334 vlc_stream_directory_Attach( stream_t** source, char const* module_name )
335 {
336     return StreamExtractorAttach( source, NULL, module_name );
337 }
338 
339 int
vlc_stream_extractor_Attach(stream_t ** source,char const * identifier,char const * module_name)340 vlc_stream_extractor_Attach( stream_t** source, char const* identifier,
341                              char const* module_name )
342 {
343     return StreamExtractorAttach( source, identifier, module_name );
344 }
345 
346 int
stream_extractor_AttachParsed(stream_t ** source,char const * data,char const ** out_extra)347 stream_extractor_AttachParsed( stream_t** source, char const* data,
348                                char const** out_extra )
349 {
350     vlc_array_t identifiers;
351 
352     if( mrl_FragmentSplit( &identifiers, out_extra, data ) )
353         return VLC_EGENERIC;
354 
355     size_t count = vlc_array_count( &identifiers );
356     size_t idx = 0;
357 
358     while( idx < count )
359     {
360         char* id = vlc_array_item_at_index( &identifiers, idx );
361 
362         if( vlc_stream_extractor_Attach( source, id, NULL ) )
363             break;
364 
365         ++idx;
366     }
367 
368     for( size_t i = 0; i < count; ++i )
369         free( vlc_array_item_at_index( &identifiers, i ) );
370     vlc_array_clear( &identifiers );
371 
372     return idx == count ? VLC_SUCCESS : VLC_EGENERIC;
373 }
374 
375 char*
vlc_stream_extractor_CreateMRL(stream_directory_t * directory,char const * subentry)376 vlc_stream_extractor_CreateMRL( stream_directory_t* directory,
377                                 char const* subentry )
378 {
379     return StreamExtractorCreateMRL( directory->source->psz_url, subentry );
380 }
381 
382 /**
383  * @}
384  **/
385 
386 #endif /* include-guard */
387