]> granicus.if.org Git - libvpx/blob - examples/vp9cx_set_ref.c
Merge "test: apply clang-format"
[libvpx] / examples / vp9cx_set_ref.c
1 /*
2  *  Copyright (c) 2016 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 // VP9 Set Reference Frame
12 // ============================
13 //
14 // This is an example demonstrating how to overwrite the VP9 encoder's
15 // internal reference frame. In the sample we set the last frame to the
16 // current frame. This technique could be used to bounce between two cameras.
17 //
18 // The decoder would also have to set the reference frame to the same value
19 // on the same frame, or the video will become corrupt. The 'test_decode'
20 // variable is set to 1 in this example that tests if the encoder and decoder
21 // results are matching.
22 //
23 // Usage
24 // -----
25 // This example encodes a raw video. And the last argument passed in specifies
26 // the frame number to update the reference frame on. For example, run
27 // examples/vp9cx_set_ref 352 288 in.yuv out.ivf 4 30
28 // The parameter is parsed as follows:
29 //
30 //
31 // Extra Variables
32 // ---------------
33 // This example maintains the frame number passed on the command line
34 // in the `update_frame_num` variable.
35 //
36 //
37 // Configuration
38 // -------------
39 //
40 // The reference frame is updated on the frame specified on the command
41 // line.
42 //
43 // Observing The Effects
44 // ---------------------
45 // The encoder and decoder results should be matching when the same reference
46 // frame setting operation is done in both encoder and decoder. Otherwise,
47 // the encoder/decoder mismatch would be seen.
48
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #include "vpx/vp8cx.h"
54 #include "vpx/vpx_decoder.h"
55 #include "vpx/vpx_encoder.h"
56
57 #include "./tools_common.h"
58 #include "./video_writer.h"
59
60 static const char *exec_name;
61
62 void usage_exit() {
63   fprintf(stderr,
64           "Usage: %s <width> <height> <infile> <outfile> "
65           "<frame> <limit(optional)>\n",
66           exec_name);
67   exit(EXIT_FAILURE);
68 }
69
70 static int compare_img(const vpx_image_t *const img1,
71                        const vpx_image_t *const img2) {
72   uint32_t l_w = img1->d_w;
73   uint32_t c_w = (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
74   const uint32_t c_h =
75       (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
76   uint32_t i;
77   int match = 1;
78
79   match &= (img1->fmt == img2->fmt);
80   match &= (img1->d_w == img2->d_w);
81   match &= (img1->d_h == img2->d_h);
82
83   for (i = 0; i < img1->d_h; ++i)
84     match &= (memcmp(img1->planes[VPX_PLANE_Y] + i * img1->stride[VPX_PLANE_Y],
85                      img2->planes[VPX_PLANE_Y] + i * img2->stride[VPX_PLANE_Y],
86                      l_w) == 0);
87
88   for (i = 0; i < c_h; ++i)
89     match &= (memcmp(img1->planes[VPX_PLANE_U] + i * img1->stride[VPX_PLANE_U],
90                      img2->planes[VPX_PLANE_U] + i * img2->stride[VPX_PLANE_U],
91                      c_w) == 0);
92
93   for (i = 0; i < c_h; ++i)
94     match &= (memcmp(img1->planes[VPX_PLANE_V] + i * img1->stride[VPX_PLANE_V],
95                      img2->planes[VPX_PLANE_V] + i * img2->stride[VPX_PLANE_V],
96                      c_w) == 0);
97
98   return match;
99 }
100
101 #define mmin(a, b) ((a) < (b) ? (a) : (b))
102 static void find_mismatch(const vpx_image_t *const img1,
103                           const vpx_image_t *const img2, int yloc[4],
104                           int uloc[4], int vloc[4]) {
105   const uint32_t bsize = 64;
106   const uint32_t bsizey = bsize >> img1->y_chroma_shift;
107   const uint32_t bsizex = bsize >> img1->x_chroma_shift;
108   const uint32_t c_w =
109       (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
110   const uint32_t c_h =
111       (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
112   int match = 1;
113   uint32_t i, j;
114   yloc[0] = yloc[1] = yloc[2] = yloc[3] = -1;
115   for (i = 0, match = 1; match && i < img1->d_h; i += bsize) {
116     for (j = 0; match && j < img1->d_w; j += bsize) {
117       int k, l;
118       const int si = mmin(i + bsize, img1->d_h) - i;
119       const int sj = mmin(j + bsize, img1->d_w) - j;
120       for (k = 0; match && k < si; ++k) {
121         for (l = 0; match && l < sj; ++l) {
122           if (*(img1->planes[VPX_PLANE_Y] +
123                 (i + k) * img1->stride[VPX_PLANE_Y] + j + l) !=
124               *(img2->planes[VPX_PLANE_Y] +
125                 (i + k) * img2->stride[VPX_PLANE_Y] + j + l)) {
126             yloc[0] = i + k;
127             yloc[1] = j + l;
128             yloc[2] = *(img1->planes[VPX_PLANE_Y] +
129                         (i + k) * img1->stride[VPX_PLANE_Y] + j + l);
130             yloc[3] = *(img2->planes[VPX_PLANE_Y] +
131                         (i + k) * img2->stride[VPX_PLANE_Y] + j + l);
132             match = 0;
133             break;
134           }
135         }
136       }
137     }
138   }
139
140   uloc[0] = uloc[1] = uloc[2] = uloc[3] = -1;
141   for (i = 0, match = 1; match && i < c_h; i += bsizey) {
142     for (j = 0; match && j < c_w; j += bsizex) {
143       int k, l;
144       const int si = mmin(i + bsizey, c_h - i);
145       const int sj = mmin(j + bsizex, c_w - j);
146       for (k = 0; match && k < si; ++k) {
147         for (l = 0; match && l < sj; ++l) {
148           if (*(img1->planes[VPX_PLANE_U] +
149                 (i + k) * img1->stride[VPX_PLANE_U] + j + l) !=
150               *(img2->planes[VPX_PLANE_U] +
151                 (i + k) * img2->stride[VPX_PLANE_U] + j + l)) {
152             uloc[0] = i + k;
153             uloc[1] = j + l;
154             uloc[2] = *(img1->planes[VPX_PLANE_U] +
155                         (i + k) * img1->stride[VPX_PLANE_U] + j + l);
156             uloc[3] = *(img2->planes[VPX_PLANE_U] +
157                         (i + k) * img2->stride[VPX_PLANE_U] + j + l);
158             match = 0;
159             break;
160           }
161         }
162       }
163     }
164   }
165   vloc[0] = vloc[1] = vloc[2] = vloc[3] = -1;
166   for (i = 0, match = 1; match && i < c_h; i += bsizey) {
167     for (j = 0; match && j < c_w; j += bsizex) {
168       int k, l;
169       const int si = mmin(i + bsizey, c_h - i);
170       const int sj = mmin(j + bsizex, c_w - j);
171       for (k = 0; match && k < si; ++k) {
172         for (l = 0; match && l < sj; ++l) {
173           if (*(img1->planes[VPX_PLANE_V] +
174                 (i + k) * img1->stride[VPX_PLANE_V] + j + l) !=
175               *(img2->planes[VPX_PLANE_V] +
176                 (i + k) * img2->stride[VPX_PLANE_V] + j + l)) {
177             vloc[0] = i + k;
178             vloc[1] = j + l;
179             vloc[2] = *(img1->planes[VPX_PLANE_V] +
180                         (i + k) * img1->stride[VPX_PLANE_V] + j + l);
181             vloc[3] = *(img2->planes[VPX_PLANE_V] +
182                         (i + k) * img2->stride[VPX_PLANE_V] + j + l);
183             match = 0;
184             break;
185           }
186         }
187       }
188     }
189   }
190 }
191
192 static void testing_decode(vpx_codec_ctx_t *encoder, vpx_codec_ctx_t *decoder,
193                            vpx_codec_enc_cfg_t *cfg, unsigned int frame_out,
194                            int *mismatch_seen) {
195   vpx_image_t enc_img, dec_img;
196   struct vp9_ref_frame ref_enc, ref_dec;
197
198   if (*mismatch_seen) return;
199
200   ref_enc.idx = 0;
201   ref_dec.idx = 0;
202   if (vpx_codec_control(encoder, VP9_GET_REFERENCE, &ref_enc))
203     die_codec(encoder, "Failed to get encoder reference frame");
204   enc_img = ref_enc.img;
205   if (vpx_codec_control(decoder, VP9_GET_REFERENCE, &ref_dec))
206     die_codec(decoder, "Failed to get decoder reference frame");
207   dec_img = ref_dec.img;
208
209   if (!compare_img(&enc_img, &dec_img)) {
210     int y[4], u[4], v[4];
211
212     *mismatch_seen = 1;
213
214     find_mismatch(&enc_img, &dec_img, y, u, v);
215     printf(
216         "Encode/decode mismatch on frame %d at"
217         " Y[%d, %d] {%d/%d},"
218         " U[%d, %d] {%d/%d},"
219         " V[%d, %d] {%d/%d}",
220         frame_out, y[0], y[1], y[2], y[3], u[0], u[1], u[2], u[3], v[0], v[1],
221         v[2], v[3]);
222   }
223
224   vpx_img_free(&enc_img);
225   vpx_img_free(&dec_img);
226 }
227
228 static int encode_frame(vpx_codec_ctx_t *ecodec, vpx_codec_enc_cfg_t *cfg,
229                         vpx_image_t *img, unsigned int frame_in,
230                         VpxVideoWriter *writer, int test_decode,
231                         vpx_codec_ctx_t *dcodec, unsigned int *frame_out,
232                         int *mismatch_seen) {
233   int got_pkts = 0;
234   vpx_codec_iter_t iter = NULL;
235   const vpx_codec_cx_pkt_t *pkt = NULL;
236   int got_data;
237   const vpx_codec_err_t res =
238       vpx_codec_encode(ecodec, img, frame_in, 1, 0, VPX_DL_GOOD_QUALITY);
239   if (res != VPX_CODEC_OK) die_codec(ecodec, "Failed to encode frame");
240
241   got_data = 0;
242
243   while ((pkt = vpx_codec_get_cx_data(ecodec, &iter)) != NULL) {
244     got_pkts = 1;
245
246     if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
247       const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
248
249       if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) {
250         *frame_out += 1;
251       }
252
253       if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf,
254                                         pkt->data.frame.sz,
255                                         pkt->data.frame.pts)) {
256         die_codec(ecodec, "Failed to write compressed frame");
257       }
258       printf(keyframe ? "K" : ".");
259       fflush(stdout);
260       got_data = 1;
261
262       // Decode 1 frame.
263       if (test_decode) {
264         if (vpx_codec_decode(dcodec, pkt->data.frame.buf,
265                              (unsigned int)pkt->data.frame.sz, NULL, 0))
266           die_codec(dcodec, "Failed to decode frame.");
267       }
268     }
269   }
270
271   // Mismatch checking
272   if (got_data && test_decode) {
273     testing_decode(ecodec, dcodec, cfg, *frame_out, mismatch_seen);
274   }
275
276   return got_pkts;
277 }
278
279 int main(int argc, char **argv) {
280   FILE *infile = NULL;
281   // Encoder
282   vpx_codec_ctx_t ecodec = { 0 };
283   vpx_codec_enc_cfg_t cfg = { 0 };
284   unsigned int frame_in = 0;
285   vpx_image_t raw;
286   vpx_codec_err_t res;
287   VpxVideoInfo info = { 0 };
288   VpxVideoWriter *writer = NULL;
289   const VpxInterface *encoder = NULL;
290
291   // Test encoder/decoder mismatch.
292   int test_decode = 1;
293   // Decoder
294   vpx_codec_ctx_t dcodec;
295   unsigned int frame_out = 0;
296
297   // The frame number to set reference frame on
298   unsigned int update_frame_num = 0;
299   int mismatch_seen = 0;
300
301   const int fps = 30;
302   const int bitrate = 500;
303
304   const char *width_arg = NULL;
305   const char *height_arg = NULL;
306   const char *infile_arg = NULL;
307   const char *outfile_arg = NULL;
308   unsigned int limit = 0;
309   exec_name = argv[0];
310
311   if (argc < 6) die("Invalid number of arguments");
312
313   width_arg = argv[1];
314   height_arg = argv[2];
315   infile_arg = argv[3];
316   outfile_arg = argv[4];
317
318   encoder = get_vpx_encoder_by_name("vp9");
319   if (!encoder) die("Unsupported codec.");
320
321   update_frame_num = atoi(argv[5]);
322   // In VP9, the reference buffers (cm->buffer_pool->frame_bufs[i].buf) are
323   // allocated while calling vpx_codec_encode(), thus, setting reference for
324   // 1st frame isn't supported.
325   if (update_frame_num <= 1) die("Couldn't parse frame number '%s'\n", argv[5]);
326
327   if (argc > 6) {
328     limit = atoi(argv[6]);
329     if (update_frame_num > limit)
330       die("Update frame number couldn't larger than limit\n");
331   }
332
333   info.codec_fourcc = encoder->fourcc;
334   info.frame_width = strtol(width_arg, NULL, 0);
335   info.frame_height = strtol(height_arg, NULL, 0);
336   info.time_base.numerator = 1;
337   info.time_base.denominator = fps;
338
339   if (info.frame_width <= 0 || info.frame_height <= 0 ||
340       (info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
341     die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
342   }
343
344   if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
345                      info.frame_height, 1)) {
346     die("Failed to allocate image.");
347   }
348
349   printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
350
351   res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
352   if (res) die_codec(&ecodec, "Failed to get default codec config.");
353
354   cfg.g_w = info.frame_width;
355   cfg.g_h = info.frame_height;
356   cfg.g_timebase.num = info.time_base.numerator;
357   cfg.g_timebase.den = info.time_base.denominator;
358   cfg.rc_target_bitrate = bitrate;
359   cfg.g_lag_in_frames = 3;
360
361   writer = vpx_video_writer_open(outfile_arg, kContainerIVF, &info);
362   if (!writer) die("Failed to open %s for writing.", outfile_arg);
363
364   if (!(infile = fopen(infile_arg, "rb")))
365     die("Failed to open %s for reading.", infile_arg);
366
367   if (vpx_codec_enc_init(&ecodec, encoder->codec_interface(), &cfg, 0))
368     die_codec(&ecodec, "Failed to initialize encoder");
369
370   // Disable alt_ref.
371   if (vpx_codec_control(&ecodec, VP8E_SET_ENABLEAUTOALTREF, 0))
372     die_codec(&ecodec, "Failed to set enable auto alt ref");
373
374   if (test_decode) {
375     const VpxInterface *decoder = get_vpx_decoder_by_name("vp9");
376     if (vpx_codec_dec_init(&dcodec, decoder->codec_interface(), NULL, 0))
377       die_codec(&dcodec, "Failed to initialize decoder.");
378   }
379
380   // Encode frames.
381   while (vpx_img_read(&raw, infile)) {
382     if (limit && frame_in >= limit) break;
383     if (update_frame_num > 1 && frame_out + 1 == update_frame_num) {
384       vpx_ref_frame_t ref;
385       ref.frame_type = VP8_LAST_FRAME;
386       ref.img = raw;
387       // Set reference frame in encoder.
388       if (vpx_codec_control(&ecodec, VP8_SET_REFERENCE, &ref))
389         die_codec(&ecodec, "Failed to set reference frame");
390       printf(" <SET_REF>");
391
392       // If set_reference in decoder is commented out, the enc/dec mismatch
393       // would be seen.
394       if (test_decode) {
395         if (vpx_codec_control(&dcodec, VP8_SET_REFERENCE, &ref))
396           die_codec(&dcodec, "Failed to set reference frame");
397       }
398     }
399
400     encode_frame(&ecodec, &cfg, &raw, frame_in, writer, test_decode, &dcodec,
401                  &frame_out, &mismatch_seen);
402     frame_in++;
403     if (mismatch_seen) break;
404   }
405
406   // Flush encoder.
407   if (!mismatch_seen)
408     while (encode_frame(&ecodec, &cfg, NULL, frame_in, writer, test_decode,
409                         &dcodec, &frame_out, &mismatch_seen)) {
410     }
411
412   printf("\n");
413   fclose(infile);
414   printf("Processed %d frames.\n", frame_out);
415
416   if (test_decode) {
417     if (!mismatch_seen)
418       printf("Encoder/decoder results are matching.\n");
419     else
420       printf("Encoder/decoder results are NOT matching.\n");
421   }
422
423   if (test_decode)
424     if (vpx_codec_destroy(&dcodec))
425       die_codec(&dcodec, "Failed to destroy decoder");
426
427   vpx_img_free(&raw);
428   if (vpx_codec_destroy(&ecodec))
429     die_codec(&ecodec, "Failed to destroy encoder.");
430
431   vpx_video_writer_close(writer);
432
433   return EXIT_SUCCESS;
434 }