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#include <stdlib.h>
28#include <limits.h>
29#include <string.h>
30#include <stdio.h>
31#include "containers/containers.h"
32#include "containers/core/containers_common.h"
33#include "containers/core/containers_logging.h"
34#include "containers/core/containers_utils.h"
35#include "containers/core/containers_io.h"
36
37#define BUFFER_SIZE 256*1024
38#define MAX_TRACKS 16
39#define MAX_SEEKS 16
40
41static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader);
42static int container_test_parse_cmdline(int argc, char **argv);
43
44/* Client i/o wrapper */
45static VC_CONTAINER_IO_T *client_io_open(const char *, VC_CONTAINER_STATUS_T *);
46
47static bool b_info = 0, b_seek = 0, b_dump = 0;
48static bool b_audio = 1, b_video = 1, b_subs = 1, b_errorcode = 1;
49static const char *psz_in = 0, *psz_out = 0;
50static long packets_num = 0;
51static long track_num = -1;
52static FILE *dump_file = 0;
53static bool b_client_io = 0;
54static bool b_packetize = 0;
55
56static struct
57{
58 uint32_t mapping;
59 uint32_t frames;
60 uint32_t packets;
61 uint64_t bytes;
62 uint32_t frame_size;
63 int64_t first_dts;
64 int64_t first_pts;
65 int64_t last_dts;
66 int64_t last_pts;
67} tracks[MAX_TRACKS];
68
69static unsigned int seeks = 0;
70static long seek_offsets[MAX_SEEKS];
71static long seek_flags[MAX_SEEKS];
72static int32_t verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO;
73static int32_t verbosity_input = -1, verbosity_output = -1;
74
75/*****************************************************************************/
76int main(int argc, char **argv)
77{
78 int retval = 0;
79 VC_CONTAINER_T *p_ctx = 0, *p_writer_ctx = 0;
80 VC_CONTAINER_STATUS_T status;
81 unsigned int i, j;
82 uint8_t *buffer = malloc(BUFFER_SIZE);
83 int64_t seek_time;
84
85 if(container_test_parse_cmdline(argc, argv))
86 goto error_silent;
87
88 /* Set the general verbosity */
89 vc_container_log_set_verbosity(0, verbosity);
90
91 if(verbosity_input < 0) verbosity_input = verbosity;
92 if(verbosity_output < 0) verbosity_output = verbosity;
93
94 /* Open a dump file if it was requested */
95 if(b_dump)
96 {
97 char *psz_dump;
98
99 if (psz_out)
100 {
101 psz_dump = vcos_strdup(psz_out);
102 } else {
103 psz_dump = strrchr(psz_in, '\\'); if(psz_dump) psz_dump++;
104 if(!psz_dump) {psz_dump = strrchr(psz_in, '/'); if(psz_dump) psz_dump++;}
105 if(!psz_dump) psz_dump = vcos_strdup(psz_in);
106 else psz_dump = vcos_strdup(psz_dump);
107 psz_dump[strlen(psz_dump)-1] = '1';
108 }
109 dump_file = fopen(psz_dump, "wb");
110 if(!dump_file) LOG_ERROR(0, "error opening dump file %s", psz_dump);
111 else LOG_INFO(0, "data packets will dumped to %s", psz_dump);
112 free(psz_dump);
113 if(!dump_file) goto error;
114 }
115
116 /* Open a writer if an output was requested */
117 if(psz_out && !b_dump)
118 {
119 vc_container_log_set_default_verbosity(verbosity_output);
120 p_writer_ctx = vc_container_open_writer(psz_out, &status, 0, 0);
121 if(!p_writer_ctx)
122 {
123 LOG_ERROR(0, "error opening file %s (%i)", psz_out, status);
124 goto error;
125 }
126 }
127
128 vc_container_log_set_default_verbosity(verbosity_input);
129
130 /* Open the container */
131 if(b_client_io)
132 {
133 VC_CONTAINER_IO_T *p_io;
134
135 LOG_INFO(0, "Using client I/O for %s", psz_in);
136 p_io = client_io_open(psz_in, &status);
137 if(!p_io)
138 {
139 LOG_ERROR(0, "error creating io for %s (%i)", psz_in, status);
140 goto error;
141 }
142
143 p_ctx = vc_container_open_reader_with_io(p_io, psz_in, &status, 0, 0);
144 if(!p_ctx)
145 vc_container_io_close(p_io);
146 }
147 else
148 {
149 p_ctx = vc_container_open_reader(psz_in, &status, 0, 0);
150 }
151
152 if(!p_ctx)
153 {
154 LOG_ERROR(0, "error opening file %s (%i)", psz_in, status);
155 goto error;
156 }
157
158 /* Disabling tracks which are not requested and enable packetisation if requested */
159 for(i = 0; i < p_ctx->tracks_num; i++)
160 {
161 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i];
162 unsigned int disable = 0;
163
164 switch(track->format->es_type)
165 {
166 case VC_CONTAINER_ES_TYPE_VIDEO: if(!b_video) disable = 1; break;
167 case VC_CONTAINER_ES_TYPE_AUDIO: if(!b_audio) disable = 1; break;
168 case VC_CONTAINER_ES_TYPE_SUBPICTURE: if(!b_subs) disable = 1; break;
169 default: break;
170 }
171 if(disable)
172 {
173 track->is_enabled = 0;
174 LOG_INFO(0, "disabling track: %i, fourcc: %4.4s", i, (char *)&track->format->codec);
175 }
176
177 if(track->is_enabled && b_packetize && !(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED))
178 {
179 status = vc_container_control(p_ctx, VC_CONTAINER_CONTROL_TRACK_PACKETIZE, i, track->format->codec_variant);
180 if(status != VC_CONTAINER_SUCCESS)
181 {
182 LOG_ERROR(0, "packetization not supported on track: %i, fourcc: %4.4s", i, (char *)&track->format->codec);
183 track->is_enabled = 0;
184 }
185 }
186 }
187
188 container_test_info(p_ctx, true);
189 if(b_info) goto end;
190
191 if(p_writer_ctx)
192 {
193 LOG_INFO(0, "----Writer Information----");
194 for(i = 0; i < p_ctx->tracks_num; i++)
195 {
196 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i];
197 if(!track->is_enabled) continue;
198 tracks[p_writer_ctx->tracks_num].mapping = i;
199 LOG_INFO(0, "adding track: %i, fourcc: %4.4s", i, (char *)&track->format->codec);
200 status = vc_container_control(p_writer_ctx, VC_CONTAINER_CONTROL_TRACK_ADD, track->format);
201 if(status)
202 {
203 LOG_INFO(0, "unsupported track type (%i, %i)", status, i);
204 track->is_enabled = 0; /* disable track */
205 }
206 }
207 if(p_writer_ctx->tracks_num)
208 {
209 status = vc_container_control(p_writer_ctx, VC_CONTAINER_CONTROL_TRACK_ADD_DONE);
210 if(status) LOG_INFO(0, "could not add tracks (%i)", status);
211 }
212 LOG_INFO(0, "--------------------------");
213 LOG_INFO(0, "");
214 }
215
216 for(i = 0; i < seeks; i++)
217 {
218 LOG_DEBUG(0, "TEST seek to %ims", seek_offsets[i]);
219 seek_time = ((int64_t)(seek_offsets[i])) * 1000;
220 status = vc_container_seek(p_ctx, &seek_time, VC_CONTAINER_SEEK_MODE_TIME, seek_flags[i]);
221 LOG_DEBUG(0, "TEST seek done (%i) to %ims", status, (int)(seek_time/1000));
222 }
223
224 LOG_DEBUG(0, "TEST start reading");
225 for(i = 0; !packets_num || (long)i < packets_num; i++)
226 {
227 VC_CONTAINER_PACKET_T packet = {0};
228 int32_t frame_num = 0;
229
230 status = vc_container_read(p_ctx, &packet, VC_CONTAINER_READ_FLAG_INFO);
231 if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST info status: %i", status); break;}
232
233 if(packet.track < MAX_TRACKS)
234 {
235 if((packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_START))
236 {
237 tracks[packet.track].frames++;
238 tracks[packet.track].frame_size = 0;
239 }
240 frame_num = tracks[packet.track].frames;
241 }
242
243 tracks[packet.track].frame_size += packet.size;
244
245 LOG_DEBUG(0, "packet info: track %i, size %i/%i/%i, pts %"PRId64", flags %x%s, num %i",
246 packet.track, packet.size, packet.frame_size, tracks[packet.track].frame_size, packet.pts, packet.flags,
247 (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "",
248 frame_num-1);
249
250 if(track_num >= 0 && packet.track != (uint32_t)track_num)
251 {
252 status = vc_container_read(p_ctx, 0, VC_CONTAINER_READ_FLAG_SKIP);
253 if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST skip status: %i", status); break;}
254 continue;
255 }
256
257 packet.buffer_size = BUFFER_SIZE;
258 packet.data = buffer;
259 packet.size = 0;
260 status = vc_container_read(p_ctx, &packet, 0);
261 if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST read status: %i", status); break;}
262
263 LOG_DEBUG(0, "packet: track %i, size %i, pts %"PRId64", flags %x", packet.track, packet.size, packet.pts, packet.flags);
264
265 if (tracks[packet.track].packets)
266 {
267 tracks[packet.track].last_dts = packet.dts;
268 tracks[packet.track].last_pts = packet.pts;
269 } else {
270 tracks[packet.track].first_dts = packet.dts;
271 tracks[packet.track].first_pts = packet.pts;
272 }
273
274 if(packet.track < MAX_TRACKS)
275 {
276 tracks[packet.track].packets++;
277 tracks[packet.track].bytes += packet.size;
278 }
279
280 if(dump_file) fwrite(packet.data, packet.size, 1, dump_file);
281
282 if(p_writer_ctx)
283 {
284 packet.track = tracks[packet.track].mapping;
285 status = vc_container_write(p_writer_ctx, &packet);
286 if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST write status: %i", status); break;}
287 }
288 }
289 LOG_DEBUG(0, "TEST stop reading");
290
291 if(b_seek)
292 {
293 LOG_DEBUG(0, "TEST start seeking");
294 for(j = 0, seek_time = 100; j < 20; j++)
295 {
296 LOG_DEBUG(0, "seeking to %ims", (int)(seek_time/1000));
297 status = vc_container_seek(p_ctx, &seek_time, VC_CONTAINER_SEEK_MODE_TIME, VC_CONTAINER_SEEK_FLAG_FORWARD);
298 LOG_DEBUG(0, "seek done (%i) to %ims", status, (int)(seek_time/1000));
299
300 for(i = 0; i < 1; i++)
301 {
302 VC_CONTAINER_PACKET_T packet = {0};
303 packet.buffer_size = BUFFER_SIZE;
304 packet.data = buffer;
305
306 status = vc_container_read(p_ctx, &packet, 0);
307 if(status) LOG_DEBUG(0, "TEST read status: %i", status);
308 if(status == VC_CONTAINER_ERROR_EOS) break;
309 if(status == VC_CONTAINER_ERROR_CORRUPTED) break;
310 if(status == VC_CONTAINER_ERROR_FORMAT_INVALID) break;
311 seek_time = packet.pts + 800000;
312 }
313 }
314 LOG_DEBUG(0, "TEST stop seeking");
315 }
316
317 /* Output stats */
318 for(i = 0; i < p_ctx->tracks_num; i++)
319 {
320 LOG_INFO(0, "track %u: read %u samples in %u packets for a total of %"PRIu64" bytes",
321 i, tracks[i].frames, tracks[i].packets, tracks[i].bytes);
322 LOG_INFO(0, "Starting at %"PRId64"us (decode at %"PRId64"us), ending at %"PRId64"us (decode at %"PRId64"us)",
323 tracks[i].first_pts, tracks[i].first_dts, tracks[i].last_pts, tracks[i].last_dts);
324 }
325
326 end:
327 if(p_ctx) vc_container_close(p_ctx);
328 if(p_writer_ctx)
329 {
330 container_test_info(p_writer_ctx, false);
331 vc_container_close(p_writer_ctx);
332 }
333 if(dump_file) fclose(dump_file);
334 free(buffer);
335
336#ifdef _MSC_VER
337 getchar();
338#endif
339
340 LOG_ERROR(0, "TEST ENDED (%i)", retval);
341 return b_errorcode ? retval : 0;
342
343 error:
344 LOG_ERROR(0, "TEST FAILED");
345 error_silent:
346 retval = -1;
347 goto end;
348}
349
350static int container_test_parse_cmdline(int argc, char **argv)
351{
352 int i, j, k;
353 int32_t *p_verbosity;
354
355 /* Parse the command line arguments */
356 for(i = 1; i < argc; i++)
357 {
358 if(!argv[i]) continue;
359
360 if(argv[i][0] != '-')
361 {
362 /* Not an option argument so will be the input URI */
363 psz_in = argv[i];
364 continue;
365 }
366
367 /* We are now dealing with command line options */
368 switch(argv[i][1])
369 {
370 case 'i': b_info = 1; break;
371 case 'S': b_seek = 1; break;
372 case 'd': b_dump = 1; break;
373 case 'c': b_client_io = 1; break;
374 case 'v':
375 if(argv[i][2] == 'i') {j = 3; p_verbosity = &verbosity_input;}
376 else if(argv[i][2] == 'o') {j = 3; p_verbosity = &verbosity_output;}
377 else {j = 2; p_verbosity = &verbosity;}
378 *p_verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO;
379 for(k = 0; k < 2 && argv[i][j+k] == 'v'; k++)
380 *p_verbosity = (*p_verbosity << 1) | 1 ;
381 break;
382 case 's':
383 if(i+1 == argc || !argv[i+1]) goto invalid_option;
384 if(seeks >= MAX_SEEKS) goto invalid_option;
385 seek_flags[seeks] = argv[i][2] == 'f' ? VC_CONTAINER_SEEK_FLAG_FORWARD : 0;
386 seek_offsets[seeks] = strtol(argv[++i], 0, 0);
387 if(seek_offsets[seeks] < 0 || seek_offsets[seeks] == LONG_MAX) goto invalid_option;
388 seeks++;
389 break;
390 case 'n':
391 if(argv[i][2] == 'a') b_audio = 0;
392 else if(argv[i][2] == 'v') b_video = 0;
393 else if(argv[i][2] == 's') b_subs = 0;
394 else if(argv[i][2] == 'r') b_errorcode = 0;
395 else goto invalid_option;
396 break;
397 case 'e':
398 if(argv[i][2] == 'p') b_packetize = 1;
399 else goto invalid_option;
400 break;
401 case 'o':
402 if(i+1 == argc || !argv[i+1] || argv[i+1][0] == '-') goto invalid_option;
403 psz_out = argv[++i];
404 break;
405 case 'p':
406 if(i+1 == argc || !argv[i+1]) goto invalid_option;
407 packets_num = strtol(argv[++i], 0, 0);
408 if(packets_num < 0 || packets_num == LONG_MAX) goto invalid_option;
409 break;
410 case 't':
411 if(i+1 == argc || !argv[i+1]) goto invalid_option;
412 track_num = strtol(argv[++i], 0, 0);
413 if(track_num == LONG_MIN || track_num == LONG_MAX) goto invalid_option;
414 break;
415 case 'h': goto usage;
416 default: goto invalid_option;
417 }
418 continue;
419 }
420
421 /* Sanity check that we have at least an input uri */
422 if(!psz_in)
423 {
424 LOG_ERROR(0, "missing uri argument");
425 goto usage;
426 }
427
428 return 0;
429
430 invalid_option:
431 LOG_ERROR(0, "invalid command line option (%s)", argv[i]);
432
433 usage:
434 psz_in = strrchr(argv[0], '\\'); if(psz_in) psz_in++;
435 if(!psz_in) {psz_in = strrchr(argv[0], '/'); if(psz_in) psz_in++;}
436 if(!psz_in) psz_in = argv[0];
437 LOG_INFO(0, "");
438 LOG_INFO(0, "usage: %s [options] uri", psz_in);
439 LOG_INFO(0, "options list:");
440 LOG_INFO(0, " -i : only print information on the container");
441 LOG_INFO(0, " -p X : read only X packets from the container");
442 LOG_INFO(0, " -t X : read only packets from track X");
443 LOG_INFO(0, " -s X : seek to X milliseconds before starting reading");
444 LOG_INFO(0, " -sf X : seek forward to X milliseconds before starting reading");
445 LOG_INFO(0, " -S : do seek testing");
446 LOG_INFO(0, " -d : dump the data read from the container to files (-o to name file)");
447 LOG_INFO(0, " -o uri: output to another uri (i.e. re-muxing)");
448 LOG_INFO(0, " -na : disable audio");
449 LOG_INFO(0, " -nv : disable video");
450 LOG_INFO(0, " -ns : disable subtitles");
451 LOG_INFO(0, " -nr : always return an error code of 0 (even in case of failure)");
452 LOG_INFO(0, " -ep : enable packetization if data is not already packetized");
453 LOG_INFO(0, " -c : use the client i/o functions");
454 LOG_INFO(0, " -vxx : general verbosity level (replace xx with a number of \'v\')");
455 LOG_INFO(0, " -vixx : verbosity specific to the input container");
456 LOG_INFO(0, " -voxx : verbosity specific to the output container");
457 LOG_INFO(0, " -h : help");
458 return 1;
459}
460
461static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader)
462{
463 const char *name_type;
464 unsigned int i;
465
466 LOG_INFO(0, "");
467 if(b_reader) LOG_INFO(0, "----Reader Information----");
468 else LOG_INFO(0, "----Writer Information----");
469
470 LOG_INFO(0, "duration: %2.2fs, size: %"PRId64, ctx->duration/1000000.0, ctx->size);
471 LOG_INFO(0, "capabilities: %x", ctx->capabilities);
472 LOG_INFO(0, "");
473
474 for(i = 0; i < ctx->tracks_num; i++)
475 {
476 VC_CONTAINER_TRACK_T *track = ctx->tracks[i];
477
478 switch(track->format->es_type)
479 {
480 case VC_CONTAINER_ES_TYPE_AUDIO: name_type = "audio"; break;
481 case VC_CONTAINER_ES_TYPE_VIDEO: name_type = "video"; break;
482 case VC_CONTAINER_ES_TYPE_SUBPICTURE: name_type = "subpicture"; break;
483 default: name_type = "unknown"; break;
484 }
485
486 LOG_INFO(0, "track: %i, type: %s, fourcc: %4.4s", i, name_type, (char *)&track->format->codec);
487 LOG_INFO(0, " bitrate: %i, framed: %i, enabled: %i", track->format->bitrate,
488 !!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED), track->is_enabled);
489 LOG_INFO(0, " extra data: %i, %p", track->format->extradata_size, track->format->extradata);
490 switch(track->format->es_type)
491 {
492 case VC_CONTAINER_ES_TYPE_AUDIO:
493 LOG_INFO(0, " samplerate: %i, channels: %i, bps: %i, block align: %i",
494 track->format->type->audio.sample_rate, track->format->type->audio.channels,
495 track->format->type->audio.bits_per_sample, track->format->type->audio.block_align);
496 LOG_INFO(0, " gapless delay: %i gapless padding: %i",
497 track->format->type->audio.gap_delay, track->format->type->audio.gap_padding);
498 LOG_INFO(0, " language: %4.4s", track->format->language);
499 break;
500
501 case VC_CONTAINER_ES_TYPE_VIDEO:
502 LOG_INFO(0, " width: %i, height: %i, (%i,%i,%i,%i)",
503 track->format->type->video.width, track->format->type->video.height,
504 track->format->type->video.x_offset, track->format->type->video.y_offset,
505 track->format->type->video.visible_width, track->format->type->video.visible_height);
506 LOG_INFO(0, " pixel aspect ratio: %i/%i, frame rate: %i/%i",
507 track->format->type->video.par_num, track->format->type->video.par_den,
508 track->format->type->video.frame_rate_num, track->format->type->video.frame_rate_den);
509 break;
510
511 case VC_CONTAINER_ES_TYPE_SUBPICTURE:
512 LOG_INFO(0, " language: %4.4s, encoding: %i", track->format->language,
513 track->format->type->subpicture.encoding);
514 break;
515
516 default: break;
517 }
518 }
519
520 for (i = 0; i < ctx->meta_num; ++i)
521 {
522 const char *name, *value;
523 if (i == 0) LOG_INFO(0, "");
524 name = vc_container_metadata_id_to_string(ctx->meta[i]->key);
525 value = ctx->meta[i]->value;
526 if(!name) continue;
527 LOG_INFO(0, "metadata(%i) : %s : %s", i, name, value);
528 }
529
530 LOG_INFO(0, "--------------------------");
531 LOG_INFO(0, "");
532
533 return 0;
534}
535
536/*****************************************************************************
537 * Client I/O wrapper. Only used when the right cmd line option is passed.
538 *****************************************************************************/
539static VC_CONTAINER_STATUS_T client_io_close( VC_CONTAINER_IO_T *p_ctx )
540{
541 FILE *fd = (FILE *)p_ctx->module;
542 fclose(fd);
543 return VC_CONTAINER_SUCCESS;
544}
545
546/*****************************************************************************/
547static size_t client_io_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size)
548{
549 FILE *fd = (FILE *)p_ctx->module;
550 size_t ret = fread(buffer, 1, size, fd);
551 if(ret != size)
552 {
553 /* Sanity check return value. Some platforms (e.g. Android) can return -1 */
554 if( ((int)ret) < 0 ) ret = 0;
555
556 if( feof(fd) ) p_ctx->status = VC_CONTAINER_ERROR_EOS;
557 else p_ctx->status = VC_CONTAINER_ERROR_FAILED;
558 }
559 LOG_DEBUG( 0, "read: %i", ret );
560 return ret;
561}
562
563/*****************************************************************************/
564static VC_CONTAINER_STATUS_T client_io_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset)
565{
566 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
567 FILE *fd = (FILE *)p_ctx->module;
568 int ret;
569
570 //FIXME: no large file support
571 if (offset > (int64_t)UINT_MAX)
572 {
573 LOG_ERROR( 0, "no large file support");
574 p_ctx->status = VC_CONTAINER_ERROR_EOS;
575 return VC_CONTAINER_ERROR_EOS;
576 }
577
578 ret = fseek(fd, (long)offset, SEEK_SET);
579 if(ret)
580 {
581 if( feof(fd) ) status = VC_CONTAINER_ERROR_EOS;
582 else status = VC_CONTAINER_ERROR_FAILED;
583 }
584
585 p_ctx->status = status;
586 return status;
587}
588
589/*****************************************************************************/
590static VC_CONTAINER_IO_T *client_io_open( const char *psz_uri, VC_CONTAINER_STATUS_T *status )
591{
592 VC_CONTAINER_IO_T *p_io;
593 VC_CONTAINER_IO_CAPABILITIES_T capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING;
594 FILE *fd;
595
596 fd = fopen(psz_uri, "rb");
597 if (!fd)
598 {
599 *status = VC_CONTAINER_ERROR_URI_OPEN_FAILED;
600 return NULL;
601 }
602
603 p_io = vc_container_io_create( psz_uri, 0, capabilities, status );
604 if(!p_io)
605 {
606 LOG_ERROR(0, "error creating io (%i)", *status);
607 fclose(fd);
608 return NULL;
609 }
610
611 p_io->module = (struct VC_CONTAINER_IO_MODULE_T *)fd;
612 p_io->pf_close = client_io_close;
613 p_io->pf_read = client_io_read;
614 p_io->pf_seek = client_io_seek;
615
616 //FIXME: no large file support
617 fseek(fd, 0, SEEK_END);
618 p_io->size = ftell(fd);
619 fseek(fd, 0, SEEK_SET);
620
621 *status = VC_CONTAINER_SUCCESS;
622 return p_io;
623}
624