tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

analyzer.cc (21284B)


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