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 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, 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
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 AV1 file"));
532 fileMenu->Append(wxID_CLOSE, _("&Close\tCtrl-W"), _("Close AV1 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