| @@ -32,6 +32,10 @@ · 31 unmodified lines |
| 32 | 32 | #include "internal.h" |
| 33 | 33 | #include "video.h" |
| 34 | 34 | |
| 35 | +typedef struct CacheItem { |
| 36 | + int64_t previous_pts_us; |
| 37 | +} CacheItem; |
| 38 | + |
| 35 | 39 | typedef struct GraphMonitorContext { |
| 36 | 40 | const AVClass *class; |
| 37 | 41 | |
| @@ -49,6 +53,10 @@ typedef struct GraphMonitorContext { · 11 unmodified lines |
| 49 | 53 | uint8_t green[4]; |
| 50 | 54 | uint8_t blue[4]; |
| 51 | 55 | uint8_t bg[4]; |
| 56 | + |
| 57 | + CacheItem *cache; |
| 58 | + unsigned int cache_size; |
| 59 | + unsigned int cache_index; |
| 52 | 60 | } GraphMonitorContext; |
| 53 | 61 | |
| 54 | 62 | enum { |
| @@ -64,6 +72,10 @@ enum { · 9 unmodified lines |
| 64 | 72 | MODE_EOF = 1 << 9, |
| 65 | 73 | MODE_SCIN = 1 << 10, |
| 66 | 74 | MODE_SCOUT = 1 << 11, |
| 75 | + MODE_PTS_DELTA = 1 << 12, |
| 76 | + MODE_TIME_DELTA = 1 << 13, |
| 77 | + MODE_FC_DELTA = 1 << 14, |
| 78 | + MODE_SC_DELTA = 1 << 15, |
| 67 | 79 | }; |
| 68 | 80 | |
| 69 | 81 | #define OFFSET(x) offsetof(GraphMonitorContext, x) |
| @@ -83,8 +95,11 @@ static const AVOption graphmonitor_options[] = { · 13 unmodified lines |
| 83 | 95 | { "queue", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_QUEUE}, 0, 0, VF, "flags" }, |
| 84 | 96 | { "frame_count_in", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FCOUT}, 0, 0, VF, "flags" }, |
| 85 | 97 | { "frame_count_out", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FCIN}, 0, 0, VF, "flags" }, |
| 98 | + { "frame_count_delta",NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FC_DELTA},0, 0, VF, "flags" }, |
| 86 | 99 | { "pts", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_PTS}, 0, 0, VF, "flags" }, |
| 100 | + { "pts_delta", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_PTS_DELTA},0,0, VF, "flags" }, |
| 87 | 101 | { "time", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_TIME}, 0, 0, VF, "flags" }, |
| 102 | + { "time_delta", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_TIME_DELTA},0,0,VF, "flags" }, |
| 88 | 103 | { "timebase", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_TB}, 0, 0, VF, "flags" }, |
| 89 | 104 | { "format", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FMT}, 0, 0, VF, "flags" }, |
| 90 | 105 | { "size", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_SIZE}, 0, 0, VF, "flags" }, |
| @@ -92,11 +107,24 @@ static const AVOption graphmonitor_options[] = { · 1 unmodified line |
| 92 | 107 | { "eof", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_EOF}, 0, 0, VF, "flags" }, |
| 93 | 108 | { "sample_count_in", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_SCOUT}, 0, 0, VF, "flags" }, |
| 94 | 109 | { "sample_count_out", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_SCIN}, 0, 0, VF, "flags" }, |
| 110 | + { "sample_count_delta",NULL,0, AV_OPT_TYPE_CONST, {.i64=MODE_SC_DELTA},0, 0, VF, "flags" }, |
| 95 | 111 | { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, VF }, |
| 96 | 112 | { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, VF }, |
| 97 | 113 | { NULL } |
| 98 | 114 | }; |
| 99 | 115 | |
| 116 | +static av_cold int init(AVFilterContext *ctx) |
| 117 | +{ |
| 118 | + GraphMonitorContext *s = ctx->priv; |
| 119 | + |
| 120 | + s->cache = av_fast_realloc(NULL, &s->cache_size, |
| 121 | + 8192 * sizeof(*(s->cache))); |
| 122 | + if (!s->cache) |
| 123 | + return AVERROR(ENOMEM); |
| 124 | + |
| 125 | + return 0; |
| 126 | +} |
| 127 | + |
| 100 | 128 | static int query_formats(AVFilterContext *ctx) |
| 101 | 129 | { |
| 102 | 130 | AVFilterLink *outlink = ctx->outputs[0]; |
| @@ -173,12 +201,14 @@ static int filter_have_queued(AVFilterContext *filter) · 70 unmodified lines |
| 173 | 201 | return 0; |
| 174 | 202 | } |
| 175 | 203 | |
| 176 | | −static void draw_items(AVFilterContext *ctx, AVFrame *out, |
| 177 | | − int xpos, int ypos, |
| 178 | | − AVFilterLink *l, |
| 179 | | − size_t frames) |
| 204 | +static int draw_items(AVFilterContext *ctx, AVFrame *out, |
| 205 | + int xpos, int ypos, |
| 206 | + AVFilterLink *l, |
| 207 | + size_t frames) |
| 180 | 208 | { |
| 181 | 209 | GraphMonitorContext *s = ctx->priv; |
| 210 | + int64_t previous_pts_us = s->cache[s->cache_index].previous_pts_us; |
| 211 | + int64_t current_pts_us = l->current_pts_us; |
| 182 | 212 | char buffer[1024] = { 0 }; |
| 183 | 213 | |
| 184 | 214 | if (s->flags & MODE_FMT) { |
| @@ -233,6 +263,11 @@ static void draw_items(AVFilterContext *ctx, AVFrame *out, · 48 unmodified lines |
| 233 | 263 | drawtext(out, xpos, ypos, buffer, s->white); |
| 234 | 264 | xpos += strlen(buffer) * 8; |
| 235 | 265 | } |
| 266 | + if (s->flags & MODE_FC_DELTA) { |
| 267 | + snprintf(buffer, sizeof(buffer)-1, " | delta: %"PRId64, l->frame_count_in - l->frame_count_out); |
| 268 | + drawtext(out, xpos, ypos, buffer, s->white); |
| 269 | + xpos += strlen(buffer) * 8; |
| 270 | + } |
| 236 | 271 | if (s->flags & MODE_SCIN) { |
| 237 | 272 | snprintf(buffer, sizeof(buffer)-1, " | sin: %"PRId64, l->sample_count_in); |
| 238 | 273 | drawtext(out, xpos, ypos, buffer, s->white); |
| @@ -243,13 +278,28 @@ static void draw_items(AVFilterContext *ctx, AVFrame *out, · 4 unmodified lines |
| 243 | 278 | drawtext(out, xpos, ypos, buffer, s->white); |
| 244 | 279 | xpos += strlen(buffer) * 8; |
| 245 | 280 | } |
| 281 | + if (s->flags & MODE_SC_DELTA) { |
| 282 | + snprintf(buffer, sizeof(buffer)-1, " | sdelta: %"PRId64, l->sample_count_in - l->sample_count_out); |
| 283 | + drawtext(out, xpos, ypos, buffer, s->white); |
| 284 | + xpos += strlen(buffer) * 8; |
| 285 | + } |
| 246 | 286 | if (s->flags & MODE_PTS) { |
| 247 | | − snprintf(buffer, sizeof(buffer)-1, " | pts: %s", av_ts2str(l->current_pts_us)); |
| 287 | + snprintf(buffer, sizeof(buffer)-1, " | pts: %s", av_ts2str(current_pts_us)); |
| 288 | + drawtext(out, xpos, ypos, buffer, s->white); |
| 289 | + xpos += strlen(buffer) * 8; |
| 290 | + } |
| 291 | + if (s->flags & MODE_PTS_DELTA) { |
| 292 | + snprintf(buffer, sizeof(buffer)-1, " | pts_delta: %s", av_ts2str(current_pts_us - previous_pts_us)); |
| 248 | 293 | drawtext(out, xpos, ypos, buffer, s->white); |
| 249 | 294 | xpos += strlen(buffer) * 8; |
| 250 | 295 | } |
| 251 | 296 | if (s->flags & MODE_TIME) { |
| 252 | | − snprintf(buffer, sizeof(buffer)-1, " | time: %s", av_ts2timestr(l->current_pts_us, &AV_TIME_BASE_Q)); |
| 297 | + snprintf(buffer, sizeof(buffer)-1, " | time: %s", av_ts2timestr(current_pts_us, &AV_TIME_BASE_Q)); |
| 298 | + drawtext(out, xpos, ypos, buffer, s->white); |
| 299 | + xpos += strlen(buffer) * 8; |
| 300 | + } |
| 301 | + if (s->flags & MODE_TIME_DELTA) { |
| 302 | + snprintf(buffer, sizeof(buffer)-1, " | time_delta: %s", av_ts2timestr(current_pts_us - previous_pts_us, &AV_TIME_BASE_Q)); |
| 253 | 303 | drawtext(out, xpos, ypos, buffer, s->white); |
| 254 | 304 | xpos += strlen(buffer) * 8; |
| 255 | 305 | } |
| @@ -258,6 +308,19 @@ static void draw_items(AVFilterContext *ctx, AVFrame *out, · 2 unmodified lines |
| 258 | 308 | drawtext(out, xpos, ypos, buffer, s->blue); |
| 259 | 309 | xpos += strlen(buffer) * 8; |
| 260 | 310 | } |
| 311 | + |
| 312 | + s->cache[s->cache_index].previous_pts_us = l->current_pts_us; |
| 313 | + |
| 314 | + if (s->cache_index + 1 >= s->cache_size / sizeof(*(s->cache))) { |
| 315 | + void *ptr = av_fast_realloc(s->cache, &s->cache_size, s->cache_size * 2); |
| 316 | + |
| 317 | + if (!ptr) |
| 318 | + return AVERROR(ENOMEM); |
| 319 | + s->cache = ptr; |
| 320 | + } |
| 321 | + s->cache_index++; |
| 322 | + |
| 323 | + return 0; |
| 261 | 324 | } |
| 262 | 325 | |
| 263 | 326 | static int create_frame(AVFilterContext *ctx, int64_t pts) |
| @@ -265,7 +328,7 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) · 1 unmodified line |
| 265 | 328 | GraphMonitorContext *s = ctx->priv; |
| 266 | 329 | AVFilterLink *outlink = ctx->outputs[0]; |
| 267 | 330 | AVFrame *out; |
| 268 | | − int xpos, ypos = 0; |
| 331 | + int ret, xpos, ypos = 0; |
| 269 | 332 | |
| 270 | 333 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
| 271 | 334 | if (!out) |
| @@ -273,6 +336,8 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) · 1 unmodified line |
| 273 | 336 | |
| 274 | 337 | clear_image(s, out, outlink); |
| 275 | 338 | |
| 339 | + s->cache_index = 0; |
| 340 | + |
| 276 | 341 | for (int i = 0; i < ctx->graph->nb_filters; i++) { |
| 277 | 342 | AVFilterContext *filter = ctx->graph->filters[i]; |
| 278 | 343 | char buffer[1024] = { 0 }; |
| @@ -298,7 +363,9 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) · 19 unmodified lines |
| 298 | 363 | xpos += strlen(buffer) * 8; |
| 299 | 364 | drawtext(out, xpos, ypos, l->src->name, s->white); |
| 300 | 365 | xpos += strlen(l->src->name) * 8 + 10; |
| 301 | | − draw_items(ctx, out, xpos, ypos, l, frames); |
| 366 | + ret = draw_items(ctx, out, xpos, ypos, l, frames); |
| 367 | + if (ret < 0) |
| 368 | + goto error; |
| 302 | 369 | ypos += 10; |
| 303 | 370 | } |
| 304 | 371 | |
| @@ -316,7 +383,9 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) · 11 unmodified lines |
| 316 | 383 | xpos += strlen(buffer) * 8; |
| 317 | 384 | drawtext(out, xpos, ypos, l->dst->name, s->white); |
| 318 | 385 | xpos += strlen(l->dst->name) * 8 + 10; |
| 319 | | − draw_items(ctx, out, xpos, ypos, l, frames); |
| 386 | + ret = draw_items(ctx, out, xpos, ypos, l, frames); |
| 387 | + if (ret < 0) |
| 388 | + goto error; |
| 320 | 389 | ypos += 10; |
| 321 | 390 | } |
| 322 | 391 | ypos += 5; |
| @@ -325,6 +394,9 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) · 2 unmodified lines |
| 325 | 394 | out->pts = pts; |
| 326 | 395 | s->pts = pts + 1; |
| 327 | 396 | return ff_filter_frame(outlink, out); |
| 397 | +error: |
| 398 | + av_frame_free(&out); |
| 399 | + return ret; |
| 328 | 400 | } |
| 329 | 401 | |
| 330 | 402 | static int activate(AVFilterContext *ctx) |
| @@ -386,6 +458,14 @@ static int config_output(AVFilterLink *outlink) · 55 unmodified lines |
| 386 | 458 | return 0; |
| 387 | 459 | } |
| 388 | 460 | |
| 461 | +static av_cold void uninit(AVFilterContext *ctx) |
| 462 | +{ |
| 463 | + GraphMonitorContext *s = ctx->priv; |
| 464 | + |
| 465 | + av_freep(&s->cache); |
| 466 | + s->cache_size = s->cache_index = 0; |
| 467 | +} |
| 468 | + |
| 389 | 469 | AVFILTER_DEFINE_CLASS_EXT(graphmonitor, "(a)graphmonitor", graphmonitor_options); |
| 390 | 470 | |
| 391 | 471 | #if CONFIG_GRAPHMONITOR_FILTER |
| @@ -410,6 +490,8 @@ const AVFilter ff_vf_graphmonitor = { · 18 unmodified lines |
| 410 | 490 | .description = NULL_IF_CONFIG_SMALL("Show various filtergraph stats."), |
| 411 | 491 | .priv_size = sizeof(GraphMonitorContext), |
| 412 | 492 | .priv_class = &graphmonitor_class, |
| 493 | + .init = init, |
| 494 | + .uninit = uninit, |
| 413 | 495 | .activate = activate, |
| 414 | 496 | FILTER_INPUTS(graphmonitor_inputs), |
| 415 | 497 | FILTER_OUTPUTS(graphmonitor_outputs), |
| @@ -440,6 +522,8 @@ const AVFilter ff_avf_agraphmonitor = { · 24 unmodified lines |
| 440 | 522 | .description = NULL_IF_CONFIG_SMALL("Show various filtergraph stats."), |
| 441 | 523 | .priv_class = &graphmonitor_class, |
| 442 | 524 | .priv_size = sizeof(GraphMonitorContext), |
| 525 | + .init = init, |
| 526 | + .uninit = uninit, |
| 443 | 527 | .activate = activate, |
| 444 | 528 | FILTER_INPUTS(agraphmonitor_inputs), |
| 445 | 529 | FILTER_OUTPUTS(agraphmonitor_outputs), |