1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, 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/******************************************************************************
44Type definitions.
45******************************************************************************/
46
47typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_READER_OPEN_FUNC_T)(VC_CONTAINER_T *);
48typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_WRITER_OPEN_FUNC_T)(VC_CONTAINER_T *);
49
50/******************************************************************************
51Prototypes for local functions
52******************************************************************************/
53
54static void reset_context(VC_CONTAINER_T *p_ctx);
55static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read);
56static void unload_library(void *handle);
57static VC_CONTAINER_READER_OPEN_FUNC_T load_reader(void **handle, const char *name);
58static VC_CONTAINER_READER_OPEN_FUNC_T load_writer(void **handle, const char *name);
59static VC_CONTAINER_READER_OPEN_FUNC_T load_metadata_reader(void **handle, const char *name);
60static const char* container_for_fileext(const char *fileext);
61
62/********************************************************************************
63 List of supported containers
64 ********************************************************************************/
65
66static const char *readers[] =
67{"mp4", "asf", "avi", "mkv", "wav", "flv", "simple", "rawvideo", "mpga", "ps", "rtp", "rtsp", "rcv", "rv9", "qsynth", "binary", 0};
68static const char *writers[] =
69{"mp4", "asf", "avi", "binary", "simple", "rawvideo", 0};
70static const char *metadata_readers[] =
71{"id3", 0};
72
73#if defined(ENABLE_CONTAINERS_STANDALONE)
74VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T * );
75VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T * );
76VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T * );
77VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T * );
78VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T * );
79VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T * );
80VC_CONTAINER_STATUS_T mkv_reader_open( VC_CONTAINER_T * );
81VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T * );
82VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T * );
83VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T * );
84VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T * );
85VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T * );
86VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T * );
87VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T * );
88VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * );
89VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T * );
90VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T * );
91VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T * );
92VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T * );
93VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * );
94VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * );
95
96VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T * );
97
98static 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
123static 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
133static 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. */
151static 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 ********************************************************************************/
172VC_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/*****************************************************************************/
244VC_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/*****************************************************************************/
284void 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/******************************************************************************
294Local Functions
295******************************************************************************/
296static 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/*****************************************************************************/
314static 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/*****************************************************************************/
320static 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/*****************************************************************************/
326static 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/*****************************************************************************/
335static 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/*****************************************************************************/
382static void unload_library(void *handle)
383{
384 vcos_dlclose(handle);
385}
386
387#else /* !defined(ENABLE_CONTAINERS_STANDALONE) */
388
389/*****************************************************************************/
390static 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/*****************************************************************************/
417static void unload_library(void *handle)
418{
419 (void)handle;
420}
421
422#endif /* !defined(ENABLE_CONTAINERS_STANDALONE) */
423
424/*****************************************************************************/
425static 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