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