Home | History | Annotate | Download | only in examples
      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/onyxc_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   const AvxInterface *decoder;
     43 
     44   insp_frame_data frame_data;
     45 
     46   aom_codec_ctx_t codec;
     47   bool show_padding;
     48 
     49  public:
     50   aom_image_t *image;
     51   int frame;
     52 
     53   int plane_mask;
     54 
     55   AV1Decoder();
     56   ~AV1Decoder();
     57 
     58   bool open(const wxString &path);
     59   void close();
     60   bool step();
     61 
     62   int getWidthPadding() const;
     63   int getHeightPadding() const;
     64   void togglePadding();
     65   int getWidth() const;
     66   int getHeight() const;
     67 
     68   bool getAccountingStruct(Accounting **acct);
     69   bool setInspectionCallback();
     70 
     71   static void inspect(void *decoder, void *data);
     72 };
     73 
     74 AV1Decoder::AV1Decoder()
     75     : reader(NULL), info(NULL), decoder(NULL), show_padding(false), image(NULL),
     76       frame(0) {}
     77 
     78 AV1Decoder::~AV1Decoder() {}
     79 
     80 void AV1Decoder::togglePadding() { show_padding = !show_padding; }
     81 
     82 bool 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   }
    100   ifd_init(&frame_data, info->frame_width, info->frame_height);
    101   setInspectionCallback();
    102   return true;
    103 }
    104 
    105 void AV1Decoder::close() {}
    106 
    107 bool 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);
    112     if (aom_codec_decode(&codec, frame_data, frame_size, NULL)) {
    113       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 
    128 int AV1Decoder::getWidth() const {
    129   return info->frame_width + 2 * getWidthPadding();
    130 }
    131 
    132 int AV1Decoder::getWidthPadding() const {
    133   return show_padding ? AOMMAX(info->frame_width + 16,
    134                                ALIGN_POWER_OF_TWO(info->frame_width, 6)) -
    135                             info->frame_width
    136                       : 0;
    137 }
    138 
    139 int AV1Decoder::getHeight() const {
    140   return info->frame_height + 2 * getHeightPadding();
    141 }
    142 
    143 int AV1Decoder::getHeightPadding() const {
    144   return show_padding ? AOMMAX(info->frame_height + 16,
    145                                ALIGN_POWER_OF_TWO(info->frame_height, 6)) -
    146                             info->frame_height
    147                       : 0;
    148 }
    149 
    150 bool AV1Decoder::getAccountingStruct(Accounting **accounting) {
    151   return aom_codec_control(&codec, AV1_GET_ACCOUNTING, accounting) ==
    152          AOM_CODEC_OK;
    153 }
    154 
    155 bool 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 
    163 void AV1Decoder::inspect(void *pbi, void *data) {
    164   AV1Decoder *decoder = (AV1Decoder *)data;
    165   ifd_inspect(&decoder->frame_data, pbi, 0);
    166 }
    167 
    168 #define MIN_ZOOM (1)
    169 #define MAX_ZOOM (4)
    170 
    171 class 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 
    184   int plane_mask;
    185 
    186   // 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();
    202   void togglePadding();
    203   bool nextFrame();
    204   void refresh();
    205 
    206   int getZoom() const;
    207   bool setZoom(int zoom);
    208 
    209   void setShowPlane(bool show_plane, int mask);
    210 
    211   void onPaint(wxPaintEvent &event);  // NOLINT
    212 };
    213 
    214 BEGIN_EVENT_TABLE(AnalyzerPanel, wxPanel)
    215 EVT_PAINT(AnalyzerPanel::onPaint)
    216 END_EVENT_TABLE()
    217 
    218 AnalyzerPanel::AnalyzerPanel(wxWindow *parent, const wxString &path,
    219                              const bool bit_accounting)
    220     : wxPanel(parent), path(path), zoom(0), pixels(NULL),
    221       bit_accounting(bit_accounting), bpp_q3(NULL), plane_mask(OD_ALL_MASK) {}
    222 
    223 AnalyzerPanel::~AnalyzerPanel() { close(); }
    224 
    225 void AnalyzerPanel::setShowPlane(bool show_plane, int mask) {
    226   if (show_plane) {
    227     plane_mask |= mask;
    228   } else {
    229     plane_mask &= ~mask;
    230   }
    231 }
    232 
    233 void AnalyzerPanel::render() {
    234   aom_image_t *img = decoder.image;
    235   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;
    239   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];
    243   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);
    246   unsigned char *p_row = pixels;
    247   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;
    253   for (int j = 0; j < decoder.getHeight(); j++) {
    254     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;
    257     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;
    260     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;
    265       int pmask;
    266       unsigned rval;
    267       unsigned gval;
    268       unsigned bval;
    269       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       }
    278       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);
    286       /*This is intentionally slow and very accurate.*/
    287       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),
    297                        65535);
    298       bval = OD_CLAMPI(
    299           0,
    300           (int32_t)OD_DIV_ROUND(
    301               2916394880000LL * yval + 5290866304968LL * cbval, 9745792000LL),
    302           65535);
    303       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       }
    314       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       }
    325       p += zoom * 3;
    326     }
    327     int dc = -((j & 1) | (1 - img->y_chroma_shift));
    328     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     }
    337     p_row += zoom * p_stride;
    338   }
    339 }
    340 
    341 void 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 
    369 void AnalyzerPanel::togglePadding() {
    370   decoder.togglePadding();
    371   updateDisplaySize();
    372 }
    373 
    374 bool AnalyzerPanel::nextFrame() {
    375   if (decoder.step()) {
    376     refresh();
    377     return true;
    378   }
    379   return false;
    380 }
    381 
    382 void AnalyzerPanel::refresh() {
    383   if (bit_accounting) {
    384     computeBitsPerPixel();
    385   }
    386   render();
    387 }
    388 
    389 int AnalyzerPanel::getDisplayWidth() const { return zoom * decoder.getWidth(); }
    390 
    391 int AnalyzerPanel::getDisplayHeight() const {
    392   return zoom * decoder.getHeight();
    393 }
    394 
    395 bool 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 
    407 bool 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 
    431 void AnalyzerPanel::close() {
    432   decoder.close();
    433   free(pixels);
    434   pixels = NULL;
    435   free(bpp_q3);
    436   bpp_q3 = NULL;
    437 }
    438 
    439 int AnalyzerPanel::getZoom() const { return zoom; }
    440 
    441 bool 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 
    454 void AnalyzerPanel::onPaint(wxPaintEvent &) {
    455   wxBitmap bmp(wxImage(getDisplayWidth(), getDisplayHeight(), pixels, true));
    456   wxBufferedPaintDC dc(this, bmp);
    457 }
    458 
    459 class 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 
    477   void onTogglePadding(wxCommandEvent &event);  // NOLINT
    478   void onZoomIn(wxCommandEvent &event);         // NOLINT
    479   void onZoomOut(wxCommandEvent &event);        // NOLINT
    480   void onActualSize(wxCommandEvent &event);     // NOLINT
    481 
    482   void onToggleViewMenuCheckBox(wxCommandEvent &event);          // NOLINT
    483   void onResetAndToggleViewMenuCheckBox(wxCommandEvent &event);  // NOLINT
    484 
    485   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);
    493   void updateViewMenu();
    494 };
    495 
    496 enum {
    497   wxID_NEXT_FRAME = 6000,
    498   wxID_SHOW_Y,
    499   wxID_SHOW_U,
    500   wxID_SHOW_V,
    501   wxID_GOTO_FRAME,
    502   wxID_RESTART,
    503   wxID_ACTUAL_SIZE,
    504   wxID_PADDING
    505 };
    506 
    507 BEGIN_EVENT_TABLE(AnalyzerFrame, wxFrame)
    508 EVT_MENU(wxID_OPEN, AnalyzerFrame::onOpen)
    509 EVT_MENU(wxID_CLOSE, AnalyzerFrame::onClose)
    510 EVT_MENU(wxID_EXIT, AnalyzerFrame::onQuit)
    511 EVT_MENU(wxID_PADDING, AnalyzerFrame::onTogglePadding)
    512 EVT_MENU(wxID_ZOOM_IN, AnalyzerFrame::onZoomIn)
    513 EVT_MENU(wxID_ZOOM_OUT, AnalyzerFrame::onZoomOut)
    514 EVT_MENU(wxID_ACTUAL_SIZE, AnalyzerFrame::onActualSize)
    515 EVT_MENU(wxID_SHOW_Y, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
    516 EVT_MENU(wxID_SHOW_U, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
    517 EVT_MENU(wxID_SHOW_V, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
    518 EVT_MENU(wxID_NEXT_FRAME, AnalyzerFrame::onNextFrame)
    519 EVT_MENU(wxID_GOTO_FRAME, AnalyzerFrame::onGotoFrame)
    520 EVT_MENU(wxID_RESTART, AnalyzerFrame::onRestart)
    521 EVT_MENU(wxID_ABOUT, AnalyzerFrame::onAbout)
    522 END_EVENT_TABLE()
    523 
    524 AnalyzerFrame::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();
    544   +viewMenu->Append(wxID_PADDING, _("Toggle padding\tCtrl-p"),
    545                     _("Show padding"));
    546   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"));
    550   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"));
    557   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 
    577 void 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 
    586 void AnalyzerFrame::onClose(wxCommandEvent &WXUNUSED(event)) {}
    587 
    588 void AnalyzerFrame::onQuit(wxCommandEvent &WXUNUSED(event)) { Close(true); }
    589 
    590 void AnalyzerFrame::onTogglePadding(wxCommandEvent &WXUNUSED(event)) {
    591   panel->togglePadding();
    592   SetClientSize(panel->GetSize());
    593   panel->render();
    594   panel->Refresh();
    595 }
    596 
    597 void AnalyzerFrame::onZoomIn(wxCommandEvent &WXUNUSED(event)) {
    598   setZoom(panel->getZoom() + 1);
    599 }
    600 
    601 void AnalyzerFrame::onZoomOut(wxCommandEvent &WXUNUSED(event)) {
    602   setZoom(panel->getZoom() - 1);
    603 }
    604 
    605 void AnalyzerFrame::onActualSize(wxCommandEvent &WXUNUSED(event)) {
    606   setZoom(MIN_ZOOM);
    607 }
    608 
    609 void AnalyzerFrame::onToggleViewMenuCheckBox(wxCommandEvent &event) {  // NOLINT
    610   GetMenuBar()->Check(event.GetId(), event.IsChecked());
    611   updateViewMenu();
    612 }
    613 
    614 void 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 
    625 void AnalyzerFrame::onNextFrame(wxCommandEvent &WXUNUSED(event)) {
    626   panel->nextFrame();
    627   panel->Refresh(false);
    628 }
    629 
    630 void AnalyzerFrame::onGotoFrame(wxCommandEvent &WXUNUSED(event)) {}
    631 
    632 void AnalyzerFrame::onRestart(wxCommandEvent &WXUNUSED(event)) {}
    633 
    634 void 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 (at) mozilla.com>"));
    642   wxAboutBox(info);
    643 }
    644 
    645 bool 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 
    656 bool 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 
    669 void 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 
    678 class 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 
    687 static 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 
    697 void Analyzer::OnInitCmdLine(wxCmdLineParser &parser) {  // NOLINT
    698   parser.SetDesc(CMD_LINE_DESC);
    699   parser.SetSwitchChars(_("-"));
    700 }
    701 
    702 bool Analyzer::OnCmdLineParsed(wxCmdLineParser &parser) {  // NOLINT
    703   bool bit_accounting = parser.Found(_("a"));
    704   if (bit_accounting && !CONFIG_ACCOUNTING) {
    705     fprintf(stderr,
    706             "Bit accounting support not found. "
    707             "Recompile with:\n./cmake -DCONFIG_ACCOUNTING=1\n");
    708     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 
    718 void usage_exit(void) {
    719   fprintf(stderr, "uhh\n");
    720   exit(EXIT_FAILURE);
    721 }
    722 
    723 IMPLEMENT_APP(Analyzer)
    724