1/* 2 * Copyright (c) 2010 The WebM project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 12// This is an example demonstrating multi-resolution encoding in VP8. 13// High-resolution input video is down-sampled to lower-resolutions. The 14// encoder then encodes the video and outputs multiple bitstreams with 15// different resolutions. 16// 17// Configure with --enable-multi-res-encoding flag to enable this example. 18 19#include <stdio.h> 20#include <stdlib.h> 21#include <string.h> 22 23#include "third_party/libyuv/include/libyuv/basic_types.h" 24#include "third_party/libyuv/include/libyuv/scale.h" 25#include "third_party/libyuv/include/libyuv/cpu_id.h" 26 27#include "vpx/vpx_encoder.h" 28#include "vpx/vp8cx.h" 29 30#include "./tools_common.h" 31#include "./video_writer.h" 32 33// The input video frame is downsampled several times to generate a 34// multi-level hierarchical structure. kNumEncoders is defined as the number 35// of encoding levels required. For example, if the size of input video is 36// 1280x720, kNumEncoders is 3, and down-sampling factor is 2, the encoder 37// outputs 3 bitstreams with resolution of 1280x720(level 0), 38// 640x360(level 1), and 320x180(level 2) respectively. 39#define kNumEncoders 3 40 41static const char *exec_name; 42 43void usage_exit() { 44 fprintf(stderr, 45 "Usage: %s <width> <height> <infile> <outfile(s)> <output psnr?>\n", 46 exec_name); 47 exit(EXIT_FAILURE); 48} 49 50int main(int argc, char *argv[]) { 51 int frame_cnt = 0; 52 FILE *infile = NULL; 53 VpxVideoWriter *writers[kNumEncoders]; 54 vpx_codec_ctx_t codec[kNumEncoders]; 55 vpx_codec_enc_cfg_t cfg[kNumEncoders]; 56 vpx_image_t raw[kNumEncoders]; 57 const VpxInterface *const encoder = get_vpx_encoder_by_name("vp8"); 58 // Currently, only realtime mode is supported in multi-resolution encoding. 59 const int arg_deadline = VPX_DL_REALTIME; 60 int i; 61 int width = 0; 62 int height = 0; 63 int frame_avail = 0; 64 int got_data = 0; 65 66 // Set show_psnr to 1/0 to show/not show PSNR. Choose show_psnr=0 if you 67 // don't need to know PSNR, which will skip PSNR calculation and save 68 // encoding time. 69 int show_psnr = 0; 70 uint64_t psnr_sse_total[kNumEncoders] = {0}; 71 uint64_t psnr_samples_total[kNumEncoders] = {0}; 72 double psnr_totals[kNumEncoders][4] = {{0, 0}}; 73 int psnr_count[kNumEncoders] = {0}; 74 75 // Set the required target bitrates for each resolution level. 76 // If target bitrate for highest-resolution level is set to 0, 77 // (i.e. target_bitrate[0]=0), we skip encoding at that level. 78 unsigned int target_bitrate[kNumEncoders] = {1000, 500, 100}; 79 80 // Enter the frame rate of the input video. 81 const int framerate = 30; 82 // Set down-sampling factor for each resolution level. 83 // dsf[0] controls down sampling from level 0 to level 1; 84 // dsf[1] controls down sampling from level 1 to level 2; 85 // dsf[2] is not used. 86 vpx_rational_t dsf[kNumEncoders] = {{2, 1}, {2, 1}, {1, 1}}; 87 88 exec_name = argv[0]; 89 90 if (!encoder) 91 die("Unsupported codec."); 92 93 // exe_name, input width, input height, input file, 94 // output file 1, output file 2, output file 3, psnr on/off 95 if (argc != (5 + kNumEncoders)) 96 die("Invalid number of input options."); 97 98 printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface())); 99 100 width = strtol(argv[1], NULL, 0); 101 height = strtol(argv[2], NULL, 0); 102 103 if (width < 16 || width % 2 || height < 16 || height % 2) 104 die("Invalid resolution: %ldx%ld", width, height); 105 106 // Open input video file for encoding 107 if (!(infile = fopen(argv[3], "rb"))) 108 die("Failed to open %s for reading", argv[3]); 109 110 show_psnr = strtol(argv[kNumEncoders + 4], NULL, 0); 111 112 // Populate default encoder configuration 113 for (i = 0; i < kNumEncoders; ++i) { 114 vpx_codec_err_t res = 115 vpx_codec_enc_config_default(encoder->codec_interface(), &cfg[i], 0); 116 if (res != VPX_CODEC_OK) { 117 printf("Failed to get config: %s\n", vpx_codec_err_to_string(res)); 118 return EXIT_FAILURE; 119 } 120 } 121 122 // Update the default configuration according to needs of the application. 123 // Highest-resolution encoder settings 124 cfg[0].g_w = width; 125 cfg[0].g_h = height; 126 cfg[0].g_threads = 1; 127 cfg[0].rc_dropframe_thresh = 30; 128 cfg[0].rc_end_usage = VPX_CBR; 129 cfg[0].rc_resize_allowed = 0; 130 cfg[0].rc_min_quantizer = 4; 131 cfg[0].rc_max_quantizer = 56; 132 cfg[0].rc_undershoot_pct = 98; 133 cfg[0].rc_overshoot_pct = 100; 134 cfg[0].rc_buf_initial_sz = 500; 135 cfg[0].rc_buf_optimal_sz = 600; 136 cfg[0].rc_buf_sz = 1000; 137 cfg[0].g_error_resilient = 1; 138 cfg[0].g_lag_in_frames = 0; 139 cfg[0].kf_mode = VPX_KF_AUTO; // VPX_KF_DISABLED 140 cfg[0].kf_min_dist = 3000; 141 cfg[0].kf_max_dist = 3000; 142 cfg[0].rc_target_bitrate = target_bitrate[0]; 143 cfg[0].g_timebase.num = 1; 144 cfg[0].g_timebase.den = framerate; 145 146 // Other-resolution encoder settings 147 for (i = 1; i < kNumEncoders; ++i) { 148 cfg[i] = cfg[0]; 149 cfg[i].g_threads = 1; 150 cfg[i].rc_target_bitrate = target_bitrate[i]; 151 152 // Note: Width & height of other-resolution encoders are calculated 153 // from the highest-resolution encoder's size and the corresponding 154 // down_sampling_factor. 155 { 156 unsigned int iw = cfg[i - 1].g_w * dsf[i - 1].den + dsf[i - 1].num - 1; 157 unsigned int ih = cfg[i - 1].g_h * dsf[i - 1].den + dsf[i - 1].num - 1; 158 cfg[i].g_w = iw / dsf[i - 1].num; 159 cfg[i].g_h = ih / dsf[i - 1].num; 160 } 161 162 // Make width & height to be multiplier of 2. 163 if ((cfg[i].g_w) % 2) 164 cfg[i].g_w++; 165 166 if ((cfg[i].g_h) % 2) 167 cfg[i].g_h++; 168 } 169 170 // Open output file for each encoder to output bitstreams 171 for (i = 0; i < kNumEncoders; ++i) { 172 VpxVideoInfo info = { 173 encoder->fourcc, 174 cfg[i].g_w, 175 cfg[i].g_h, 176 {cfg[i].g_timebase.num, cfg[i].g_timebase.den} 177 }; 178 179 if (!(writers[i] = vpx_video_writer_open(argv[i+4], kContainerIVF, &info))) 180 die("Failed to open %s for writing", argv[i+4]); 181 } 182 183 // Allocate image for each encoder 184 for (i = 0; i < kNumEncoders; ++i) 185 if (!vpx_img_alloc(&raw[i], VPX_IMG_FMT_I420, cfg[i].g_w, cfg[i].g_h, 32)) 186 die("Failed to allocate image", cfg[i].g_w, cfg[i].g_h); 187 188 // Initialize multi-encoder 189 if (vpx_codec_enc_init_multi(&codec[0], encoder->codec_interface(), &cfg[0], 190 kNumEncoders, 191 show_psnr ? VPX_CODEC_USE_PSNR : 0, &dsf[0])) 192 die_codec(&codec[0], "Failed to initialize encoder"); 193 194 // The extra encoding configuration parameters can be set as follows. 195 for (i = 0; i < kNumEncoders; i++) { 196 // Set encoding speed 197 if (vpx_codec_control(&codec[i], VP8E_SET_CPUUSED, -6)) 198 die_codec(&codec[i], "Failed to set cpu_used"); 199 200 // Set static threshold. 201 if (vpx_codec_control(&codec[i], VP8E_SET_STATIC_THRESHOLD, 1)) 202 die_codec(&codec[i], "Failed to set static threshold"); 203 204 // Set NOISE_SENSITIVITY to do TEMPORAL_DENOISING 205 // Enable denoising for the highest-resolution encoder. 206 if (vpx_codec_control(&codec[0], VP8E_SET_NOISE_SENSITIVITY, i == 0)) 207 die_codec(&codec[0], "Failed to set noise_sensitivity"); 208 } 209 210 frame_avail = 1; 211 got_data = 0; 212 213 while (frame_avail || got_data) { 214 vpx_codec_iter_t iter[kNumEncoders] = {NULL}; 215 const vpx_codec_cx_pkt_t *pkt[kNumEncoders]; 216 217 frame_avail = vpx_img_read(&raw[0], infile); 218 219 if (frame_avail) { 220 for (i = 1; i < kNumEncoders; ++i) { 221 vpx_image_t *const prev = &raw[i - 1]; 222 223 // Scale the image down a number of times by downsampling factor 224 // FilterMode 1 or 2 give better psnr than FilterMode 0. 225 I420Scale(prev->planes[VPX_PLANE_Y], prev->stride[VPX_PLANE_Y], 226 prev->planes[VPX_PLANE_U], prev->stride[VPX_PLANE_U], 227 prev->planes[VPX_PLANE_V], prev->stride[VPX_PLANE_V], 228 prev->d_w, prev->d_h, 229 raw[i].planes[VPX_PLANE_Y], raw[i].stride[VPX_PLANE_Y], 230 raw[i].planes[VPX_PLANE_U], raw[i].stride[VPX_PLANE_U], 231 raw[i].planes[VPX_PLANE_V], raw[i].stride[VPX_PLANE_V], 232 raw[i].d_w, raw[i].d_h, 1); 233 } 234 } 235 236 // Encode frame. 237 if (vpx_codec_encode(&codec[0], frame_avail? &raw[0] : NULL, 238 frame_cnt, 1, 0, arg_deadline)) { 239 die_codec(&codec[0], "Failed to encode frame"); 240 } 241 242 for (i = kNumEncoders - 1; i >= 0; i--) { 243 got_data = 0; 244 245 while ((pkt[i] = vpx_codec_get_cx_data(&codec[i], &iter[i]))) { 246 got_data = 1; 247 switch (pkt[i]->kind) { 248 case VPX_CODEC_CX_FRAME_PKT: 249 vpx_video_writer_write_frame(writers[i], pkt[i]->data.frame.buf, 250 pkt[i]->data.frame.sz, frame_cnt - 1); 251 break; 252 case VPX_CODEC_PSNR_PKT: 253 if (show_psnr) { 254 int j; 255 psnr_sse_total[i] += pkt[i]->data.psnr.sse[0]; 256 psnr_samples_total[i] += pkt[i]->data.psnr.samples[0]; 257 for (j = 0; j < 4; j++) 258 psnr_totals[i][j] += pkt[i]->data.psnr.psnr[j]; 259 psnr_count[i]++; 260 } 261 break; 262 default: 263 break; 264 } 265 printf(pkt[i]->kind == VPX_CODEC_CX_FRAME_PKT && 266 (pkt[i]->data.frame.flags & VPX_FRAME_IS_KEY)? "K":"."); 267 fflush(stdout); 268 } 269 } 270 frame_cnt++; 271 } 272 printf("\n"); 273 274 fclose(infile); 275 276 printf("Processed %d frames.\n", frame_cnt - 1); 277 for (i = 0; i < kNumEncoders; ++i) { 278 // Calculate PSNR and print it out 279 if (show_psnr && psnr_count[i] > 0) { 280 int j; 281 double ovpsnr = sse_to_psnr(psnr_samples_total[i], 255.0, 282 psnr_sse_total[i]); 283 284 fprintf(stderr, "\n ENC%d PSNR (Overall/Avg/Y/U/V)", i); 285 fprintf(stderr, " %.3lf", ovpsnr); 286 for (j = 0; j < 4; j++) 287 fprintf(stderr, " %.3lf", psnr_totals[i][j]/psnr_count[i]); 288 } 289 290 if (vpx_codec_destroy(&codec[i])) 291 die_codec(&codec[i], "Failed to destroy codec"); 292 293 vpx_img_free(&raw[i]); 294 vpx_video_writer_close(writers[i]); 295 } 296 printf("\n"); 297 298 return EXIT_SUCCESS; 299} 300