blob: 6a42eca248528e5d8d1ad41406e861b796e6ad82 [file] [log] [blame]
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -04001/*
2 * Copyright (c) 2017, 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#include <wx/wx.h>
12#include <wx/aboutdlg.h>
13#include <wx/cmdline.h>
14#include <wx/dcbuffer.h>
Tom Finegan77902132018-05-21 10:19:15 -070015
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040016#include "aom/aom_decoder.h"
17#include "aom/aomdx.h"
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040018#include "av1/common/onyxc_int.h"
Tom Finegan77902132018-05-21 10:19:15 -070019#include "av1/decoder/accounting.h"
Nathan E. Egge2693ca52017-02-22 16:23:02 -050020#include "av1/decoder/inspection.h"
Tom Finegan77902132018-05-21 10:19:15 -070021#include "common/tools_common.h"
22#include "common/video_reader.h"
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040023
24#define OD_SIGNMASK(a) (-((a) < 0))
25#define OD_FLIPSIGNI(a, b) (((a) + OD_SIGNMASK(b)) ^ OD_SIGNMASK(b))
26#define OD_DIV_ROUND(x, y) (((x) + OD_FLIPSIGNI((y) >> 1, x)) / (y))
27
Nathan E. Egge625bc262017-02-22 10:42:54 -050028enum {
29 OD_LUMA_MASK = 1 << 0,
30 OD_CB_MASK = 1 << 1,
31 OD_CR_MASK = 1 << 2,
32 OD_ALL_MASK = OD_LUMA_MASK | OD_CB_MASK | OD_CR_MASK
33};
34
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040035class AV1Decoder {
36 private:
37 FILE *input;
38 wxString path;
39
40 AvxVideoReader *reader;
41 const AvxVideoInfo *info;
42 const AvxInterface *decoder;
43
Nathan E. Egge2693ca52017-02-22 16:23:02 -050044 insp_frame_data frame_data;
45
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040046 aom_codec_ctx_t codec;
Tristan Matthews4d5182c2017-03-21 10:30:54 -040047 bool show_padding;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040048
49 public:
50 aom_image_t *image;
51 int frame;
52
Nathan E. Egge625bc262017-02-22 10:42:54 -050053 int plane_mask;
54
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040055 AV1Decoder();
56 ~AV1Decoder();
57
58 bool open(const wxString &path);
59 void close();
60 bool step();
61
Tristan Matthews4d5182c2017-03-21 10:30:54 -040062 int getWidthPadding() const;
63 int getHeightPadding() const;
64 void togglePadding();
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040065 int getWidth() const;
66 int getHeight() const;
67
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040068 bool getAccountingStruct(Accounting **acct);
Nathan E. Egge2693ca52017-02-22 16:23:02 -050069 bool setInspectionCallback();
70
71 static void inspect(void *decoder, void *data);
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040072};
73
74AV1Decoder::AV1Decoder()
Tristan Matthews4d5182c2017-03-21 10:30:54 -040075 : reader(NULL), info(NULL), decoder(NULL), show_padding(false), image(NULL),
76 frame(0) {}
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040077
78AV1Decoder::~AV1Decoder() {}
79
Tristan Matthews4d5182c2017-03-21 10:30:54 -040080void AV1Decoder::togglePadding() { show_padding = !show_padding; }
81
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040082bool AV1Decoder::open(const wxString &path) {
83 reader = aom_video_reader_open(path.mb_str());
84 if (!reader) {
85 fprintf(stderr, "Failed to open %s for reading.", path.mb_str().data());
86 return false;
87 }
88 this->path = path;
89 info = aom_video_reader_get_info(reader);
90 decoder = get_aom_decoder_by_fourcc(info->codec_fourcc);
91 if (!decoder) {
92 fprintf(stderr, "Unknown input codec.");
93 return false;
94 }
95 printf("Using %s\n", aom_codec_iface_name(decoder->codec_interface()));
96 if (aom_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0)) {
97 fprintf(stderr, "Failed to initialize decoder.");
98 return false;
99 }
Nathan E. Egge2693ca52017-02-22 16:23:02 -0500100 ifd_init(&frame_data, info->frame_width, info->frame_height);
101 setInspectionCallback();
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400102 return true;
103}
104
105void AV1Decoder::close() {}
106
107bool AV1Decoder::step() {
108 if (aom_video_reader_read_frame(reader)) {
109 size_t frame_size;
110 const unsigned char *frame_data;
111 frame_data = aom_video_reader_get_frame(reader, &frame_size);
Sean DuBois47cc2552018-01-23 07:44:16 +0000112 if (aom_codec_decode(&codec, frame_data, frame_size, NULL)) {
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400113 fprintf(stderr, "Failed to decode frame.");
114 return false;
115 } else {
116 aom_codec_iter_t iter = NULL;
117 image = aom_codec_get_frame(&codec, &iter);
118 if (image != NULL) {
119 frame++;
120 return true;
121 }
122 return false;
123 }
124 }
125 return false;
126}
127
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400128int AV1Decoder::getWidth() const {
129 return info->frame_width + 2 * getWidthPadding();
130}
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400131
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400132int AV1Decoder::getWidthPadding() const {
Johann3c30fb42018-02-08 14:33:20 -0800133 return show_padding ? AOMMAX(info->frame_width + 16,
134 ALIGN_POWER_OF_TWO(info->frame_width, 6)) -
135 info->frame_width
136 : 0;
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400137}
138
139int AV1Decoder::getHeight() const {
140 return info->frame_height + 2 * getHeightPadding();
141}
142
143int AV1Decoder::getHeightPadding() const {
Johann3c30fb42018-02-08 14:33:20 -0800144 return show_padding ? AOMMAX(info->frame_height + 16,
145 ALIGN_POWER_OF_TWO(info->frame_height, 6)) -
146 info->frame_height
147 : 0;
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400148}
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400149
150bool AV1Decoder::getAccountingStruct(Accounting **accounting) {
151 return aom_codec_control(&codec, AV1_GET_ACCOUNTING, accounting) ==
152 AOM_CODEC_OK;
153}
154
Nathan E. Egge2693ca52017-02-22 16:23:02 -0500155bool AV1Decoder::setInspectionCallback() {
156 aom_inspect_init ii;
157 ii.inspect_cb = AV1Decoder::inspect;
158 ii.inspect_ctx = (void *)this;
159 return aom_codec_control(&codec, AV1_SET_INSPECTION_CALLBACK, &ii) ==
160 AOM_CODEC_OK;
161}
162
163void AV1Decoder::inspect(void *pbi, void *data) {
164 AV1Decoder *decoder = (AV1Decoder *)data;
165 ifd_inspect(&decoder->frame_data, pbi);
166}
167
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400168#define MIN_ZOOM (1)
169#define MAX_ZOOM (4)
170
171class AnalyzerPanel : public wxPanel {
172 DECLARE_EVENT_TABLE()
173
174 private:
175 AV1Decoder decoder;
176 const wxString path;
177
178 int zoom;
179 unsigned char *pixels;
180
181 const bool bit_accounting;
182 double *bpp_q3;
183
Nathan E. Egge625bc262017-02-22 10:42:54 -0500184 int plane_mask;
185
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400186 // The display size is the decode size, scaled by the zoom.
187 int getDisplayWidth() const;
188 int getDisplayHeight() const;
189
190 bool updateDisplaySize();
191
192 void computeBitsPerPixel();
193
194 public:
195 AnalyzerPanel(wxWindow *parent, const wxString &path,
196 const bool bit_accounting);
197 ~AnalyzerPanel();
198
199 bool open(const wxString &path);
200 void close();
201 void render();
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400202 void togglePadding();
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400203 bool nextFrame();
204 void refresh();
205
206 int getZoom() const;
207 bool setZoom(int zoom);
208
Nathan E. Egge625bc262017-02-22 10:42:54 -0500209 void setShowPlane(bool show_plane, int mask);
210
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400211 void onPaint(wxPaintEvent &event); // NOLINT
212};
213
214BEGIN_EVENT_TABLE(AnalyzerPanel, wxPanel)
215EVT_PAINT(AnalyzerPanel::onPaint)
216END_EVENT_TABLE()
217
218AnalyzerPanel::AnalyzerPanel(wxWindow *parent, const wxString &path,
219 const bool bit_accounting)
220 : wxPanel(parent), path(path), zoom(0), pixels(NULL),
Nathan E. Egge625bc262017-02-22 10:42:54 -0500221 bit_accounting(bit_accounting), bpp_q3(NULL), plane_mask(OD_ALL_MASK) {}
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400222
223AnalyzerPanel::~AnalyzerPanel() { close(); }
224
Nathan E. Egge625bc262017-02-22 10:42:54 -0500225void AnalyzerPanel::setShowPlane(bool show_plane, int mask) {
226 if (show_plane) {
227 plane_mask |= mask;
228 } else {
229 plane_mask &= ~mask;
230 }
231}
232
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400233void AnalyzerPanel::render() {
234 aom_image_t *img = decoder.image;
Tristan Matthews86a67382017-06-28 14:28:32 -0400235 const int hbd = !!(img->fmt & AOM_IMG_FMT_HIGHBITDEPTH);
236 int y_stride = img->stride[0] >> hbd;
237 int cb_stride = img->stride[1] >> hbd;
238 int cr_stride = img->stride[2] >> hbd;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400239 int p_stride = 3 * getDisplayWidth();
240 unsigned char *y_row = img->planes[0];
241 unsigned char *cb_row = img->planes[1];
242 unsigned char *cr_row = img->planes[2];
Tristan Matthews86a67382017-06-28 14:28:32 -0400243 uint16_t *y_row16 = reinterpret_cast<uint16_t *>(y_row);
244 uint16_t *cb_row16 = reinterpret_cast<uint16_t *>(cb_row);
245 uint16_t *cr_row16 = reinterpret_cast<uint16_t *>(cr_row);
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400246 unsigned char *p_row = pixels;
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400247 int y_width_padding = decoder.getWidthPadding();
248 int cb_width_padding = y_width_padding >> 1;
249 int cr_width_padding = y_width_padding >> 1;
250 int y_height_padding = decoder.getHeightPadding();
251 int cb_height_padding = y_height_padding >> 1;
252 int cr_height_padding = y_height_padding >> 1;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400253 for (int j = 0; j < decoder.getHeight(); j++) {
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400254 unsigned char *y = y_row - y_stride * y_height_padding;
255 unsigned char *cb = cb_row - cb_stride * cb_height_padding;
256 unsigned char *cr = cr_row - cr_stride * cr_height_padding;
Tristan Matthews86a67382017-06-28 14:28:32 -0400257 uint16_t *y16 = y_row16 - y_stride * y_height_padding;
258 uint16_t *cb16 = cb_row16 - cb_stride * cb_height_padding;
259 uint16_t *cr16 = cr_row16 - cr_stride * cr_height_padding;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400260 unsigned char *p = p_row;
261 for (int i = 0; i < decoder.getWidth(); i++) {
262 int64_t yval;
263 int64_t cbval;
264 int64_t crval;
Nathan E. Egge625bc262017-02-22 10:42:54 -0500265 int pmask;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400266 unsigned rval;
267 unsigned gval;
268 unsigned bval;
Tristan Matthews86a67382017-06-28 14:28:32 -0400269 if (hbd) {
270 yval = *(y16 - y_width_padding);
271 cbval = *(cb16 - cb_width_padding);
272 crval = *(cr16 - cr_width_padding);
273 } else {
274 yval = *(y - y_width_padding);
275 cbval = *(cb - cb_width_padding);
276 crval = *(cr - cr_width_padding);
277 }
Nathan E. Egge625bc262017-02-22 10:42:54 -0500278 pmask = plane_mask;
279 if (pmask & OD_LUMA_MASK) {
280 yval -= 16;
281 } else {
282 yval = 128;
283 }
284 cbval = ((pmask & OD_CB_MASK) >> 1) * (cbval - 128);
285 crval = ((pmask & OD_CR_MASK) >> 2) * (crval - 128);
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400286 /*This is intentionally slow and very accurate.*/
Johann3c30fb42018-02-08 14:33:20 -0800287 rval = OD_CLAMPI(
288 0,
289 (int32_t)OD_DIV_ROUND(
290 2916394880000LL * yval + 4490222169144LL * crval, 9745792000LL),
291 65535);
292 gval = OD_CLAMPI(0,
293 (int32_t)OD_DIV_ROUND(2916394880000LL * yval -
294 534117096223LL * cbval -
295 1334761232047LL * crval,
296 9745792000LL),
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400297 65535);
Johann3c30fb42018-02-08 14:33:20 -0800298 bval = OD_CLAMPI(
299 0,
300 (int32_t)OD_DIV_ROUND(
301 2916394880000LL * yval + 5290866304968LL * cbval, 9745792000LL),
302 65535);
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400303 unsigned char *px_row = p;
304 for (int v = 0; v < zoom; v++) {
305 unsigned char *px = px_row;
306 for (int u = 0; u < zoom; u++) {
307 *(px + 0) = (unsigned char)(rval >> 8);
308 *(px + 1) = (unsigned char)(gval >> 8);
309 *(px + 2) = (unsigned char)(bval >> 8);
310 px += 3;
311 }
312 px_row += p_stride;
313 }
Tristan Matthews86a67382017-06-28 14:28:32 -0400314 if (hbd) {
315 int dc = ((y16 - y_row16) & 1) | (1 - img->x_chroma_shift);
316 y16++;
317 cb16 += dc;
318 cr16 += dc;
319 } else {
320 int dc = ((y - y_row) & 1) | (1 - img->x_chroma_shift);
321 y++;
322 cb += dc;
323 cr += dc;
324 }
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400325 p += zoom * 3;
326 }
327 int dc = -((j & 1) | (1 - img->y_chroma_shift));
Tristan Matthews86a67382017-06-28 14:28:32 -0400328 if (hbd) {
329 y_row16 += y_stride;
330 cb_row16 += dc & cb_stride;
331 cr_row16 += dc & cr_stride;
332 } else {
333 y_row += y_stride;
334 cb_row += dc & cb_stride;
335 cr_row += dc & cr_stride;
336 }
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400337 p_row += zoom * p_stride;
338 }
339}
340
341void AnalyzerPanel::computeBitsPerPixel() {
342 Accounting *acct;
343 double bpp_total;
344 int totals_q3[MAX_SYMBOL_TYPES] = { 0 };
345 int sym_count[MAX_SYMBOL_TYPES] = { 0 };
346 decoder.getAccountingStruct(&acct);
347 for (int j = 0; j < decoder.getHeight(); j++) {
348 for (int i = 0; i < decoder.getWidth(); i++) {
349 bpp_q3[j * decoder.getWidth() + i] = 0.0;
350 }
351 }
352 bpp_total = 0;
353 for (int i = 0; i < acct->syms.num_syms; i++) {
354 AccountingSymbol *s;
355 s = &acct->syms.syms[i];
356 totals_q3[s->id] += s->bits;
357 sym_count[s->id] += s->samples;
358 }
359 printf("=== Frame: %-3i ===\n", decoder.frame - 1);
360 for (int i = 0; i < acct->syms.dictionary.num_strs; i++) {
361 if (totals_q3[i]) {
362 printf("%30s = %10.3f (%f bit/symbol)\n", acct->syms.dictionary.strs[i],
363 (float)totals_q3[i] / 8, (float)totals_q3[i] / 8 / sym_count[i]);
364 }
365 }
366 printf("\n");
367}
368
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400369void AnalyzerPanel::togglePadding() {
370 decoder.togglePadding();
371 updateDisplaySize();
372}
373
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400374bool AnalyzerPanel::nextFrame() {
375 if (decoder.step()) {
376 refresh();
377 return true;
378 }
379 return false;
380}
381
382void AnalyzerPanel::refresh() {
383 if (bit_accounting) {
384 computeBitsPerPixel();
385 }
386 render();
387}
388
389int AnalyzerPanel::getDisplayWidth() const { return zoom * decoder.getWidth(); }
390
391int AnalyzerPanel::getDisplayHeight() const {
392 return zoom * decoder.getHeight();
393}
394
395bool AnalyzerPanel::updateDisplaySize() {
396 unsigned char *p = (unsigned char *)malloc(
397 sizeof(*p) * 3 * getDisplayWidth() * getDisplayHeight());
398 if (p == NULL) {
399 return false;
400 }
401 free(pixels);
402 pixels = p;
403 SetSize(getDisplayWidth(), getDisplayHeight());
404 return true;
405}
406
407bool AnalyzerPanel::open(const wxString &path) {
408 if (!decoder.open(path)) {
409 return false;
410 }
411 if (!setZoom(MIN_ZOOM)) {
412 return false;
413 }
414 if (bit_accounting) {
415 bpp_q3 = (double *)malloc(sizeof(*bpp_q3) * decoder.getWidth() *
416 decoder.getHeight());
417 if (bpp_q3 == NULL) {
418 fprintf(stderr, "Could not allocate memory for bit accounting\n");
419 close();
420 return false;
421 }
422 }
423 if (!nextFrame()) {
424 close();
425 return false;
426 }
427 SetFocus();
428 return true;
429}
430
431void AnalyzerPanel::close() {
432 decoder.close();
433 free(pixels);
434 pixels = NULL;
435 free(bpp_q3);
436 bpp_q3 = NULL;
437}
438
439int AnalyzerPanel::getZoom() const { return zoom; }
440
441bool AnalyzerPanel::setZoom(int z) {
442 if (z <= MAX_ZOOM && z >= MIN_ZOOM && zoom != z) {
443 int old_zoom = zoom;
444 zoom = z;
445 if (!updateDisplaySize()) {
446 zoom = old_zoom;
447 return false;
448 }
449 return true;
450 }
451 return false;
452}
453
454void AnalyzerPanel::onPaint(wxPaintEvent &) {
455 wxBitmap bmp(wxImage(getDisplayWidth(), getDisplayHeight(), pixels, true));
456 wxBufferedPaintDC dc(this, bmp);
457}
458
459class AnalyzerFrame : public wxFrame {
460 DECLARE_EVENT_TABLE()
461
462 private:
463 AnalyzerPanel *panel;
464 const bool bit_accounting;
465
466 wxMenu *fileMenu;
467 wxMenu *viewMenu;
468 wxMenu *playbackMenu;
469
470 public:
471 AnalyzerFrame(const bool bit_accounting); // NOLINT
472
473 void onOpen(wxCommandEvent &event); // NOLINT
474 void onClose(wxCommandEvent &event); // NOLINT
475 void onQuit(wxCommandEvent &event); // NOLINT
476
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400477 void onTogglePadding(wxCommandEvent &event); // NOLINT
478 void onZoomIn(wxCommandEvent &event); // NOLINT
479 void onZoomOut(wxCommandEvent &event); // NOLINT
480 void onActualSize(wxCommandEvent &event); // NOLINT
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400481
Nathan E. Egge625bc262017-02-22 10:42:54 -0500482 void onToggleViewMenuCheckBox(wxCommandEvent &event); // NOLINT
483 void onResetAndToggleViewMenuCheckBox(wxCommandEvent &event); // NOLINT
484
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400485 void onNextFrame(wxCommandEvent &event); // NOLINT
486 void onGotoFrame(wxCommandEvent &event); // NOLINT
487 void onRestart(wxCommandEvent &event); // NOLINT
488
489 void onAbout(wxCommandEvent &event); // NOLINT
490
491 bool open(const wxString &path);
492 bool setZoom(int zoom);
Nathan E. Egge625bc262017-02-22 10:42:54 -0500493 void updateViewMenu();
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400494};
495
496enum {
497 wxID_NEXT_FRAME = 6000,
Nathan E. Egge625bc262017-02-22 10:42:54 -0500498 wxID_SHOW_Y,
499 wxID_SHOW_U,
500 wxID_SHOW_V,
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400501 wxID_GOTO_FRAME,
502 wxID_RESTART,
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400503 wxID_ACTUAL_SIZE,
504 wxID_PADDING
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400505};
506
507BEGIN_EVENT_TABLE(AnalyzerFrame, wxFrame)
508EVT_MENU(wxID_OPEN, AnalyzerFrame::onOpen)
509EVT_MENU(wxID_CLOSE, AnalyzerFrame::onClose)
510EVT_MENU(wxID_EXIT, AnalyzerFrame::onQuit)
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400511EVT_MENU(wxID_PADDING, AnalyzerFrame::onTogglePadding)
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400512EVT_MENU(wxID_ZOOM_IN, AnalyzerFrame::onZoomIn)
513EVT_MENU(wxID_ZOOM_OUT, AnalyzerFrame::onZoomOut)
514EVT_MENU(wxID_ACTUAL_SIZE, AnalyzerFrame::onActualSize)
Nathan E. Egge625bc262017-02-22 10:42:54 -0500515EVT_MENU(wxID_SHOW_Y, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
516EVT_MENU(wxID_SHOW_U, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
517EVT_MENU(wxID_SHOW_V, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400518EVT_MENU(wxID_NEXT_FRAME, AnalyzerFrame::onNextFrame)
519EVT_MENU(wxID_GOTO_FRAME, AnalyzerFrame::onGotoFrame)
520EVT_MENU(wxID_RESTART, AnalyzerFrame::onRestart)
521EVT_MENU(wxID_ABOUT, AnalyzerFrame::onAbout)
522END_EVENT_TABLE()
523
524AnalyzerFrame::AnalyzerFrame(const bool bit_accounting)
525 : wxFrame(NULL, wxID_ANY, _("AV1 Stream Analyzer"), wxDefaultPosition,
526 wxDefaultSize, wxDEFAULT_FRAME_STYLE),
527 panel(NULL), bit_accounting(bit_accounting) {
528 wxMenuBar *mb = new wxMenuBar();
529
530 fileMenu = new wxMenu();
531 fileMenu->Append(wxID_OPEN, _("&Open...\tCtrl-O"), _("Open daala file"));
532 fileMenu->Append(wxID_CLOSE, _("&Close\tCtrl-W"), _("Close daala file"));
533 fileMenu->Enable(wxID_CLOSE, false);
534 fileMenu->Append(wxID_EXIT, _("E&xit\tCtrl-Q"), _("Quit this program"));
535 mb->Append(fileMenu, _("&File"));
536
537 wxAcceleratorEntry entries[2];
538 entries[0].Set(wxACCEL_CTRL, (int)'=', wxID_ZOOM_IN);
539 entries[1].Set(wxACCEL_CTRL | wxACCEL_SHIFT, (int)'-', wxID_ZOOM_OUT);
540 wxAcceleratorTable accel(2, entries);
541 this->SetAcceleratorTable(accel);
542
543 viewMenu = new wxMenu();
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400544 +viewMenu->Append(wxID_PADDING, _("Toggle padding\tCtrl-p"),
545 _("Show padding"));
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400546 viewMenu->Append(wxID_ZOOM_IN, _("Zoom-In\tCtrl-+"), _("Double image size"));
547 viewMenu->Append(wxID_ZOOM_OUT, _("Zoom-Out\tCtrl--"), _("Half image size"));
548 viewMenu->Append(wxID_ACTUAL_SIZE, _("Actual size\tCtrl-0"),
549 _("Actual size of the frame"));
Nathan E. Egge625bc262017-02-22 10:42:54 -0500550 viewMenu->AppendSeparator();
551 viewMenu->AppendCheckItem(wxID_SHOW_Y, _("&Y plane\tCtrl-Y"),
552 _("Show Y plane"));
553 viewMenu->AppendCheckItem(wxID_SHOW_U, _("&U plane\tCtrl-U"),
554 _("Show U plane"));
555 viewMenu->AppendCheckItem(wxID_SHOW_V, _("&V plane\tCtrl-V"),
556 _("Show V plane"));
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400557 mb->Append(viewMenu, _("&View"));
558
559 playbackMenu = new wxMenu();
560 playbackMenu->Append(wxID_NEXT_FRAME, _("Next frame\tCtrl-."),
561 _("Go to next frame"));
562 /*playbackMenu->Append(wxID_RESTART, _("&Restart\tCtrl-R"),
563 _("Set video to frame 0"));
564 playbackMenu->Append(wxID_GOTO_FRAME, _("Jump to Frame\tCtrl-J"),
565 _("Go to frame number"));*/
566 mb->Append(playbackMenu, _("&Playback"));
567
568 wxMenu *helpMenu = new wxMenu();
569 helpMenu->Append(wxID_ABOUT, _("&About...\tF1"), _("Show about dialog"));
570 mb->Append(helpMenu, _("&Help"));
571
572 SetMenuBar(mb);
573
574 CreateStatusBar(1);
575}
576
577void AnalyzerFrame::onOpen(wxCommandEvent &WXUNUSED(event)) {
578 wxFileDialog openFileDialog(this, _("Open file"), wxEmptyString,
579 wxEmptyString, _("AV1 files (*.ivf)|*.ivf"),
580 wxFD_OPEN | wxFD_FILE_MUST_EXIST);
581 if (openFileDialog.ShowModal() != wxID_CANCEL) {
582 open(openFileDialog.GetPath());
583 }
584}
585
586void AnalyzerFrame::onClose(wxCommandEvent &WXUNUSED(event)) {}
587
588void AnalyzerFrame::onQuit(wxCommandEvent &WXUNUSED(event)) { Close(true); }
589
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400590void AnalyzerFrame::onTogglePadding(wxCommandEvent &WXUNUSED(event)) {
591 panel->togglePadding();
592 SetClientSize(panel->GetSize());
593 panel->render();
594 panel->Refresh();
595}
596
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400597void AnalyzerFrame::onZoomIn(wxCommandEvent &WXUNUSED(event)) {
598 setZoom(panel->getZoom() + 1);
599}
600
601void AnalyzerFrame::onZoomOut(wxCommandEvent &WXUNUSED(event)) {
602 setZoom(panel->getZoom() - 1);
603}
604
605void AnalyzerFrame::onActualSize(wxCommandEvent &WXUNUSED(event)) {
606 setZoom(MIN_ZOOM);
607}
608
Nathan E. Egge625bc262017-02-22 10:42:54 -0500609void AnalyzerFrame::onToggleViewMenuCheckBox(wxCommandEvent &event) { // NOLINT
610 GetMenuBar()->Check(event.GetId(), event.IsChecked());
611 updateViewMenu();
612}
613
614void AnalyzerFrame::onResetAndToggleViewMenuCheckBox(
615 wxCommandEvent &event) { // NOLINT
616 int id = event.GetId();
617 if (id != wxID_SHOW_Y && id != wxID_SHOW_U && id != wxID_SHOW_V) {
618 GetMenuBar()->Check(wxID_SHOW_Y, true);
619 GetMenuBar()->Check(wxID_SHOW_U, true);
620 GetMenuBar()->Check(wxID_SHOW_V, true);
621 }
622 onToggleViewMenuCheckBox(event);
623}
624
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400625void AnalyzerFrame::onNextFrame(wxCommandEvent &WXUNUSED(event)) {
626 panel->nextFrame();
627 panel->Refresh(false);
628}
629
630void AnalyzerFrame::onGotoFrame(wxCommandEvent &WXUNUSED(event)) {}
631
632void AnalyzerFrame::onRestart(wxCommandEvent &WXUNUSED(event)) {}
633
634void AnalyzerFrame::onAbout(wxCommandEvent &WXUNUSED(event)) {
635 wxAboutDialogInfo info;
636 info.SetName(_("AV1 Bitstream Analyzer"));
637 info.SetVersion(_("0.1-beta"));
638 info.SetDescription(
639 _("This program implements a bitstream analyzer for AV1"));
640 info.SetCopyright(
641 wxT("(C) 2017 Alliance for Open Media <negge@mozilla.com>"));
642 wxAboutBox(info);
643}
644
645bool AnalyzerFrame::open(const wxString &path) {
646 panel = new AnalyzerPanel(this, path, bit_accounting);
647 if (panel->open(path)) {
648 SetClientSize(panel->GetSize());
649 return true;
650 } else {
651 delete panel;
652 return false;
653 }
654}
655
656bool AnalyzerFrame::setZoom(int zoom) {
657 if (panel->setZoom(zoom)) {
658 GetMenuBar()->Enable(wxID_ACTUAL_SIZE, zoom != MIN_ZOOM);
659 GetMenuBar()->Enable(wxID_ZOOM_IN, zoom != MAX_ZOOM);
660 GetMenuBar()->Enable(wxID_ZOOM_OUT, zoom != MIN_ZOOM);
661 SetClientSize(panel->GetSize());
662 panel->render();
663 panel->Refresh();
664 return true;
665 }
666 return false;
667}
668
Nathan E. Egge625bc262017-02-22 10:42:54 -0500669void AnalyzerFrame::updateViewMenu() {
670 panel->setShowPlane(GetMenuBar()->IsChecked(wxID_SHOW_Y), OD_LUMA_MASK);
671 panel->setShowPlane(GetMenuBar()->IsChecked(wxID_SHOW_U), OD_CB_MASK);
672 panel->setShowPlane(GetMenuBar()->IsChecked(wxID_SHOW_V), OD_CR_MASK);
673 SetClientSize(panel->GetSize());
674 panel->render();
675 panel->Refresh(false);
676}
677
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400678class Analyzer : public wxApp {
679 private:
680 AnalyzerFrame *frame;
681
682 public:
683 void OnInitCmdLine(wxCmdLineParser &parser); // NOLINT
684 bool OnCmdLineParsed(wxCmdLineParser &parser); // NOLINT
685};
686
687static const wxCmdLineEntryDesc CMD_LINE_DESC[] = {
688 { wxCMD_LINE_SWITCH, _("h"), _("help"), _("Display this help and exit."),
689 wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
690 { wxCMD_LINE_SWITCH, _("a"), _("bit-accounting"), _("Enable bit accounting"),
691 wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
692 { wxCMD_LINE_PARAM, NULL, NULL, _("input.ivf"), wxCMD_LINE_VAL_STRING,
693 wxCMD_LINE_PARAM_OPTIONAL },
694 { wxCMD_LINE_NONE }
695};
696
697void Analyzer::OnInitCmdLine(wxCmdLineParser &parser) { // NOLINT
698 parser.SetDesc(CMD_LINE_DESC);
699 parser.SetSwitchChars(_("-"));
700}
701
702bool Analyzer::OnCmdLineParsed(wxCmdLineParser &parser) { // NOLINT
703 bool bit_accounting = parser.Found(_("a"));
704 if (bit_accounting && !CONFIG_ACCOUNTING) {
705 fprintf(stderr,
Johanne07a6752018-01-10 12:47:44 -0800706 "Bit accounting support not found. "
707 "Recompile with:\n./cmake -DCONFIG_ACCOUNTING=1\n");
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400708 return false;
709 }
710 frame = new AnalyzerFrame(parser.Found(_("a")));
711 frame->Show();
712 if (parser.GetParamCount() > 0) {
713 return frame->open(parser.GetParam(0));
714 }
715 return true;
716}
717
718void usage_exit(void) {
719 fprintf(stderr, "uhh\n");
720 exit(EXIT_FAILURE);
721}
722
723IMPLEMENT_APP(Analyzer)