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 <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 | |
42 | static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader); |
43 | static int container_test_parse_cmdline(int argc, char **argv); |
44 | |
45 | static const char *psz_in = 0; |
46 | static long packets_num = 0; |
47 | static long track_num = -1; |
48 | static int fps = 30; |
49 | static int margin = 5; // margin for frame interval, percent |
50 | |
51 | static 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 | |
64 | static int32_t verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; |
65 | |
66 | /*****************************************************************************/ |
67 | int 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 | |
222 | static 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 | |
284 | static 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 | |