1 | /* |
2 | Copyright (c) 2012, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the following conditions are met: |
7 | * Redistributions of source code must retain the above copyright |
8 | notice, this list of conditions and the following disclaimer. |
9 | * Redistributions in binary form must reproduce the above copyright |
10 | notice, this list of conditions and the following disclaimer in the |
11 | documentation and/or other materials provided with the distribution. |
12 | * Neither the name of the copyright holder nor the |
13 | names of its contributors may be used to endorse or promote products |
14 | derived from this software without specific prior written permission. |
15 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | #include <stdlib.h> |
29 | #include <string.h> |
30 | #include <stdio.h> |
31 | |
32 | #include "containers/core/containers_private.h" |
33 | #include "containers/core/containers_loader.h" |
34 | |
35 | #if !defined(ENABLE_CONTAINERS_STANDALONE) |
36 | #include "vcos_dlfcn.h" |
37 | #define DL_SUFFIX VCOS_SO_EXT |
38 | #ifndef DL_PATH_PREFIX |
39 | #define DL_PATH_PREFIX "" |
40 | #endif |
41 | #endif |
42 | |
43 | /****************************************************************************** |
44 | Type definitions. |
45 | ******************************************************************************/ |
46 | |
47 | typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_READER_OPEN_FUNC_T)(VC_CONTAINER_T *); |
48 | typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_WRITER_OPEN_FUNC_T)(VC_CONTAINER_T *); |
49 | |
50 | /****************************************************************************** |
51 | Prototypes for local functions |
52 | ******************************************************************************/ |
53 | |
54 | static void reset_context(VC_CONTAINER_T *p_ctx); |
55 | static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read); |
56 | static void unload_library(void *handle); |
57 | static VC_CONTAINER_READER_OPEN_FUNC_T load_reader(void **handle, const char *name); |
58 | static VC_CONTAINER_READER_OPEN_FUNC_T load_writer(void **handle, const char *name); |
59 | static VC_CONTAINER_READER_OPEN_FUNC_T load_metadata_reader(void **handle, const char *name); |
60 | static const char* container_for_fileext(const char *fileext); |
61 | |
62 | /******************************************************************************** |
63 | List of supported containers |
64 | ********************************************************************************/ |
65 | |
66 | static const char *readers[] = |
67 | {"mp4" , "asf" , "avi" , "mkv" , "wav" , "flv" , "simple" , "rawvideo" , "mpga" , "ps" , "rtp" , "rtsp" , "rcv" , "rv9" , "qsynth" , "binary" , 0}; |
68 | static const char *writers[] = |
69 | {"mp4" , "asf" , "avi" , "binary" , "simple" , "rawvideo" , 0}; |
70 | static const char *metadata_readers[] = |
71 | {"id3" , 0}; |
72 | |
73 | #if defined(ENABLE_CONTAINERS_STANDALONE) |
74 | VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T * ); |
75 | VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T * ); |
76 | VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T * ); |
77 | VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T * ); |
78 | VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T * ); |
79 | VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T * ); |
80 | VC_CONTAINER_STATUS_T mkv_reader_open( VC_CONTAINER_T * ); |
81 | VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T * ); |
82 | VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T * ); |
83 | VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T * ); |
84 | VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T * ); |
85 | VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T * ); |
86 | VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T * ); |
87 | VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T * ); |
88 | VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * ); |
89 | VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T * ); |
90 | VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T * ); |
91 | VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T * ); |
92 | VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T * ); |
93 | VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * ); |
94 | VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * ); |
95 | |
96 | VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T * ); |
97 | |
98 | static struct |
99 | { |
100 | const char *name; |
101 | VC_CONTAINER_READER_OPEN_FUNC_T func; |
102 | } reader_entry_points[] = |
103 | { |
104 | {"asf" , &asf_reader_open}, |
105 | {"avi" , &avi_reader_open}, |
106 | {"mpga" , &mpga_reader_open}, |
107 | {"mkv" , &mkv_reader_open}, |
108 | {"wav" , &wav_reader_open}, |
109 | {"mp4" , &mp4_reader_open}, |
110 | {"flv" , &flv_reader_open}, |
111 | {"ps" , &ps_reader_open}, |
112 | {"binary" , &binary_reader_open}, |
113 | {"rtp" , &rtp_reader_open}, |
114 | {"rtsp" , &rtsp_reader_open}, |
115 | {"rcv" , &rcv_reader_open}, |
116 | {"rv9" , &rv9_reader_open}, |
117 | {"qsynth" , &qsynth_reader_open}, |
118 | {"simple" , &simple_reader_open}, |
119 | {"rawvideo" , &rawvideo_reader_open}, |
120 | {0, 0} |
121 | }; |
122 | |
123 | static struct |
124 | { |
125 | const char *name; |
126 | VC_CONTAINER_READER_OPEN_FUNC_T func; |
127 | } metadata_reader_entry_points[] = |
128 | { |
129 | {"id3" , &id3_metadata_reader_open}, |
130 | {0, 0} |
131 | }; |
132 | |
133 | static struct |
134 | { |
135 | const char *name; |
136 | VC_CONTAINER_WRITER_OPEN_FUNC_T func; |
137 | } writer_entry_points[] = |
138 | { |
139 | {"avi" , &avi_writer_open}, |
140 | {"mp4" , &mp4_writer_open}, |
141 | {"binary" , &binary_writer_open}, |
142 | {"simple" , &simple_writer_open}, |
143 | {"rawvideo" , &rawvideo_writer_open}, |
144 | {0, 0} |
145 | }; |
146 | #endif /* defined(ENABLE_CONTAINERS_STANDALONE) */ |
147 | |
148 | /** Table describing the mapping between file extensions and container name. |
149 | This is only used as optimisation to decide which container to try first. |
150 | Entries where the file extension and container have the same name can be omitted. */ |
151 | static const struct { |
152 | const char *extension; |
153 | const char *container; |
154 | } extension_container_mapping[] = |
155 | { |
156 | { "wma" , "asf" }, |
157 | { "wmv" , "asf" }, |
158 | { "mov" , "mp4" }, |
159 | { "3gp" , "mp4" }, |
160 | { "mp2" , "mpga" }, |
161 | { "mp3" , "mpga" }, |
162 | { "webm" , "mkv" }, |
163 | { "mid" , "qsynth" }, |
164 | { "mld" , "qsynth" }, |
165 | { "mmf" , "qsynth" }, |
166 | { 0, 0 } |
167 | }; |
168 | |
169 | /******************************************************************************** |
170 | Public functions |
171 | ********************************************************************************/ |
172 | VC_CONTAINER_STATUS_T vc_container_load_reader(VC_CONTAINER_T *p_ctx, const char *fileext) |
173 | { |
174 | const char *name; |
175 | void *handle = NULL; |
176 | VC_CONTAINER_READER_OPEN_FUNC_T func; |
177 | VC_CONTAINER_STATUS_T status; |
178 | unsigned int i; |
179 | int64_t offset; |
180 | |
181 | vc_container_assert(p_ctx && !p_ctx->priv->module_handle); |
182 | |
183 | /* FIXME: the missing part here is code that reads a configuration or |
184 | searches the filesystem for container libraries. Instead, we currently |
185 | rely on static arrays i.e. 'readers', 'writers', etc. */ |
186 | |
187 | /* Before trying proper container readers, iterate through metadata |
188 | readers to parse tags concatenated to start/end of stream */ |
189 | for(i = 0; metadata_readers[i]; i++) |
190 | { |
191 | if ((func = load_metadata_reader(&handle, metadata_readers[i])) != NULL) |
192 | { |
193 | status = (*func)(p_ctx); |
194 | if(!status && p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx); |
195 | reset_context(p_ctx); |
196 | unload_library(handle); |
197 | if(status == VC_CONTAINER_SUCCESS) break; |
198 | if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; |
199 | } |
200 | } |
201 | |
202 | /* Store the current position, in case any containers don't leave the stream |
203 | at the start, and the IO layer can cope with the seek */ |
204 | offset = p_ctx->priv->io->offset; |
205 | |
206 | /* Now move to containers, try to find a readers using the file extension to name |
207 | mapping first */ |
208 | if (fileext && (name = container_for_fileext(fileext)) != NULL && (func = load_reader(&handle, name)) != NULL) |
209 | { |
210 | status = (*func)(p_ctx); |
211 | if(status == VC_CONTAINER_SUCCESS) goto success; |
212 | unload_library(handle); |
213 | if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; |
214 | } |
215 | |
216 | /* If there was no suitable mapping, iterate through all readers. */ |
217 | for(i = 0; readers[i]; i++) |
218 | { |
219 | if ((func = load_reader(&handle, readers[i])) != NULL) |
220 | { |
221 | if(vc_container_io_seek(p_ctx->priv->io, offset) != VC_CONTAINER_SUCCESS) |
222 | { |
223 | unload_library(handle); |
224 | goto error; |
225 | } |
226 | |
227 | status = (*func)(p_ctx); |
228 | if(status == VC_CONTAINER_SUCCESS) goto success; |
229 | reset_context(p_ctx); |
230 | unload_library(handle); |
231 | if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; |
232 | } |
233 | } |
234 | |
235 | error: |
236 | return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
237 | |
238 | success: |
239 | p_ctx->priv->module_handle = handle; |
240 | return VC_CONTAINER_SUCCESS; |
241 | } |
242 | |
243 | /*****************************************************************************/ |
244 | VC_CONTAINER_STATUS_T vc_container_load_writer(VC_CONTAINER_T *p_ctx, const char *fileext) |
245 | { |
246 | const char *name; |
247 | void *handle = NULL; |
248 | VC_CONTAINER_WRITER_OPEN_FUNC_T func; |
249 | VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FAILED; |
250 | unsigned int i; |
251 | |
252 | vc_container_assert(p_ctx && !p_ctx->priv->module_handle); |
253 | |
254 | /* Do we have a container mapping for this file extension? */ |
255 | if ((name = container_for_fileext(fileext)) != NULL && (func = load_writer(&handle, name)) != NULL) |
256 | { |
257 | status = (*func)(p_ctx); |
258 | if(status == VC_CONTAINER_SUCCESS) goto success; |
259 | unload_library(handle); |
260 | if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; |
261 | } |
262 | |
263 | /* If there was no suitable mapping, iterate through all writers. */ |
264 | for(i = 0; writers[i]; i++) |
265 | { |
266 | if ((func = load_writer(&handle, writers[i])) != NULL) |
267 | { |
268 | status = (*func)(p_ctx); |
269 | if(status == VC_CONTAINER_SUCCESS) goto success; |
270 | unload_library(handle); |
271 | if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; |
272 | } |
273 | } |
274 | |
275 | error: |
276 | return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
277 | |
278 | success: |
279 | p_ctx->priv->module_handle = handle; |
280 | return status; |
281 | } |
282 | |
283 | /*****************************************************************************/ |
284 | void vc_container_unload(VC_CONTAINER_T *p_ctx) |
285 | { |
286 | if (p_ctx->priv->module_handle) |
287 | { |
288 | unload_library(p_ctx->priv->module_handle); |
289 | p_ctx->priv->module_handle = NULL; |
290 | } |
291 | } |
292 | |
293 | /****************************************************************************** |
294 | Local Functions |
295 | ******************************************************************************/ |
296 | static void reset_context(VC_CONTAINER_T *p_ctx) |
297 | { |
298 | vc_container_assert(p_ctx); |
299 | |
300 | p_ctx->capabilities = 0; |
301 | p_ctx->tracks = NULL; |
302 | p_ctx->tracks_num = 0; |
303 | p_ctx->drm = NULL; |
304 | p_ctx->priv->module = NULL; |
305 | p_ctx->priv->pf_close = NULL; |
306 | p_ctx->priv->pf_read = NULL; |
307 | p_ctx->priv->pf_write = NULL; |
308 | p_ctx->priv->pf_seek = NULL; |
309 | p_ctx->priv->pf_control = NULL; |
310 | p_ctx->priv->tmp_io = NULL; |
311 | } |
312 | |
313 | /*****************************************************************************/ |
314 | static VC_CONTAINER_READER_OPEN_FUNC_T load_reader(void **handle, const char *name) |
315 | { |
316 | return load_library(handle, name, NULL, 1); |
317 | } |
318 | |
319 | /*****************************************************************************/ |
320 | static VC_CONTAINER_READER_OPEN_FUNC_T load_writer(void **handle, const char *name) |
321 | { |
322 | return load_library(handle, name, NULL, 0); |
323 | } |
324 | |
325 | /*****************************************************************************/ |
326 | static VC_CONTAINER_READER_OPEN_FUNC_T load_metadata_reader(void **handle, const char *name) |
327 | { |
328 | #define DL_PREFIX_METADATA "metadata_" |
329 | return load_library(handle, name, DL_PREFIX_METADATA, 1); |
330 | } |
331 | |
332 | #if !defined(ENABLE_CONTAINERS_STANDALONE) |
333 | |
334 | /*****************************************************************************/ |
335 | static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read) |
336 | { |
337 | #define DL_PREFIX_RD "reader_" |
338 | #define DL_PREFIX_WR "writer_" |
339 | const char *entrypt_read = {"reader_open" }; |
340 | const char *entrypt_write = {"writer_open" }; |
341 | char *dl_name, *entrypt_name; |
342 | void *dl_handle; |
343 | VC_CONTAINER_READER_OPEN_FUNC_T func = NULL; |
344 | unsigned dl_size, ep_size, name_len = strlen(name) + (ext ? strlen(ext) : 0); |
345 | |
346 | vc_container_assert(read == 0 || read == 1); |
347 | |
348 | dl_size = strlen(DL_PATH_PREFIX) + MAX(strlen(DL_PREFIX_RD), strlen(DL_PREFIX_WR)) + name_len + strlen(DL_SUFFIX) + 1; |
349 | if ((dl_name = malloc(dl_size)) == NULL) |
350 | return NULL; |
351 | |
352 | ep_size = name_len + 1 + MAX(strlen(entrypt_read), strlen(entrypt_write)) + 1; |
353 | if ((entrypt_name = malloc(ep_size)) == NULL) |
354 | { |
355 | free(dl_name); |
356 | return NULL; |
357 | } |
358 | |
359 | snprintf(dl_name, dl_size, "%s%s%s%s%s" , DL_PATH_PREFIX, read ? DL_PREFIX_RD : DL_PREFIX_WR, ext ? ext : "" , name, DL_SUFFIX); |
360 | snprintf(entrypt_name, ep_size, "%s_%s%s" , name, ext ? ext : "" , read ? entrypt_read : entrypt_write); |
361 | |
362 | if ( (dl_handle = vcos_dlopen(dl_name, VCOS_DL_NOW)) != NULL ) |
363 | { |
364 | /* Try generic entrypoint name before the mangled, full name */ |
365 | func = (VC_CONTAINER_READER_OPEN_FUNC_T)vcos_dlsym(dl_handle, read ? entrypt_read : entrypt_write); |
366 | #if !defined(__VIDEOCORE__) /* The following would be pointless on MW/VideoCore */ |
367 | if (!func) func = (VC_CONTAINER_READER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name); |
368 | #endif |
369 | /* Only return handle if symbol found */ |
370 | if (func) |
371 | *handle = dl_handle; |
372 | else |
373 | vcos_dlclose(dl_handle); |
374 | } |
375 | |
376 | free(entrypt_name); |
377 | free(dl_name); |
378 | return func; |
379 | } |
380 | |
381 | /*****************************************************************************/ |
382 | static void unload_library(void *handle) |
383 | { |
384 | vcos_dlclose(handle); |
385 | } |
386 | |
387 | #else /* !defined(ENABLE_CONTAINERS_STANDALONE) */ |
388 | |
389 | /*****************************************************************************/ |
390 | static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read) |
391 | { |
392 | int i; |
393 | VC_CONTAINER_PARAM_UNUSED(handle); |
394 | VC_CONTAINER_PARAM_UNUSED(ext); |
395 | |
396 | if (read) |
397 | { |
398 | for (i = 0; reader_entry_points[i].name; i++) |
399 | if (!strcasecmp(reader_entry_points[i].name, name)) |
400 | return reader_entry_points[i].func; |
401 | |
402 | for (i = 0; metadata_reader_entry_points[i].name; i++) |
403 | if (!strcasecmp(metadata_reader_entry_points[i].name, name)) |
404 | return metadata_reader_entry_points[i].func; |
405 | } |
406 | else |
407 | { |
408 | for (i = 0; writer_entry_points[i].name; i++) |
409 | if (!strcasecmp(writer_entry_points[i].name, name)) |
410 | return writer_entry_points[i].func; |
411 | } |
412 | |
413 | return NULL; |
414 | } |
415 | |
416 | /*****************************************************************************/ |
417 | static void unload_library(void *handle) |
418 | { |
419 | (void)handle; |
420 | } |
421 | |
422 | #endif /* !defined(ENABLE_CONTAINERS_STANDALONE) */ |
423 | |
424 | /*****************************************************************************/ |
425 | static const char* container_for_fileext(const char *fileext) |
426 | { |
427 | int i; |
428 | |
429 | for( i = 0; fileext && extension_container_mapping[i].extension; i++ ) |
430 | { |
431 | if (!strcasecmp( fileext, extension_container_mapping[i].extension )) |
432 | return extension_container_mapping[i].container; |
433 | } |
434 | |
435 | return fileext; |
436 | } |
437 | |