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 <[email protected]>"));
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