/src/vlc/src/input/stream_extractor.c
Line | Count | Source |
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_arrays.h> |
30 | | #include <vlc_stream.h> |
31 | | #include <vlc_stream_extractor.h> |
32 | | #include <vlc_modules.h> |
33 | | #include <vlc_url.h> |
34 | | #include <vlc_memstream.h> |
35 | | #include "../libvlc.h" |
36 | | #include <assert.h> |
37 | | |
38 | | #include "stream.h" |
39 | | #include "mrl_helpers.h" |
40 | | |
41 | | /** |
42 | | * \defgroup stream_extractor_Internals Stream Extractor Internals |
43 | | * \ingroup stream_extractor |
44 | | * \internal |
45 | | * @{ |
46 | | * \file |
47 | | **/ |
48 | | |
49 | | struct stream_extractor_private { |
50 | | union { |
51 | | stream_extractor_t extractor; |
52 | | stream_directory_t directory; |
53 | | }; |
54 | | |
55 | | /** |
56 | | * Callback to handle initialization |
57 | | * |
58 | | * \ref pf_init will be called after successful module probing to initialize |
59 | | * the relevant members of the underlying stream-extractor object, as well |
60 | | * as the wrapping stream. |
61 | | **/ |
62 | | int (*pf_init)( struct stream_extractor_private*, stream_t* ); |
63 | | |
64 | | /** |
65 | | * Callback to handle clean-up |
66 | | * |
67 | | * \ref pf_clean, unless NULL, will be called when the stream-extractor is to |
68 | | * be destroyed, and shall be used to clean-up resources (acquired during |
69 | | * initialization, see \ref pf_init). |
70 | | */ |
71 | | void (*pf_clean)( struct stream_extractor_private* ); |
72 | | |
73 | | stream_t* wrapper; /**< the wrapping \ref stream_t used to access the |
74 | | underlying stream-extractor */ |
75 | | |
76 | | stream_t* source; /**< the source stream consumed by the stream-extractor */ |
77 | | module_t* module; /**< the stream-extractor module */ |
78 | | |
79 | | vlc_object_t* object; /**< the underlying stream-extractor object */ |
80 | | }; |
81 | | |
82 | | /** |
83 | | * Create an MRL for a specific sub-entry |
84 | | * |
85 | | * This internal function is used to create an MRL that refers to \p subentry |
86 | | * within \p base, see \ref mrl_helpers for further information. |
87 | | **/ |
88 | | |
89 | | VLC_MALLOC static char* |
90 | | StreamExtractorCreateMRL( char const* base, char const* subentry, |
91 | | char const** volumes, size_t volumes_count ) |
92 | 0 | { |
93 | 0 | struct vlc_memstream buffer; |
94 | 0 | char* escaped; |
95 | |
|
96 | 0 | escaped = mrl_EscapeFragmentIdentifier( subentry ); |
97 | 0 | if ( escaped == NULL ) |
98 | 0 | return NULL; |
99 | | |
100 | 0 | if( vlc_memstream_open( &buffer ) ) |
101 | 0 | { |
102 | 0 | free( escaped ); |
103 | 0 | return NULL; |
104 | 0 | } |
105 | | |
106 | 0 | vlc_memstream_puts( &buffer, base ); |
107 | |
|
108 | 0 | if( !strstr( base, "#" ) ) |
109 | 0 | vlc_memstream_putc( &buffer, '#' ); |
110 | |
|
111 | 0 | vlc_memstream_puts( &buffer, "!/" ); |
112 | 0 | vlc_memstream_puts( &buffer, escaped ); |
113 | |
|
114 | 0 | for( size_t i=0; i<volumes_count; i++ ) |
115 | 0 | { |
116 | 0 | vlc_memstream_puts( &buffer, "!+" ); |
117 | 0 | vlc_memstream_puts( &buffer, volumes[i] ); |
118 | 0 | } |
119 | |
|
120 | 0 | free( escaped ); |
121 | 0 | return vlc_memstream_close( &buffer ) ? NULL : buffer.ptr; |
122 | 0 | } |
123 | | |
124 | | /** |
125 | | * Release the private data associated with a stream-extractor |
126 | | * \param priv pointer to the private section |
127 | | */ |
128 | | static void se_Release( struct stream_extractor_private* priv ) |
129 | 0 | { |
130 | 0 | if( priv->pf_clean ) |
131 | 0 | priv->pf_clean( priv ); |
132 | |
|
133 | 0 | if( priv->module ) |
134 | 0 | { |
135 | 0 | module_unneed( priv->object, priv->module ); |
136 | |
|
137 | 0 | if( priv->source ) |
138 | 0 | vlc_stream_Delete( priv->source ); |
139 | 0 | } |
140 | |
|
141 | 0 | vlc_object_delete(priv->object); |
142 | 0 | } |
143 | | |
144 | | /** |
145 | | * \name Callbacks to forward work to the underlying stream-extractor |
146 | | * |
147 | | * @{ |
148 | | */ |
149 | | |
150 | | static void |
151 | | se_StreamDelete( stream_t* stream ) |
152 | 0 | { |
153 | 0 | se_Release( stream->p_sys ); |
154 | 0 | } |
155 | | |
156 | | static ssize_t |
157 | | se_StreamRead( stream_t* stream, void* buf, size_t len ) |
158 | 0 | { |
159 | 0 | struct stream_extractor_private* priv = stream->p_sys; |
160 | 0 | return priv->extractor.pf_read( &priv->extractor, buf, len ); |
161 | 0 | } |
162 | | |
163 | | static block_t* |
164 | | se_StreamBlock( stream_t* stream, bool* restrict eof ) |
165 | 0 | { |
166 | 0 | struct stream_extractor_private* priv = stream->p_sys; |
167 | 0 | return priv->extractor.pf_block( &priv->extractor, eof ); |
168 | 0 | } |
169 | | |
170 | | static int |
171 | | se_StreamSeek( stream_t* stream, uint64_t offset ) |
172 | 0 | { |
173 | 0 | struct stream_extractor_private* priv = stream->p_sys; |
174 | 0 | return priv->extractor.pf_seek( &priv->extractor, offset ); |
175 | 0 | } |
176 | | |
177 | | static int |
178 | | se_ReadDir( stream_t* stream, input_item_node_t* node ) |
179 | 0 | { |
180 | 0 | struct stream_extractor_private* priv = stream->p_sys; |
181 | 0 | return priv->directory.pf_readdir( &priv->directory, node ); |
182 | 0 | } |
183 | | |
184 | | static int |
185 | | se_StreamControl( stream_t* stream, int req, va_list args ) |
186 | 0 | { |
187 | 0 | struct stream_extractor_private* priv = stream->p_sys; |
188 | 0 | return priv->extractor.pf_control( &priv->extractor, req, args ); |
189 | 0 | } |
190 | | |
191 | | static int |
192 | | se_DirControl( stream_t* stream, int req, va_list args ) |
193 | 0 | { |
194 | 0 | (void)stream; |
195 | 0 | (void)req; |
196 | 0 | (void)args; |
197 | |
|
198 | 0 | return VLC_EGENERIC; |
199 | 0 | } |
200 | | |
201 | | /** |
202 | | * @} |
203 | | **/ |
204 | | |
205 | | /** |
206 | | * \name stream-extractor resource handlers |
207 | | * \ingroup stream_extractor |
208 | | * @{ |
209 | | */ |
210 | | |
211 | | static int |
212 | | se_InitStream( struct stream_extractor_private* priv, stream_t* s ) |
213 | 0 | { |
214 | 0 | if( priv->extractor.pf_read ) s->pf_read = se_StreamRead; |
215 | 0 | else s->pf_block = se_StreamBlock; |
216 | |
|
217 | 0 | s->pf_seek = se_StreamSeek; |
218 | 0 | s->pf_control = se_StreamControl; |
219 | 0 | s->psz_url = StreamExtractorCreateMRL( priv->extractor.source->psz_url, |
220 | 0 | priv->extractor.identifier, |
221 | 0 | (char const **) priv->extractor.volumes, |
222 | 0 | priv->extractor.volumes_count ); |
223 | 0 | if( unlikely( !s->psz_url ) ) |
224 | 0 | return VLC_ENOMEM; |
225 | | |
226 | 0 | return VLC_SUCCESS; |
227 | 0 | } |
228 | | |
229 | | static void |
230 | | se_CleanStreamExtractor( struct stream_extractor_private* priv ) |
231 | 0 | { |
232 | 0 | free( (char*)priv->extractor.identifier ); |
233 | 0 | for( size_t i=0; i<priv->extractor.volumes_count; i++ ) |
234 | 0 | free( priv->extractor.volumes[i] ); |
235 | 0 | free( priv->extractor.volumes ); |
236 | 0 | } |
237 | | |
238 | | static void |
239 | | se_CleanStreamDirectory( struct stream_extractor_private* priv ) |
240 | 0 | { |
241 | 0 | for( size_t i=0; i<priv->directory.volumes_count; i++ ) |
242 | 0 | free( priv->directory.volumes[i] ); |
243 | 0 | free( priv->directory.volumes ); |
244 | 0 | } |
245 | | |
246 | | static int |
247 | | se_InitDirectory( struct stream_extractor_private* priv, stream_t* s ) |
248 | 0 | { |
249 | 0 | stream_directory_t* directory = &priv->directory; |
250 | |
|
251 | 0 | s->pf_readdir = se_ReadDir; |
252 | 0 | s->pf_control = se_DirControl; |
253 | 0 | s->psz_url = strdup( directory->source->psz_url ); |
254 | |
|
255 | 0 | if( unlikely( !s->psz_url ) ) |
256 | 0 | return VLC_EGENERIC; |
257 | | |
258 | 0 | return VLC_SUCCESS; |
259 | 0 | } |
260 | | |
261 | | /** |
262 | | * @} |
263 | | **/ |
264 | | |
265 | | /** |
266 | | * Create the public stream_t that wraps a stream-extractor |
267 | | * |
268 | | * This initializes the relevant data-members of the public stream_t which is |
269 | | * used to read from the underlying stream-extractor. |
270 | | * |
271 | | * \param priv the private section of the stream_extractor_t |
272 | | * \param source the source stream which the stream_extractor_t should |
273 | | * will read from |
274 | | * \return VLC_SUCCESS on success, an error-code on failure. |
275 | | **/ |
276 | | static int |
277 | | se_AttachWrapper( struct stream_extractor_private* priv, stream_t* source ) |
278 | 0 | { |
279 | 0 | stream_t* s = vlc_stream_CommonNew( vlc_object_parent(source), |
280 | 0 | se_StreamDelete ); |
281 | |
|
282 | 0 | if( unlikely( !s ) ) |
283 | 0 | return VLC_ENOMEM; |
284 | | |
285 | 0 | if( priv->pf_init( priv, s ) ) |
286 | 0 | { |
287 | 0 | stream_CommonDelete( s ); |
288 | 0 | return VLC_EGENERIC; |
289 | 0 | } |
290 | | |
291 | 0 | priv->wrapper = s; |
292 | 0 | priv->wrapper->p_input_item = source->p_input_item; |
293 | 0 | priv->wrapper->p_sys = priv; |
294 | |
|
295 | 0 | priv->source = source; |
296 | |
|
297 | 0 | priv->wrapper = stream_FilterChainNew( priv->wrapper, "prefetch,cache" ); |
298 | 0 | return VLC_SUCCESS; |
299 | 0 | } |
300 | | |
301 | | static int |
302 | | StreamExtractorAttach( stream_t** source, char const* identifier, |
303 | | char const* module_name, const char **volumes, size_t volumes_count ) |
304 | 0 | { |
305 | 0 | const bool extractor = identifier != NULL; |
306 | 0 | char const* capability = extractor ? "stream_extractor" |
307 | 0 | : "stream_directory"; |
308 | |
|
309 | 0 | struct stream_extractor_private* priv = vlc_custom_create( |
310 | 0 | vlc_object_parent(*source), sizeof( *priv ), capability ); |
311 | |
|
312 | 0 | if( unlikely( !priv ) ) |
313 | 0 | return VLC_ENOMEM; |
314 | | |
315 | 0 | char ***dst_volumes; |
316 | 0 | size_t * dst_volumes_count; |
317 | |
|
318 | 0 | if( extractor ) |
319 | 0 | { |
320 | 0 | priv->object = VLC_OBJECT( &priv->extractor ); |
321 | |
|
322 | 0 | priv->pf_init = se_InitStream; |
323 | 0 | priv->pf_clean = se_CleanStreamExtractor; |
324 | |
|
325 | 0 | priv->extractor.source = *source; |
326 | 0 | priv->extractor.identifier = strdup( identifier ); |
327 | |
|
328 | 0 | if( unlikely( !priv->extractor.identifier ) ) |
329 | 0 | goto error; |
330 | | |
331 | 0 | dst_volumes = &priv->extractor.volumes; |
332 | 0 | dst_volumes_count = &priv->extractor.volumes_count; |
333 | 0 | } |
334 | 0 | else |
335 | 0 | { |
336 | 0 | priv->object = VLC_OBJECT( &priv->directory ); |
337 | |
|
338 | 0 | priv->pf_init = se_InitDirectory; |
339 | 0 | priv->pf_clean = se_CleanStreamDirectory; |
340 | |
|
341 | 0 | priv->directory.source = *source; |
342 | |
|
343 | 0 | dst_volumes = &priv->directory.volumes; |
344 | 0 | dst_volumes_count = &priv->directory.volumes_count; |
345 | 0 | } |
346 | | |
347 | 0 | if( volumes_count ) |
348 | 0 | { |
349 | 0 | *dst_volumes = malloc(volumes_count * sizeof(char *)); |
350 | 0 | if( unlikely(!*dst_volumes) ) |
351 | 0 | goto error; |
352 | 0 | for( *dst_volumes_count=0; *dst_volumes_count<volumes_count; (*dst_volumes_count)++ ) |
353 | 0 | { |
354 | 0 | (*dst_volumes)[*dst_volumes_count] = strdup(volumes[*dst_volumes_count]); |
355 | 0 | if( unlikely(!(*dst_volumes)[*dst_volumes_count]) ) |
356 | 0 | goto error; |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | 0 | priv->module = module_need( priv->object, capability, module_name, true ); |
361 | |
|
362 | 0 | if( !priv->module || se_AttachWrapper( priv, *source ) ) |
363 | 0 | goto error; |
364 | | |
365 | 0 | *source = priv->wrapper; |
366 | 0 | return VLC_SUCCESS; |
367 | | |
368 | 0 | error: |
369 | 0 | se_Release( priv ); |
370 | 0 | return VLC_EGENERIC; |
371 | 0 | } |
372 | | |
373 | | int |
374 | | vlc_stream_directory_Attach( stream_t** source, char const* module_name, |
375 | | const char **volumes, size_t volumes_count ) |
376 | 0 | { |
377 | 0 | return StreamExtractorAttach( source, NULL, module_name, volumes, volumes_count ); |
378 | 0 | } |
379 | | |
380 | | int |
381 | | vlc_stream_extractor_Attach( stream_t** source, char const* identifier, |
382 | | char const* module_name, const char **volumes, size_t volumes_count ) |
383 | 0 | { |
384 | 0 | return StreamExtractorAttach( source, identifier, module_name, volumes, volumes_count ); |
385 | 0 | } |
386 | | |
387 | | int |
388 | | stream_extractor_AttachParsed( stream_t** source, const struct mrl_info *mrli ) |
389 | 0 | { |
390 | 0 | size_t count = vlc_array_count( &mrli->identifiers ); |
391 | 0 | size_t idx = 0; |
392 | |
|
393 | 0 | while( idx < count ) |
394 | 0 | { |
395 | 0 | const char* id = vlc_array_item_at_index( &mrli->identifiers, idx ); |
396 | |
|
397 | 0 | if( vlc_stream_extractor_Attach( source, id, NULL, |
398 | 0 | (char const **) mrli->volumes.pp_elems, |
399 | 0 | mrli->volumes.i_count ) ) |
400 | 0 | break; |
401 | | |
402 | 0 | ++idx; |
403 | 0 | } |
404 | |
|
405 | 0 | return idx == count ? VLC_SUCCESS : VLC_EGENERIC; |
406 | 0 | } |
407 | | |
408 | | char* |
409 | | vlc_stream_extractor_CreateMRL( stream_directory_t* directory, |
410 | | char const* subentry, |
411 | | char const **volumes, size_t volumes_count ) |
412 | 0 | { |
413 | 0 | return StreamExtractorCreateMRL( directory->source->psz_url, subentry, |
414 | 0 | volumes, volumes_count ); |
415 | 0 | } |
416 | | |
417 | | /** |
418 | | * @} |
419 | | **/ |
420 | | |
421 | | #endif /* include-guard */ |