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 | #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 | |
41 | static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader); |
42 | static int container_test_parse_cmdline(int argc, char **argv); |
43 | |
44 | /* Client i/o wrapper */ |
45 | static VC_CONTAINER_IO_T *client_io_open(const char *, VC_CONTAINER_STATUS_T *); |
46 | |
47 | static bool b_info = 0, b_seek = 0, b_dump = 0; |
48 | static bool b_audio = 1, b_video = 1, b_subs = 1, b_errorcode = 1; |
49 | static const char *psz_in = 0, *psz_out = 0; |
50 | static long packets_num = 0; |
51 | static long track_num = -1; |
52 | static FILE *dump_file = 0; |
53 | static bool b_client_io = 0; |
54 | static bool b_packetize = 0; |
55 | |
56 | static 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 | |
69 | static unsigned int seeks = 0; |
70 | static long seek_offsets[MAX_SEEKS]; |
71 | static long seek_flags[MAX_SEEKS]; |
72 | static int32_t verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; |
73 | static int32_t verbosity_input = -1, verbosity_output = -1; |
74 | |
75 | /*****************************************************************************/ |
76 | int 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 | |
350 | static 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 | |
461 | static 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 | *****************************************************************************/ |
539 | static 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 | /*****************************************************************************/ |
547 | static 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 | /*****************************************************************************/ |
564 | static 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 | /*****************************************************************************/ |
590 | static 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 | |