rate_hist.c (8271B)
1 /* 2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved. 3 * 4 * This source code is subject to the terms of the BSD 2 Clause License and 5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License 6 * was not distributed with this source code in the LICENSE file, you can 7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open 8 * Media Patent License 1.0 was not distributed with this source code in the 9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent. 10 */ 11 12 #include "stats/rate_hist.h" 13 14 #include <assert.h> 15 #include <limits.h> 16 #include <math.h> 17 #include <stdint.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 21 #define RATE_BINS 100 22 #define HIST_BAR_MAX 40 23 24 struct hist_bucket { 25 int low; 26 int high; 27 int count; 28 }; 29 30 struct rate_hist { 31 int64_t *pts; 32 int *sz; 33 int samples; 34 int frames; 35 struct hist_bucket bucket[RATE_BINS]; 36 int total; 37 }; 38 39 struct rate_hist *init_rate_histogram(const aom_codec_enc_cfg_t *cfg, 40 const aom_rational_t *fps) { 41 int i; 42 struct rate_hist *hist = calloc(1, sizeof(*hist)); 43 44 if (hist == NULL || cfg == NULL || fps == NULL || fps->num == 0 || 45 fps->den == 0) { 46 goto fail; 47 } 48 49 // Determine the number of samples in the buffer. Use the file's framerate 50 // to determine the number of frames in rc_buf_sz milliseconds, with an 51 // adjustment (5/4) to account for alt-refs 52 hist->samples = 53 (int)((int64_t)cfg->rc_buf_sz * 5 / 4 * fps->num / fps->den / 1000); 54 55 // prevent division by zero 56 if (hist->samples == 0) hist->samples = 1; 57 58 hist->frames = 0; 59 hist->total = 0; 60 61 hist->pts = calloc(hist->samples, sizeof(*hist->pts)); 62 hist->sz = calloc(hist->samples, sizeof(*hist->sz)); 63 if (hist->pts == NULL || hist->sz == NULL) goto fail; 64 for (i = 0; i < RATE_BINS; i++) { 65 hist->bucket[i].low = INT_MAX; 66 hist->bucket[i].high = 0; 67 hist->bucket[i].count = 0; 68 } 69 70 return hist; 71 72 fail: 73 fprintf(stderr, 74 "Warning: Unable to allocate buffers required for " 75 "show_rate_histogram().\n" 76 "Continuing without rate histogram feature...\n"); 77 destroy_rate_histogram(hist); 78 return NULL; 79 } 80 81 void destroy_rate_histogram(struct rate_hist *hist) { 82 if (hist) { 83 free(hist->pts); 84 free(hist->sz); 85 free(hist); 86 } 87 } 88 89 void update_rate_histogram(struct rate_hist *hist, 90 const aom_codec_enc_cfg_t *cfg, 91 const aom_codec_cx_pkt_t *pkt) { 92 int i; 93 int64_t then = 0; 94 int64_t avg_bitrate = 0; 95 int64_t sum_sz = 0; 96 const int64_t now = pkt->data.frame.pts * 1000 * 97 (uint64_t)cfg->g_timebase.num / 98 (uint64_t)cfg->g_timebase.den; 99 100 int idx; 101 102 if (hist == NULL || cfg == NULL || pkt == NULL) return; 103 104 idx = hist->frames++ % hist->samples; 105 hist->pts[idx] = now; 106 hist->sz[idx] = (int)pkt->data.frame.sz; 107 108 if (now < cfg->rc_buf_initial_sz) return; 109 110 if (!cfg->rc_target_bitrate) return; 111 112 then = now; 113 114 /* Sum the size over the past rc_buf_sz ms */ 115 for (i = hist->frames; i > 0 && hist->frames - i < hist->samples; i--) { 116 const int i_idx = (i - 1) % hist->samples; 117 118 then = hist->pts[i_idx]; 119 if (now - then > cfg->rc_buf_sz) break; 120 sum_sz += hist->sz[i_idx]; 121 } 122 123 if (now == then) return; 124 125 avg_bitrate = sum_sz * 8 * 1000 / (now - then); 126 idx = (int)(avg_bitrate * (RATE_BINS / 2) / (cfg->rc_target_bitrate * 1000)); 127 if (idx < 0) idx = 0; 128 if (idx > RATE_BINS - 1) idx = RATE_BINS - 1; 129 if (hist->bucket[idx].low > avg_bitrate) 130 hist->bucket[idx].low = (int)avg_bitrate; 131 if (hist->bucket[idx].high < avg_bitrate) 132 hist->bucket[idx].high = (int)avg_bitrate; 133 hist->bucket[idx].count++; 134 hist->total++; 135 } 136 137 static int merge_hist_buckets(struct hist_bucket *bucket, int max_buckets, 138 int *num_buckets) { 139 int small_bucket = 0, merge_bucket = INT_MAX, big_bucket = 0; 140 int buckets; 141 int i; 142 143 assert(bucket != NULL); 144 assert(num_buckets != NULL); 145 146 buckets = *num_buckets; 147 148 /* Find the extrema for this list of buckets */ 149 big_bucket = small_bucket = 0; 150 for (i = 0; i < buckets; i++) { 151 if (bucket[i].count < bucket[small_bucket].count) small_bucket = i; 152 if (bucket[i].count > bucket[big_bucket].count) big_bucket = i; 153 } 154 155 /* If we have too many buckets, merge the smallest with an adjacent 156 * bucket. 157 */ 158 while (buckets > max_buckets) { 159 int last_bucket = buckets - 1; 160 161 /* merge the small bucket with an adjacent one. */ 162 if (small_bucket == 0) 163 merge_bucket = 1; 164 else if (small_bucket == last_bucket) 165 merge_bucket = last_bucket - 1; 166 else if (bucket[small_bucket - 1].count < bucket[small_bucket + 1].count) 167 merge_bucket = small_bucket - 1; 168 else 169 merge_bucket = small_bucket + 1; 170 171 assert(abs(merge_bucket - small_bucket) <= 1); 172 assert(small_bucket < buckets); 173 assert(big_bucket < buckets); 174 assert(merge_bucket < buckets); 175 176 if (merge_bucket < small_bucket) { 177 bucket[merge_bucket].high = bucket[small_bucket].high; 178 bucket[merge_bucket].count += bucket[small_bucket].count; 179 } else { 180 bucket[small_bucket].high = bucket[merge_bucket].high; 181 bucket[small_bucket].count += bucket[merge_bucket].count; 182 merge_bucket = small_bucket; 183 } 184 185 assert(bucket[merge_bucket].low != bucket[merge_bucket].high); 186 187 buckets--; 188 189 /* Remove the merge_bucket from the list, and find the new small 190 * and big buckets while we're at it 191 */ 192 big_bucket = small_bucket = 0; 193 for (i = 0; i < buckets; i++) { 194 if (i > merge_bucket) bucket[i] = bucket[i + 1]; 195 196 if (bucket[i].count < bucket[small_bucket].count) small_bucket = i; 197 if (bucket[i].count > bucket[big_bucket].count) big_bucket = i; 198 } 199 } 200 201 *num_buckets = buckets; 202 return bucket[big_bucket].count; 203 } 204 205 static void show_histogram(const struct hist_bucket *bucket, int buckets, 206 int total, int scale) { 207 int width1, width2; 208 int i; 209 210 if (!buckets) return; 211 assert(bucket != NULL); 212 assert(buckets > 0); 213 214 switch ((int)(log(bucket[buckets - 1].high) / log(10)) + 1) { 215 case 1: 216 case 2: 217 width1 = 4; 218 width2 = 2; 219 break; 220 case 3: 221 width1 = 5; 222 width2 = 3; 223 break; 224 case 4: 225 width1 = 6; 226 width2 = 4; 227 break; 228 case 5: 229 width1 = 7; 230 width2 = 5; 231 break; 232 case 6: 233 width1 = 8; 234 width2 = 6; 235 break; 236 case 7: 237 width1 = 9; 238 width2 = 7; 239 break; 240 default: 241 width1 = 12; 242 width2 = 10; 243 break; 244 } 245 246 for (i = 0; i < buckets; i++) { 247 int len; 248 int j; 249 float pct; 250 251 pct = (float)(100.0 * bucket[i].count / total); 252 len = HIST_BAR_MAX * bucket[i].count / scale; 253 if (len < 1) len = 1; 254 assert(len <= HIST_BAR_MAX); 255 256 if (bucket[i].low == bucket[i].high) 257 fprintf(stderr, "%*d %*s: ", width1, bucket[i].low, width2, ""); 258 else 259 fprintf(stderr, "%*d-%*d: ", width1, bucket[i].low, width2, 260 bucket[i].high); 261 262 for (j = 0; j < HIST_BAR_MAX; j++) fprintf(stderr, j < len ? "=" : " "); 263 fprintf(stderr, "\t%5d (%6.2f%%)\n", bucket[i].count, pct); 264 } 265 } 266 267 void show_q_histogram(const int counts[64], int max_buckets) { 268 struct hist_bucket bucket[64]; 269 int buckets = 0; 270 int total = 0; 271 int scale; 272 int i; 273 274 for (i = 0; i < 64; i++) { 275 if (counts[i]) { 276 bucket[buckets].low = bucket[buckets].high = i; 277 bucket[buckets].count = counts[i]; 278 buckets++; 279 total += counts[i]; 280 } 281 } 282 283 fprintf(stderr, "\nQuantizer Selection:\n"); 284 scale = merge_hist_buckets(bucket, max_buckets, &buckets); 285 show_histogram(bucket, buckets, total, scale); 286 } 287 288 void show_rate_histogram(struct rate_hist *hist, const aom_codec_enc_cfg_t *cfg, 289 int max_buckets) { 290 int i, scale; 291 int buckets = 0; 292 293 if (hist == NULL || cfg == NULL) return; 294 295 for (i = 0; i < RATE_BINS; i++) { 296 if (hist->bucket[i].low == INT_MAX) continue; 297 hist->bucket[buckets++] = hist->bucket[i]; 298 } 299 300 fprintf(stderr, "\nRate (over %dms window):\n", cfg->rc_buf_sz); 301 scale = merge_hist_buckets(hist->bucket, max_buckets, &buckets); 302 show_histogram(hist->bucket, buckets, hist->total, scale); 303 }