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 
AV1Decoder()74 AV1Decoder::AV1Decoder()
75     : reader(NULL), info(NULL), decoder(NULL), show_padding(false), image(NULL),
76       frame(0) {}
77 
~AV1Decoder()78 AV1Decoder::~AV1Decoder() {}
79 
togglePadding()80 void AV1Decoder::togglePadding() { show_padding = !show_padding; }
81 
open(const wxString & path)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 
close()105 void AV1Decoder::close() {}
106 
step()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 
getWidth() const128 int AV1Decoder::getWidth() const {
129   return info->frame_width + 2 * getWidthPadding();
130 }
131 
getWidthPadding() const132 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 
getHeight() const139 int AV1Decoder::getHeight() const {
140   return info->frame_height + 2 * getHeightPadding();
141 }
142 
getHeightPadding() const143 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 
getAccountingStruct(Accounting ** accounting)150 bool AV1Decoder::getAccountingStruct(Accounting **accounting) {
151   return aom_codec_control(&codec, AV1_GET_ACCOUNTING, accounting) ==
152          AOM_CODEC_OK;
153 }
154 
setInspectionCallback()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 
inspect(void * pbi,void * data)163 void AV1Decoder::inspect(void *pbi, void *data) {
164   AV1Decoder *decoder = (AV1Decoder *)data;
165   ifd_inspect(&decoder->frame_data, pbi);
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 
BEGIN_EVENT_TABLE(AnalyzerPanel,wxPanel)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 
~AnalyzerPanel()223 AnalyzerPanel::~AnalyzerPanel() { close(); }
224 
setShowPlane(bool show_plane,int mask)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 
render()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 
computeBitsPerPixel()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 
togglePadding()369 void AnalyzerPanel::togglePadding() {
370   decoder.togglePadding();
371   updateDisplaySize();
372 }
373 
nextFrame()374 bool AnalyzerPanel::nextFrame() {
375   if (decoder.step()) {
376     refresh();
377     return true;
378   }
379   return false;
380 }
381 
refresh()382 void AnalyzerPanel::refresh() {
383   if (bit_accounting) {
384     computeBitsPerPixel();
385   }
386   render();
387 }
388 
getDisplayWidth() const389 int AnalyzerPanel::getDisplayWidth() const { return zoom * decoder.getWidth(); }
390 
getDisplayHeight() const391 int AnalyzerPanel::getDisplayHeight() const {
392   return zoom * decoder.getHeight();
393 }
394 
updateDisplaySize()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 
open(const wxString & path)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 
close()431 void AnalyzerPanel::close() {
432   decoder.close();
433   free(pixels);
434   pixels = NULL;
435   free(bpp_q3);
436   bpp_q3 = NULL;
437 }
438 
getZoom() const439 int AnalyzerPanel::getZoom() const { return zoom; }
440 
setZoom(int z)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 
onPaint(wxPaintEvent &)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 
BEGIN_EVENT_TABLE(AnalyzerFrame,wxFrame)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 
onOpen(wxCommandEvent & WXUNUSED (event))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 
onClose(wxCommandEvent & WXUNUSED (event))586 void AnalyzerFrame::onClose(wxCommandEvent &WXUNUSED(event)) {}
587 
onQuit(wxCommandEvent & WXUNUSED (event))588 void AnalyzerFrame::onQuit(wxCommandEvent &WXUNUSED(event)) { Close(true); }
589 
onTogglePadding(wxCommandEvent & WXUNUSED (event))590 void AnalyzerFrame::onTogglePadding(wxCommandEvent &WXUNUSED(event)) {
591   panel->togglePadding();
592   SetClientSize(panel->GetSize());
593   panel->render();
594   panel->Refresh();
595 }
596 
onZoomIn(wxCommandEvent & WXUNUSED (event))597 void AnalyzerFrame::onZoomIn(wxCommandEvent &WXUNUSED(event)) {
598   setZoom(panel->getZoom() + 1);
599 }
600 
onZoomOut(wxCommandEvent & WXUNUSED (event))601 void AnalyzerFrame::onZoomOut(wxCommandEvent &WXUNUSED(event)) {
602   setZoom(panel->getZoom() - 1);
603 }
604 
onActualSize(wxCommandEvent & WXUNUSED (event))605 void AnalyzerFrame::onActualSize(wxCommandEvent &WXUNUSED(event)) {
606   setZoom(MIN_ZOOM);
607 }
608 
onToggleViewMenuCheckBox(wxCommandEvent & event)609 void AnalyzerFrame::onToggleViewMenuCheckBox(wxCommandEvent &event) {  // NOLINT
610   GetMenuBar()->Check(event.GetId(), event.IsChecked());
611   updateViewMenu();
612 }
613 
onResetAndToggleViewMenuCheckBox(wxCommandEvent & event)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 
onNextFrame(wxCommandEvent & WXUNUSED (event))625 void AnalyzerFrame::onNextFrame(wxCommandEvent &WXUNUSED(event)) {
626   panel->nextFrame();
627   panel->Refresh(false);
628 }
629 
onGotoFrame(wxCommandEvent & WXUNUSED (event))630 void AnalyzerFrame::onGotoFrame(wxCommandEvent &WXUNUSED(event)) {}
631 
onRestart(wxCommandEvent & WXUNUSED (event))632 void AnalyzerFrame::onRestart(wxCommandEvent &WXUNUSED(event)) {}
633 
onAbout(wxCommandEvent & WXUNUSED (event))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@mozilla.com>"));
642   wxAboutBox(info);
643 }
644 
open(const wxString & path)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 
setZoom(int zoom)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 
updateViewMenu()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 
OnInitCmdLine(wxCmdLineParser & parser)697 void Analyzer::OnInitCmdLine(wxCmdLineParser &parser) {  // NOLINT
698   parser.SetDesc(CMD_LINE_DESC);
699   parser.SetSwitchChars(_("-"));
700 }
701 
OnCmdLineParsed(wxCmdLineParser & parser)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 
usage_exit(void)718 void usage_exit(void) {
719   fprintf(stderr, "uhh\n");
720   exit(EXIT_FAILURE);
721 }
722 
723 IMPLEMENT_APP(Analyzer)
724