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