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 
31 #include "containers/core/containers_private.h"
32 #include "containers/core/containers_io.h"
33 #include "containers/core/containers_filters.h"
34 #include "containers/core/containers_loader.h"
35 #include "containers/core/containers_logging.h"
36 #include "containers/core/containers_utils.h"
37 
38 #define WRITER_SPACE_SAFETY_MARGIN (10*1024)
39 #define PACKETIZER_BUFFER_SIZE (32*1024)
40 
41 /*****************************************************************************/
vc_container_open_reader_with_io(struct VC_CONTAINER_IO_T * io,const char * uri,VC_CONTAINER_STATUS_T * p_status,VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress,void * progress_userdata)42 VC_CONTAINER_T *vc_container_open_reader_with_io( struct VC_CONTAINER_IO_T *io,
43    const char *uri, VC_CONTAINER_STATUS_T *p_status,
44    VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata)
45 {
46    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
47    VC_CONTAINER_T *p_ctx = 0;
48    const char *extension;
49 
50    VC_CONTAINER_PARAM_UNUSED(pfn_progress);
51    VC_CONTAINER_PARAM_UNUSED(progress_userdata);
52    VC_CONTAINER_PARAM_UNUSED(uri);
53 
54    /* Sanity check the i/o */
55    if (!io || !io->pf_read || !io->pf_seek)
56    {
57       LOG_ERROR(0, "invalid i/o instance: %p", io);
58       status = VC_CONTAINER_ERROR_INVALID_ARGUMENT;
59       goto error;
60    }
61 
62    /* Allocate our context before trying out the different readers / writers */
63    p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->drm));
64    if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
65    memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->drm));
66    p_ctx->priv = (VC_CONTAINER_PRIVATE_T *)(p_ctx + 1);
67    p_ctx->priv->verbosity = vc_container_log_get_default_verbosity();
68    p_ctx->drm = (VC_CONTAINER_DRM_T *)(p_ctx->priv + 1);
69    p_ctx->size = io->size;
70    p_ctx->priv->io = io;
71    p_ctx->priv->uri = io->uri_parts;
72 
73    /* If the uri has an extension, use it as a hint when loading the container */
74    extension = vc_uri_path_extension(p_ctx->priv->uri);
75    /* If the user has specified a container, then use that instead */
76    vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
77 
78    status = vc_container_load_reader(p_ctx, extension);
79    if (status != VC_CONTAINER_SUCCESS)
80       goto error;
81 
82    p_ctx->priv->drm_filter = vc_container_filter_open(VC_FOURCC('d','r','m',' '),
83       VC_FOURCC('u','n','k','n'), p_ctx, &status);
84    if (status != VC_CONTAINER_SUCCESS)
85    {
86       /* Some other problem occurred aside from the filter not being appropriate or
87          the stream not needing it. */
88       if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error;
89 
90       /* Report no DRM and continue as normal */
91       p_ctx->drm = NULL;
92       status = VC_CONTAINER_SUCCESS;
93    }
94 
95 end:
96    if(p_status) *p_status = status;
97    return p_ctx;
98 
99 error:
100    if (p_ctx)
101    {
102       p_ctx->priv->io = NULL; /* The i/o doesn't belong to us */
103       vc_container_close(p_ctx);
104       p_ctx = NULL;
105    }
106    goto end;
107 }
108 
109 /*****************************************************************************/
vc_container_open_reader(const char * uri,VC_CONTAINER_STATUS_T * p_status,VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress,void * progress_userdata)110 VC_CONTAINER_T *vc_container_open_reader( const char *uri, VC_CONTAINER_STATUS_T *p_status,
111    VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata)
112 {
113    VC_CONTAINER_IO_T *io;
114    VC_CONTAINER_T *ctx;
115 
116    /* Start by opening the URI */
117    io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_READ, p_status );
118    if (!io)
119       return 0;
120 
121    ctx = vc_container_open_reader_with_io( io, uri, p_status, pfn_progress, progress_userdata);
122    if (!ctx)
123       vc_container_io_close(io);
124    return ctx;
125 }
126 
127 /*****************************************************************************/
vc_container_open_writer(const char * uri,VC_CONTAINER_STATUS_T * p_status,VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress,void * progress_userdata)128 VC_CONTAINER_T *vc_container_open_writer( const char *uri, VC_CONTAINER_STATUS_T *p_status,
129    VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata)
130 {
131    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
132    VC_CONTAINER_T *p_ctx = 0;
133    VC_CONTAINER_IO_T *io;
134    const char *extension;
135    VC_CONTAINER_PARAM_UNUSED(pfn_progress);
136    VC_CONTAINER_PARAM_UNUSED(progress_userdata);
137 
138    /* Start by opening the URI */
139    io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_WRITE, &status );
140    if(!io) goto error;
141 
142    /* Make sure we have enough space available to start writing */
143    if(io->max_size && io->max_size < WRITER_SPACE_SAFETY_MARGIN)
144    {
145       LOG_DEBUG(p_ctx, "not enough space available to open a writer");
146       status = VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
147       goto error;
148    }
149 
150    /* Allocate our context before trying out the different readers / writers */
151    p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv));
152    if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
153    memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv));
154    p_ctx->priv = (VC_CONTAINER_PRIVATE_T *)(p_ctx + 1);
155    p_ctx->priv->verbosity = vc_container_log_get_default_verbosity();
156    p_ctx->priv->io = io;
157    p_ctx->priv->uri = io->uri_parts;
158    io = NULL; /* io now owned by the context */
159 
160    /* If the uri has an extension, use it as a hint when loading the container */
161    extension = vc_uri_path_extension(p_ctx->priv->uri);
162    /* If the user has specified a container, then use that instead */
163    vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
164 
165    status = vc_container_load_writer(p_ctx, extension);
166    if(status != VC_CONTAINER_SUCCESS) goto error;
167 
168  end:
169    if(p_status) *p_status = status;
170    return p_ctx;
171 
172 error:
173    if(io) vc_container_io_close(io);
174    if (p_ctx) vc_container_close(p_ctx);
175    p_ctx = NULL;
176    goto end;
177 }
178 
179 /*****************************************************************************/
vc_container_close(VC_CONTAINER_T * p_ctx)180 VC_CONTAINER_STATUS_T vc_container_close( VC_CONTAINER_T *p_ctx )
181 {
182    unsigned int i;
183 
184    if(!p_ctx)
185       return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
186 
187    for(i = 0; i < p_ctx->tracks_num; i++)
188       if(p_ctx->tracks[i]->priv->packetizer)
189          vc_packetizer_close(p_ctx->tracks[i]->priv->packetizer);
190    if(p_ctx->priv->packetizer_buffer) free(p_ctx->priv->packetizer_buffer);
191    if(p_ctx->priv->drm_filter) vc_container_filter_close(p_ctx->priv->drm_filter);
192    if(p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx);
193    if(p_ctx->priv->io) vc_container_io_close(p_ctx->priv->io);
194    if(p_ctx->priv->module_handle) vc_container_unload(p_ctx);
195    for(i = 0; i < p_ctx->meta_num; i++) free(p_ctx->meta[i]);
196    if(p_ctx->meta_num) free(p_ctx->meta);
197    p_ctx->meta_num = 0;
198    free(p_ctx);
199 
200    return VC_CONTAINER_SUCCESS;
201 }
202 
203 /*****************************************************************************/
container_read_packet(VC_CONTAINER_T * p_ctx,VC_CONTAINER_PACKET_T * p_packet,uint32_t flags)204 static VC_CONTAINER_STATUS_T container_read_packet( VC_CONTAINER_T *p_ctx,
205    VC_CONTAINER_PACKET_T *p_packet, uint32_t flags )
206 {
207    VC_CONTAINER_STATUS_T status;
208 
209    while(1)
210    {
211       status = p_ctx->priv->pf_read(p_ctx, p_packet, flags);
212       if(status == VC_CONTAINER_ERROR_CONTINUE)
213          continue;
214 
215       if(!p_packet || (flags & VC_CONTAINER_READ_FLAG_SKIP))
216          return status; /* We've just been requested to skip the data */
217 
218       if(status != VC_CONTAINER_SUCCESS)
219          return status;
220 
221       /* Skip data from out of bounds tracks, disabled tracks or packets that are encrypted
222          and cannot be decrypted */
223       if(p_packet->track >= p_ctx->tracks_num ||
224          !p_ctx->tracks[p_packet->track]->is_enabled ||
225          ((p_packet->flags & VC_CONTAINER_PACKET_FLAG_ENCRYPTED) && !p_ctx->priv->drm_filter))
226       {
227          if(flags & VC_CONTAINER_READ_FLAG_INFO)
228             status = p_ctx->priv->pf_read(p_ctx, p_packet, VC_CONTAINER_READ_FLAG_SKIP);
229          if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_CONTINUE)
230             continue;
231       }
232       if(status != VC_CONTAINER_SUCCESS)
233          return status;
234 
235       if(p_ctx->priv->drm_filter)
236          status = vc_container_filter_process(p_ctx->priv->drm_filter, p_packet);
237 
238       break;
239    }
240    return status;
241 }
242 
243 /*****************************************************************************/
vc_container_read(VC_CONTAINER_T * p_ctx,VC_CONTAINER_PACKET_T * p_packet,uint32_t flags)244 VC_CONTAINER_STATUS_T vc_container_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags )
245 {
246    VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_CONTINUE;
247    VC_PACKETIZER_FLAGS_T packetizer_flags = 0;
248    VC_PACKETIZER_T *packetizer;
249    uint32_t force = flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK;
250    unsigned int i;
251 
252    if(!p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP))
253       return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
254    if(!p_packet && (flags & VC_CONTAINER_READ_FLAG_INFO))
255       return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
256    if(p_packet && !p_packet->data && !(flags & (VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_SKIP)))
257       return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
258    if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) &&
259       (!p_packet || p_packet->track >= p_ctx->tracks_num || !p_ctx->tracks[p_packet->track]->is_enabled))
260       return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
261 
262    /* Always having a packet structure to work with simplifies things */
263    if(!p_packet)
264       p_packet = &p_ctx->priv->packetizer_packet;
265 
266    /* Simple/Fast case first */
267    if(!p_ctx->priv->packetizing)
268    {
269       status = container_read_packet( p_ctx, p_packet, flags );
270       goto end;
271    }
272 
273    if(flags & VC_CONTAINER_READ_FLAG_INFO)
274       packetizer_flags |= VC_PACKETIZER_FLAG_INFO;
275    if(flags & VC_CONTAINER_READ_FLAG_SKIP)
276       packetizer_flags |= VC_PACKETIZER_FLAG_SKIP;
277 
278    /* Loop through all the packetized tracks first to see if we've got any
279     * data to consume there */
280    for(i = 0; i < p_ctx->tracks_num; i++)
281    {
282       VC_PACKETIZER_T *packetizer = p_ctx->tracks[i]->priv->packetizer;
283       if(!p_ctx->tracks[i]->is_enabled || !packetizer ||
284          (force && i != p_packet->track))
285          continue;
286 
287       status = vc_packetizer_read(packetizer, p_packet, packetizer_flags);
288       p_packet->track = i;
289       if(status == VC_CONTAINER_SUCCESS)
290          break;
291    }
292    if(i < p_ctx->tracks_num) /* We've got some data */
293       goto end;
294 
295    /* Let's go and read some data from the actual container */
296    while(1)
297    {
298       VC_CONTAINER_PACKET_T *tmp = &p_ctx->priv->packetizer_packet;
299       tmp->track = p_packet->track;
300 
301       /* Let's check what the container has to offer */
302       status = container_read_packet( p_ctx, tmp, force|VC_PACKETIZER_FLAG_INFO );
303       if(status != VC_CONTAINER_SUCCESS)
304          return status;
305 
306       if(!p_ctx->tracks[tmp->track]->priv->packetizer)
307       {
308          status = container_read_packet( p_ctx, p_packet, flags );
309          break;
310       }
311 
312       /* Feed data from the container onto the packetizer */
313       packetizer = p_ctx->tracks[tmp->track]->priv->packetizer;
314 
315       tmp->data = p_ctx->priv->packetizer_buffer;
316       tmp->buffer_size = PACKETIZER_BUFFER_SIZE;
317       tmp->size = 0;
318       status = container_read_packet( p_ctx, tmp, force );
319       if(status != VC_CONTAINER_SUCCESS)
320          return status;
321 
322       p_packet->track = tmp->track;
323       vc_packetizer_push(packetizer, tmp);
324       vc_packetizer_pop(packetizer, &tmp, VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT);
325 
326       status = vc_packetizer_read(packetizer, p_packet, packetizer_flags);
327       if(status == VC_CONTAINER_SUCCESS)
328          break;
329    }
330 
331  end:
332    if(status != VC_CONTAINER_SUCCESS)
333       return status;
334 
335    if(p_packet && p_packet->dts > p_ctx->position)
336       p_ctx->position = p_packet->dts;
337    if(p_packet && p_packet->pts > p_ctx->position)
338       p_ctx->position = p_packet->pts;
339 
340    return VC_CONTAINER_SUCCESS;
341 }
342 
343 /*****************************************************************************/
vc_container_write(VC_CONTAINER_T * p_ctx,VC_CONTAINER_PACKET_T * p_packet)344 VC_CONTAINER_STATUS_T vc_container_write( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet )
345 {
346    VC_CONTAINER_STATUS_T status;
347    void * p_metadata_buffer = NULL;
348    uint32_t metadata_length = 0;
349 
350    /* TODO: check other similar argument errors and non-stateless errors */
351    if (!p_packet || !p_packet->data || p_packet->track >= p_ctx->tracks_num)
352       return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
353 
354    /* Check for a previous error */
355    if(p_ctx->priv->status != VC_CONTAINER_SUCCESS && p_ctx->priv->status != VC_CONTAINER_ERROR_NOT_READY)
356       return p_ctx->priv->status;
357 
358    /* Check we have enough space to write the data */
359    if(p_ctx->priv->max_size &&
360       p_ctx->size + p_packet->size + WRITER_SPACE_SAFETY_MARGIN > p_ctx->priv->max_size)
361    {status = VC_CONTAINER_ERROR_LIMIT_REACHED; goto end;}
362    if(p_ctx->priv->io->max_size &&
363       p_ctx->size + p_packet->size + WRITER_SPACE_SAFETY_MARGIN +
364          (p_ctx->priv->tmp_io ? p_ctx->priv->tmp_io->offset : 0) > p_ctx->priv->io->max_size)
365    {status = VC_CONTAINER_ERROR_OUT_OF_SPACE; goto end;}
366 
367    /* If a filter is created, then send the packet to the filter for encryption. */
368    if(p_ctx->priv->drm_filter)
369    {
370       status = vc_container_filter_process(p_ctx->priv->drm_filter, p_packet);
371 
372       if(status == VC_CONTAINER_SUCCESS)
373       {
374          /* Get the encryption metadata and send it to the output first. */
375          if(vc_container_control(p_ctx, VC_CONTAINER_CONTROL_GET_DRM_METADATA,
376              &p_metadata_buffer, &metadata_length) == VC_CONTAINER_SUCCESS && metadata_length > 0)
377          {
378             /* Make a packet up with the metadata in the payload and write it. */
379             VC_CONTAINER_PACKET_T metadata_packet;
380             metadata_packet.data = p_metadata_buffer;
381             metadata_packet.buffer_size = metadata_length;
382             metadata_packet.size = metadata_length;
383             metadata_packet.frame_size = p_packet->frame_size + metadata_length;
384             metadata_packet.pts = p_packet->pts;
385             metadata_packet.dts = p_packet->dts;
386             metadata_packet.num = p_packet->num;
387             metadata_packet.track = p_packet->track;
388             /* As this packet is written first, we must transfer any frame start
389                flag from the following packet. Also, this packet can never have the end flag set. */
390             metadata_packet.flags = p_packet->flags & ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
391 
392             p_packet->pts = p_packet->dts = VC_CONTAINER_TIME_UNKNOWN;
393             p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_START;
394             if(p_ctx->priv->pf_write(p_ctx, &metadata_packet) != VC_CONTAINER_SUCCESS) goto end;
395          }
396       }
397       else if (status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION)
398       {
399          /* Encryption was appropriate but a problem has occurred. Skip the write of data
400             to the io and return the status to the caller. */
401          goto end;
402       }
403    }
404 
405    status = p_ctx->priv->pf_write(p_ctx, p_packet);
406 
407  end:
408    p_ctx->priv->status = status;
409    return status;
410 }
411 
412 /*****************************************************************************/
vc_container_seek(VC_CONTAINER_T * p_ctx,int64_t * p_offset,VC_CONTAINER_SEEK_MODE_T mode,VC_CONTAINER_SEEK_FLAGS_T flags)413 VC_CONTAINER_STATUS_T vc_container_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset,
414    VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
415 {
416    VC_CONTAINER_STATUS_T status;
417    int64_t offset = *p_offset;
418    unsigned int i;
419 
420    /* Reset all packetizers */
421    for(i = 0; i < p_ctx->tracks_num; i++)
422       if(p_ctx->tracks[i]->priv->packetizer)
423          vc_packetizer_reset(p_ctx->tracks[i]->priv->packetizer);
424 
425    status = p_ctx->priv->pf_seek(p_ctx, p_offset, mode, flags);
426 
427    /* */
428    if(status == VC_CONTAINER_SUCCESS && (flags & VC_CONTAINER_SEEK_FLAG_FORWARD) &&
429       *p_offset < offset)
430    {
431       LOG_DEBUG(p_ctx, "container didn't seek forward as requested (%"PRIi64"/%"PRIi64")",
432                 *p_offset, offset);
433       for(i = 1; i <= 5 && *p_offset < offset; i++)
434       {
435          *p_offset = offset + i * i * 100000;
436          status = p_ctx->priv->pf_seek(p_ctx, p_offset, mode, flags);
437          if(status != VC_CONTAINER_SUCCESS) break;
438       }
439    }
440 
441    return status;
442 }
443 
444 /*****************************************************************************/
vc_container_control(VC_CONTAINER_T * p_ctx,VC_CONTAINER_CONTROL_T operation,...)445 VC_CONTAINER_STATUS_T vc_container_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, ... )
446 {
447    VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
448    va_list args;
449 
450    va_start( args, operation );
451 
452    if(operation == VC_CONTAINER_CONTROL_ENCRYPT_TRACK)
453    {
454       VC_CONTAINER_FOURCC_T type = va_arg(args, VC_CONTAINER_FOURCC_T);
455 
456       uint32_t track_num = va_arg(args, uint32_t);
457       void *p_drm_data = va_arg(args, void *);
458       int drm_data_size = va_arg(args, uint32_t);
459 
460       if(!p_ctx->priv->drm_filter && track_num < p_ctx->tracks_num)
461       {
462          VC_CONTAINER_TRACK_T * p_track = p_ctx->tracks[track_num];
463 
464          if ((status = vc_container_track_allocate_drmdata(p_ctx, p_track, drm_data_size)) != VC_CONTAINER_SUCCESS)
465          {
466             LOG_DEBUG(p_ctx, "failed to allocate memory for 'drm data' (%d bytes)", drm_data_size);
467             goto end;
468          }
469          memcpy(p_track->priv->drmdata, p_drm_data, drm_data_size);
470 
471          /* Track encryption enabled and no filter - create one now. */
472          p_ctx->priv->drm_filter = vc_container_filter_open(VC_FOURCC('d','r','m',' '), type, p_ctx, &status);
473          if(status != VC_CONTAINER_SUCCESS)
474             goto end;
475 
476          status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, track_num);
477       }
478    }
479    else if(operation == VC_CONTAINER_CONTROL_GET_DRM_METADATA)
480    {
481       void *p_drm_data = va_arg(args, void *);
482       int *drm_data_size = va_arg(args, int *);
483       status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, p_drm_data, drm_data_size);
484    }
485 
486    if(status == VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION && p_ctx->priv->pf_control)
487       status = p_ctx->priv->pf_control(p_ctx, operation, args);
488 
489    /* If the request has already been handled then we're done */
490    if(status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION)
491       goto end;
492 
493    switch(operation)
494    {
495    case VC_CONTAINER_CONTROL_DRM_PLAY:
496       if (p_ctx->priv->drm_filter)
497          status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, args);
498       break;
499 
500    case VC_CONTAINER_CONTROL_SET_MAXIMUM_SIZE:
501       p_ctx->priv->max_size = (uint64_t)va_arg( args, uint64_t );
502       if(p_ctx->priv->io->max_size &&
503          p_ctx->priv->max_size > p_ctx->priv->io->max_size)
504          p_ctx->priv->max_size = p_ctx->priv->io->max_size;
505       status = VC_CONTAINER_SUCCESS;
506       break;
507 
508    case VC_CONTAINER_CONTROL_TRACK_PACKETIZE:
509       {
510          unsigned int track_num = va_arg(args, unsigned int);
511          VC_CONTAINER_FOURCC_T fourcc = va_arg(args, VC_CONTAINER_FOURCC_T);
512          VC_CONTAINER_TRACK_T *p_track;
513 
514          if(track_num >= p_ctx->tracks_num)
515          {
516             status = VC_CONTAINER_ERROR_INVALID_ARGUMENT;
517             break;
518          }
519          if(p_ctx->tracks[track_num]->priv->packetizer)
520          {
521             status = VC_CONTAINER_SUCCESS;
522             break;
523          }
524 
525          p_track = p_ctx->tracks[track_num];
526          p_track->priv->packetizer = vc_packetizer_open( p_track->format, fourcc, &status );
527          if(!p_track->priv->packetizer)
528             break;
529 
530          if(!p_ctx->priv->packetizer_buffer)
531          {
532             p_ctx->priv->packetizer_buffer = malloc(PACKETIZER_BUFFER_SIZE);
533             if(!p_ctx->priv->packetizer_buffer)
534             {
535                status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
536                vc_packetizer_close(p_track->priv->packetizer);
537                p_track->priv->packetizer = NULL;
538                break;
539             }
540          }
541 
542          vc_container_format_copy(p_track->format, p_track->priv->packetizer->out,
543             p_track->format->extradata_size);
544          p_track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
545          p_ctx->priv->packetizing = true;
546       }
547       break;
548 
549    default: break;
550    }
551 
552    if (status == VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION)
553       status = vc_container_io_control_list(p_ctx->priv->io, operation, args);
554 
555  end:
556    va_end( args );
557    return status;
558 }
559 
560 /*****************************************************************************/
vc_container_allocate_track(VC_CONTAINER_T * context,unsigned int extra_size)561 VC_CONTAINER_TRACK_T *vc_container_allocate_track( VC_CONTAINER_T *context, unsigned int extra_size )
562 {
563    VC_CONTAINER_TRACK_T *p_ctx;
564    unsigned int size;
565    VC_CONTAINER_PARAM_UNUSED(context);
566 
567    size = sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->format) +
568       sizeof(*p_ctx->format->type) + extra_size;
569 
570    p_ctx = malloc(size);
571    if(!p_ctx) return 0;
572 
573    memset(p_ctx, 0, size);
574    p_ctx->priv = (VC_CONTAINER_TRACK_PRIVATE_T *)(p_ctx + 1);
575    p_ctx->format = (VC_CONTAINER_ES_FORMAT_T *)(p_ctx->priv + 1);
576    p_ctx->format->type = (void *)(p_ctx->format + 1);
577    p_ctx->priv->module = (struct VC_CONTAINER_TRACK_MODULE_T *)(p_ctx->format->type + 1);
578    return p_ctx;
579 }
580 
581 /*****************************************************************************/
vc_container_free_track(VC_CONTAINER_T * context,VC_CONTAINER_TRACK_T * p_track)582 void vc_container_free_track( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *p_track )
583 {
584    VC_CONTAINER_PARAM_UNUSED(context);
585    if(p_track)
586    {
587       if(p_track->priv->extradata) free(p_track->priv->extradata);
588       if(p_track->priv->drmdata) free(p_track->priv->drmdata);
589       free(p_track);
590    }
591 }
592 
593 /*****************************************************************************/
vc_container_track_allocate_extradata(VC_CONTAINER_T * context,VC_CONTAINER_TRACK_T * p_track,unsigned int extra_size)594 VC_CONTAINER_STATUS_T vc_container_track_allocate_extradata( VC_CONTAINER_T *context,
595    VC_CONTAINER_TRACK_T *p_track, unsigned int extra_size )
596 {
597    VC_CONTAINER_PARAM_UNUSED(context);
598 
599    /* Sanity check the size of the extra data */
600    if(extra_size > 100*1024) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
601 
602    /* Check if we need to allocate a buffer */
603    if(extra_size > p_track->priv->extradata_size)
604    {
605       p_track->priv->extradata_size = 0;
606       if(p_track->priv->extradata) free(p_track->priv->extradata);
607       p_track->priv->extradata = malloc(extra_size);
608       p_track->format->extradata = p_track->priv->extradata;
609       if(!p_track->priv->extradata) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
610       p_track->priv->extradata_size = extra_size;
611    }
612    p_track->format->extradata = p_track->priv->extradata;
613 
614    return VC_CONTAINER_SUCCESS;
615 }
616 
617 /*****************************************************************************/
vc_container_track_allocate_drmdata(VC_CONTAINER_T * context,VC_CONTAINER_TRACK_T * p_track,unsigned int size)618 VC_CONTAINER_STATUS_T vc_container_track_allocate_drmdata( VC_CONTAINER_T *context,
619    VC_CONTAINER_TRACK_T *p_track, unsigned int size )
620 {
621    VC_CONTAINER_PARAM_UNUSED(context);
622 
623    /* Sanity check the size of the drm data */
624    if(size > 200*1024) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
625 
626    /* Check if we need to allocate a buffer */
627    if(size > p_track->priv->drmdata_size)
628    {
629       p_track->priv->drmdata_size = 0;
630       if(p_track->priv->drmdata) free(p_track->priv->drmdata);
631       p_track->priv->drmdata = malloc(size);
632       if(!p_track->priv->drmdata) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
633       p_track->priv->drmdata_size = size;
634    }
635 
636    return VC_CONTAINER_SUCCESS;
637 }
638