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 <limits.h>
30#include <string.h>
31#include <stdio.h>
32#include "containers/containers.h"
33#include "containers/core/containers_common.h"
34#include "containers/core/containers_logging.h"
35#include "containers/core/containers_utils.h"
36#include "containers/core/containers_io.h"
37
38#define BUFFER_SIZE 256*1024
39#define MAX_TRACKS 16
40#define MAX_SEEKS 16
41
42static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader);
43static int container_test_parse_cmdline(int argc, char **argv);
44
45static const char *psz_in = 0;
46static long packets_num = 0;
47static long track_num = -1;
48static int fps = 30;
49static int margin = 5; // margin for frame interval, percent
50
51static struct
52{
53 uint32_t mapping;
54 uint32_t frames;
55 uint32_t packets;
56 uint64_t bytes;
57 uint32_t frame_size;
58 int64_t first_dts;
59 int64_t first_pts;
60 int64_t last_dts;
61 int64_t last_pts;
62} tracks[MAX_TRACKS];
63
64static int32_t verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO;
65
66/*****************************************************************************/
67int main(int argc, char **argv)
68{
69 int retval = 0;
70 VC_CONTAINER_T *p_ctx = 0;
71 VC_CONTAINER_STATUS_T status;
72 unsigned int i;
73 uint8_t *buffer = malloc(BUFFER_SIZE);
74 int32_t interval;
75 int64_t last_packet_pts = -1;
76 int32_t max_interval = 0, max_interval_after_first = 0;
77 int fail = 0;
78
79 if(container_test_parse_cmdline(argc, argv))
80 goto error_silent;
81
82 LOG_INFO (0, "Require that no frame interval greater than 1/%d seconds (%d%% margin)", fps, margin);
83
84 /* Set the general verbosity */
85 vc_container_log_set_verbosity(0, verbosity);
86 vc_container_log_set_default_verbosity(verbosity);
87
88 p_ctx = vc_container_open_reader(psz_in, &status, 0, 0);
89
90 if(!p_ctx)
91 {
92 LOG_ERROR(0, "error opening file %s (%i)", psz_in, status);
93 goto error;
94 }
95
96 if (verbosity & VC_CONTAINER_LOG_DEBUG)
97 {
98 container_test_info(p_ctx, true);
99 }
100 LOG_INFO (0, "Search for video track only");
101
102 /* Disabling tracks which are not requested and enable packetisation if requested */
103 for(i = 0; i < p_ctx->tracks_num; i++)
104 {
105 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i];
106 track->is_enabled = (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO);
107 }
108
109
110 LOG_DEBUG(0, "TEST start reading");
111 for(i = 0; !packets_num || (long)i < packets_num; i++)
112 {
113 VC_CONTAINER_PACKET_T packet = {0};
114 int32_t frame_num = 0;
115
116 status = vc_container_read(p_ctx, &packet, VC_CONTAINER_READ_FLAG_INFO);
117 if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST info status: %i", status); break;}
118
119 if(packet.track < MAX_TRACKS)
120 {
121 if((packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_START))
122 {
123 tracks[packet.track].frames++;
124 tracks[packet.track].frame_size = 0;
125 }
126 frame_num = tracks[packet.track].frames;
127 }
128
129 tracks[packet.track].frame_size += packet.size;
130
131// LOG_DEBUG(0, "packet info: track %i, size %i/%i/%i, pts %"PRId64", flags %x%s, num %i",
132// packet.track, packet.size, packet.frame_size, tracks[packet.track].frame_size, packet.pts, packet.flags,
133// (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "",
134// frame_num-1);
135
136 if (last_packet_pts != -1)
137 interval = packet.pts - last_packet_pts;
138 else
139 interval = 0; // packet.pts;
140 last_packet_pts = packet.pts;
141 max_interval = MAX (max_interval, interval);
142 if (i >= 2)
143 max_interval_after_first = MAX (max_interval_after_first, interval);
144
145 /* Check if interval (in us) exceeds 1/fps, with percentage margin */
146 if (interval * fps > 1e4 * (100+margin))
147 {
148 LOG_INFO (0, "Frame %d, interval %.3f FAILED", i, interval / 1000.0f);
149 fail = 1;
150 }
151
152 LOG_DEBUG (0, "Frame %d, interval %.3f", i, interval / 1000.0f);
153
154 if(track_num >= 0 && packet.track != (uint32_t)track_num)
155 {
156 status = vc_container_read(p_ctx, 0, VC_CONTAINER_READ_FLAG_SKIP);
157 if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST skip status: %i", status); break;}
158 continue;
159 }
160
161 packet.buffer_size = BUFFER_SIZE;
162 packet.data = buffer;
163 packet.size = 0;
164 status = vc_container_read(p_ctx, &packet, 0);
165 if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST read status: %i", status); break;}
166
167 // LOG_DEBUG(0, "packet: track %i, size %i, pts %"PRId64", flags %x", packet.track, packet.size, packet.pts, packet.flags);
168
169 if (tracks[packet.track].packets)
170 {
171 tracks[packet.track].last_dts = packet.dts;
172 tracks[packet.track].last_pts = packet.pts;
173 } else {
174 tracks[packet.track].first_dts = packet.dts;
175 tracks[packet.track].first_pts = packet.pts;
176 }
177
178 if(packet.track < MAX_TRACKS)
179 {
180 tracks[packet.track].packets++;
181 tracks[packet.track].bytes += packet.size;
182 }
183
184 }
185 LOG_DEBUG(0, "TEST stop reading");
186
187 /* Output stats */
188 for(i = 0; i < p_ctx->tracks_num; i++)
189 {
190 LOG_INFO(0, "track %u: read %u samples in %u packets for a total of %"PRIu64" bytes",
191 i, tracks[i].frames, tracks[i].packets, tracks[i].bytes);
192 LOG_INFO(0, "Starting at %"PRId64"us (decode at %"PRId64"us), ending at %"PRId64"us (decode at %"PRId64"us)",
193 tracks[i].first_pts, tracks[i].first_dts, tracks[i].last_pts, tracks[i].last_dts);
194 }
195
196 LOG_INFO (0, "---\nMax interval = %.3f ms; max interval (after first) = %.3f ms\n", (float) max_interval / 1000.0, (float) max_interval_after_first / 1000.0);
197
198 end:
199 if(p_ctx) vc_container_close(p_ctx);
200 free(buffer);
201
202#ifdef _MSC_VER
203 getchar();
204#endif
205
206 retval = fail;
207 if (fail)
208 {
209 LOG_INFO (0, "TEST FAILED: highest frame interval = %.3f ms", max_interval / 1000.0f);
210 }
211 else
212 LOG_INFO (0, "TEST PASSED");
213 return retval;
214
215 error:
216 LOG_ERROR(0, "TEST FAILED TO RUN");
217 error_silent:
218 retval = -1;
219 goto end;
220}
221
222static int container_test_parse_cmdline(int argc, char **argv)
223{
224 int i, j, k;
225 int32_t *p_verbosity;
226
227 /* Parse the command line arguments */
228 for(i = 1; i < argc; i++)
229 {
230 if(!argv[i]) continue;
231
232 if(argv[i][0] != '-')
233 {
234 /* Not an option argument so will be the input URI */
235 psz_in = argv[i];
236 continue;
237 }
238
239 /* We are now dealing with command line options */
240 switch(argv[i][1])
241 {
242 case 'v':
243 j = 2;
244 p_verbosity = &verbosity;
245 *p_verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO;
246 for(k = 0; k < 2 && argv[i][j+k] == 'v'; k++)
247 *p_verbosity = (*p_verbosity << 1) | 1 ;
248 break;
249 case 'f':
250 if(i+1 == argc || !argv[i+1]) goto invalid_option;
251 fps = strtol(argv[++i], 0, 0);
252 break;
253 case 'h': goto usage;
254 default: goto invalid_option;
255 }
256 continue;
257 }
258
259 /* Sanity check that we have at least an input uri */
260 if(!psz_in)
261 {
262 LOG_ERROR(0, "missing uri argument");
263 goto usage;
264 }
265
266 return 0;
267
268 invalid_option:
269 LOG_ERROR(0, "invalid command line option (%s)", argv[i]);
270
271 usage:
272 psz_in = strrchr(argv[0], '\\'); if(psz_in) psz_in++;
273 if(!psz_in) {psz_in = strrchr(argv[0], '/'); if(psz_in) psz_in++;}
274 if(!psz_in) psz_in = argv[0];
275 LOG_INFO(0, "");
276 LOG_INFO(0, "usage: %s [options] uri", psz_in);
277 LOG_INFO(0, "options list:");
278 LOG_INFO(0, " -vxx : general verbosity level (replace xx with a number of \'v\')");
279 LOG_INFO(0, " -f : required frame rate/second (frame interval must not exceed 1/f)");
280 LOG_INFO(0, " -h : help");
281 return 1;
282}
283
284static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader)
285{
286 const char *name_type;
287 unsigned int i;
288
289 LOG_INFO(0, "");
290 if(b_reader) LOG_INFO(0, "----Reader Information----");
291 else LOG_INFO(0, "----Writer Information----");
292
293 LOG_INFO(0, "duration: %2.2fs, size: %"PRId64, ctx->duration/1000000.0, ctx->size);
294 LOG_INFO(0, "capabilities: %x", ctx->capabilities);
295 LOG_INFO(0, "");
296
297 for(i = 0; i < ctx->tracks_num; i++)
298 {
299 VC_CONTAINER_TRACK_T *track = ctx->tracks[i];
300
301 switch(track->format->es_type)
302 {
303 case VC_CONTAINER_ES_TYPE_AUDIO: name_type = "audio"; break;
304 case VC_CONTAINER_ES_TYPE_VIDEO: name_type = "video"; break;
305 case VC_CONTAINER_ES_TYPE_SUBPICTURE: name_type = "subpicture"; break;
306 default: name_type = "unknown"; break;
307 }
308
309 if (!strcmp (name_type, "video"))
310 {
311 LOG_INFO(0, "track: %i, type: %s, fourcc: %4.4s", i, name_type, (char *)&track->format->codec);
312 LOG_INFO(0, " bitrate: %i, framed: %i, enabled: %i", track->format->bitrate,
313 !!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED), track->is_enabled);
314 LOG_INFO(0, " extra data: %i, %p", track->format->extradata_size, track->format->extradata);
315 switch(track->format->es_type)
316 {
317 case VC_CONTAINER_ES_TYPE_VIDEO:
318 LOG_INFO(0, " width: %i, height: %i, (%i,%i,%i,%i)",
319 track->format->type->video.width, track->format->type->video.height,
320 track->format->type->video.x_offset, track->format->type->video.y_offset,
321 track->format->type->video.visible_width, track->format->type->video.visible_height);
322 LOG_INFO(0, " pixel aspect ratio: %i/%i, frame rate: %i/%i",
323 track->format->type->video.par_num, track->format->type->video.par_den,
324 track->format->type->video.frame_rate_num, track->format->type->video.frame_rate_den);
325 break;
326
327 default: break;
328 }
329 }
330 }
331
332 for (i = 0; i < ctx->meta_num; ++i)
333 {
334 const char *name, *value;
335 if (i == 0) LOG_INFO(0, "");
336 name = vc_container_metadata_id_to_string(ctx->meta[i]->key);
337 value = ctx->meta[i]->value;
338 if(!name) continue;
339 LOG_INFO(0, "metadata(%i) : %s : %s", i, name, value);
340 }
341
342 LOG_INFO(0, "--------------------------");
343 LOG_INFO(0, "");
344
345 return 0;
346}
347
348
349