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