blob: 752f1bd2e9c7421258909b11b350528f64c426d4 [file] [log] [blame]
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -04001/*
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#include "./tools_common.h"
16#include "./video_reader.h"
17#include "aom/aom_decoder.h"
18#include "aom/aomdx.h"
19#include "av1/common/accounting.h"
20#include "av1/common/onyxc_int.h"
Nathan E. Egge2693ca52017-02-22 16:23:02 -050021#include "av1/decoder/inspection.h"
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040022
23#define OD_SIGNMASK(a) (-((a) < 0))
24#define OD_FLIPSIGNI(a, b) (((a) + OD_SIGNMASK(b)) ^ OD_SIGNMASK(b))
25#define OD_DIV_ROUND(x, y) (((x) + OD_FLIPSIGNI((y) >> 1, x)) / (y))
26
Nathan E. Egge625bc262017-02-22 10:42:54 -050027enum {
28 OD_LUMA_MASK = 1 << 0,
29 OD_CB_MASK = 1 << 1,
30 OD_CR_MASK = 1 << 2,
31 OD_ALL_MASK = OD_LUMA_MASK | OD_CB_MASK | OD_CR_MASK
32};
33
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040034class AV1Decoder {
35 private:
36 FILE *input;
37 wxString path;
38
39 AvxVideoReader *reader;
40 const AvxVideoInfo *info;
41 const AvxInterface *decoder;
42
Nathan E. Egge2693ca52017-02-22 16:23:02 -050043 insp_frame_data frame_data;
44
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040045 aom_codec_ctx_t codec;
Tristan Matthews4d5182c2017-03-21 10:30:54 -040046 bool show_padding;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040047
48 public:
49 aom_image_t *image;
50 int frame;
51
Nathan E. Egge625bc262017-02-22 10:42:54 -050052 int plane_mask;
53
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040054 AV1Decoder();
55 ~AV1Decoder();
56
57 bool open(const wxString &path);
58 void close();
59 bool step();
60
Tristan Matthews4d5182c2017-03-21 10:30:54 -040061 int getWidthPadding() const;
62 int getHeightPadding() const;
63 void togglePadding();
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040064 int getWidth() const;
65 int getHeight() const;
66
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040067 bool getAccountingStruct(Accounting **acct);
Nathan E. Egge2693ca52017-02-22 16:23:02 -050068 bool setInspectionCallback();
69
70 static void inspect(void *decoder, void *data);
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040071};
72
73AV1Decoder::AV1Decoder()
Tristan Matthews4d5182c2017-03-21 10:30:54 -040074 : reader(NULL), info(NULL), decoder(NULL), show_padding(false), image(NULL),
75 frame(0) {}
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040076
77AV1Decoder::~AV1Decoder() {}
78
Tristan Matthews4d5182c2017-03-21 10:30:54 -040079void AV1Decoder::togglePadding() { show_padding = !show_padding; }
80
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040081bool 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->codec_interface()));
95 if (aom_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0)) {
96 fprintf(stderr, "Failed to initialize decoder.");
97 return false;
98 }
Nathan E. Egge2693ca52017-02-22 16:23:02 -050099 ifd_init(&frame_data, info->frame_width, info->frame_height);
100 setInspectionCallback();
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400101 return true;
102}
103
104void AV1Decoder::close() {}
105
106bool 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, 0)) {
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
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400127int AV1Decoder::getWidth() const {
128 return info->frame_width + 2 * getWidthPadding();
129}
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400130
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400131int AV1Decoder::getWidthPadding() const {
132 return show_padding
133 ? AOMMAX(info->frame_width + 16,
134 ALIGN_POWER_OF_TWO(info->frame_width, 6)) -
135 info->frame_width
136 : 0;
137}
138
139int AV1Decoder::getHeight() const {
140 return info->frame_height + 2 * getHeightPadding();
141}
142
143int AV1Decoder::getHeightPadding() const {
144 return show_padding
145 ? AOMMAX(info->frame_height + 16,
146 ALIGN_POWER_OF_TWO(info->frame_height, 6)) -
147 info->frame_height
148 : 0;
149}
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400150
151bool AV1Decoder::getAccountingStruct(Accounting **accounting) {
152 return aom_codec_control(&codec, AV1_GET_ACCOUNTING, accounting) ==
153 AOM_CODEC_OK;
154}
155
Nathan E. Egge2693ca52017-02-22 16:23:02 -0500156bool AV1Decoder::setInspectionCallback() {
157 aom_inspect_init ii;
158 ii.inspect_cb = AV1Decoder::inspect;
159 ii.inspect_ctx = (void *)this;
160 return aom_codec_control(&codec, AV1_SET_INSPECTION_CALLBACK, &ii) ==
161 AOM_CODEC_OK;
162}
163
164void AV1Decoder::inspect(void *pbi, void *data) {
165 AV1Decoder *decoder = (AV1Decoder *)data;
166 ifd_inspect(&decoder->frame_data, pbi);
167}
168
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400169#define MIN_ZOOM (1)
170#define MAX_ZOOM (4)
171
172class AnalyzerPanel : public wxPanel {
173 DECLARE_EVENT_TABLE()
174
175 private:
176 AV1Decoder decoder;
177 const wxString path;
178
179 int zoom;
180 unsigned char *pixels;
181
182 const bool bit_accounting;
183 double *bpp_q3;
184
Nathan E. Egge625bc262017-02-22 10:42:54 -0500185 int plane_mask;
186
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400187 // The display size is the decode size, scaled by the zoom.
188 int getDisplayWidth() const;
189 int getDisplayHeight() const;
190
191 bool updateDisplaySize();
192
193 void computeBitsPerPixel();
194
195 public:
196 AnalyzerPanel(wxWindow *parent, const wxString &path,
197 const bool bit_accounting);
198 ~AnalyzerPanel();
199
200 bool open(const wxString &path);
201 void close();
202 void render();
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400203 void togglePadding();
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400204 bool nextFrame();
205 void refresh();
206
207 int getZoom() const;
208 bool setZoom(int zoom);
209
Nathan E. Egge625bc262017-02-22 10:42:54 -0500210 void setShowPlane(bool show_plane, int mask);
211
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400212 void onPaint(wxPaintEvent &event); // NOLINT
213};
214
215BEGIN_EVENT_TABLE(AnalyzerPanel, wxPanel)
216EVT_PAINT(AnalyzerPanel::onPaint)
217END_EVENT_TABLE()
218
219AnalyzerPanel::AnalyzerPanel(wxWindow *parent, const wxString &path,
220 const bool bit_accounting)
221 : wxPanel(parent), path(path), zoom(0), pixels(NULL),
Nathan E. Egge625bc262017-02-22 10:42:54 -0500222 bit_accounting(bit_accounting), bpp_q3(NULL), plane_mask(OD_ALL_MASK) {}
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400223
224AnalyzerPanel::~AnalyzerPanel() { close(); }
225
Nathan E. Egge625bc262017-02-22 10:42:54 -0500226void AnalyzerPanel::setShowPlane(bool show_plane, int mask) {
227 if (show_plane) {
228 plane_mask |= mask;
229 } else {
230 plane_mask &= ~mask;
231 }
232}
233
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400234void AnalyzerPanel::render() {
235 aom_image_t *img = decoder.image;
236 int y_stride = img->stride[0];
237 int cb_stride = img->stride[1];
238 int cr_stride = img->stride[2];
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 unsigned char *p_row = pixels;
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400244 int y_width_padding = decoder.getWidthPadding();
245 int cb_width_padding = y_width_padding >> 1;
246 int cr_width_padding = y_width_padding >> 1;
247 int y_height_padding = decoder.getHeightPadding();
248 int cb_height_padding = y_height_padding >> 1;
249 int cr_height_padding = y_height_padding >> 1;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400250 for (int j = 0; j < decoder.getHeight(); j++) {
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400251 unsigned char *y = y_row - y_stride * y_height_padding;
252 unsigned char *cb = cb_row - cb_stride * cb_height_padding;
253 unsigned char *cr = cr_row - cr_stride * cr_height_padding;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400254 unsigned char *p = p_row;
255 for (int i = 0; i < decoder.getWidth(); i++) {
256 int64_t yval;
257 int64_t cbval;
258 int64_t crval;
Nathan E. Egge625bc262017-02-22 10:42:54 -0500259 int pmask;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400260 unsigned rval;
261 unsigned gval;
262 unsigned bval;
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400263 yval = *(y - y_width_padding);
264 cbval = *(cb - cb_width_padding);
265 crval = *(cr - cr_width_padding);
Nathan E. Egge625bc262017-02-22 10:42:54 -0500266 pmask = plane_mask;
267 if (pmask & OD_LUMA_MASK) {
268 yval -= 16;
269 } else {
270 yval = 128;
271 }
272 cbval = ((pmask & OD_CB_MASK) >> 1) * (cbval - 128);
273 crval = ((pmask & OD_CR_MASK) >> 2) * (crval - 128);
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400274 /*This is intentionally slow and very accurate.*/
275 rval = OD_CLAMPI(0, (int32_t)OD_DIV_ROUND(
276 2916394880000LL * yval + 4490222169144LL * crval,
277 9745792000LL),
278 65535);
279 gval = OD_CLAMPI(0, (int32_t)OD_DIV_ROUND(2916394880000LL * yval -
280 534117096223LL * cbval -
281 1334761232047LL * crval,
282 9745792000LL),
283 65535);
284 bval = OD_CLAMPI(0, (int32_t)OD_DIV_ROUND(
285 2916394880000LL * yval + 5290866304968LL * cbval,
286 9745792000LL),
287 65535);
288 unsigned char *px_row = p;
289 for (int v = 0; v < zoom; v++) {
290 unsigned char *px = px_row;
291 for (int u = 0; u < zoom; u++) {
292 *(px + 0) = (unsigned char)(rval >> 8);
293 *(px + 1) = (unsigned char)(gval >> 8);
294 *(px + 2) = (unsigned char)(bval >> 8);
295 px += 3;
296 }
297 px_row += p_stride;
298 }
299 int dc = ((y - y_row) & 1) | (1 - img->x_chroma_shift);
300 y++;
301 cb += dc;
302 cr += dc;
303 p += zoom * 3;
304 }
305 int dc = -((j & 1) | (1 - img->y_chroma_shift));
306 y_row += y_stride;
307 cb_row += dc & cb_stride;
308 cr_row += dc & cr_stride;
309 p_row += zoom * p_stride;
310 }
311}
312
313void AnalyzerPanel::computeBitsPerPixel() {
314 Accounting *acct;
315 double bpp_total;
316 int totals_q3[MAX_SYMBOL_TYPES] = { 0 };
317 int sym_count[MAX_SYMBOL_TYPES] = { 0 };
318 decoder.getAccountingStruct(&acct);
319 for (int j = 0; j < decoder.getHeight(); j++) {
320 for (int i = 0; i < decoder.getWidth(); i++) {
321 bpp_q3[j * decoder.getWidth() + i] = 0.0;
322 }
323 }
324 bpp_total = 0;
325 for (int i = 0; i < acct->syms.num_syms; i++) {
326 AccountingSymbol *s;
327 s = &acct->syms.syms[i];
328 totals_q3[s->id] += s->bits;
329 sym_count[s->id] += s->samples;
330 }
331 printf("=== Frame: %-3i ===\n", decoder.frame - 1);
332 for (int i = 0; i < acct->syms.dictionary.num_strs; i++) {
333 if (totals_q3[i]) {
334 printf("%30s = %10.3f (%f bit/symbol)\n", acct->syms.dictionary.strs[i],
335 (float)totals_q3[i] / 8, (float)totals_q3[i] / 8 / sym_count[i]);
336 }
337 }
338 printf("\n");
339}
340
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400341void AnalyzerPanel::togglePadding() {
342 decoder.togglePadding();
343 updateDisplaySize();
344}
345
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400346bool AnalyzerPanel::nextFrame() {
347 if (decoder.step()) {
348 refresh();
349 return true;
350 }
351 return false;
352}
353
354void AnalyzerPanel::refresh() {
355 if (bit_accounting) {
356 computeBitsPerPixel();
357 }
358 render();
359}
360
361int AnalyzerPanel::getDisplayWidth() const { return zoom * decoder.getWidth(); }
362
363int AnalyzerPanel::getDisplayHeight() const {
364 return zoom * decoder.getHeight();
365}
366
367bool AnalyzerPanel::updateDisplaySize() {
368 unsigned char *p = (unsigned char *)malloc(
369 sizeof(*p) * 3 * getDisplayWidth() * getDisplayHeight());
370 if (p == NULL) {
371 return false;
372 }
373 free(pixels);
374 pixels = p;
375 SetSize(getDisplayWidth(), getDisplayHeight());
376 return true;
377}
378
379bool AnalyzerPanel::open(const wxString &path) {
380 if (!decoder.open(path)) {
381 return false;
382 }
383 if (!setZoom(MIN_ZOOM)) {
384 return false;
385 }
386 if (bit_accounting) {
387 bpp_q3 = (double *)malloc(sizeof(*bpp_q3) * decoder.getWidth() *
388 decoder.getHeight());
389 if (bpp_q3 == NULL) {
390 fprintf(stderr, "Could not allocate memory for bit accounting\n");
391 close();
392 return false;
393 }
394 }
395 if (!nextFrame()) {
396 close();
397 return false;
398 }
399 SetFocus();
400 return true;
401}
402
403void AnalyzerPanel::close() {
404 decoder.close();
405 free(pixels);
406 pixels = NULL;
407 free(bpp_q3);
408 bpp_q3 = NULL;
409}
410
411int AnalyzerPanel::getZoom() const { return zoom; }
412
413bool AnalyzerPanel::setZoom(int z) {
414 if (z <= MAX_ZOOM && z >= MIN_ZOOM && zoom != z) {
415 int old_zoom = zoom;
416 zoom = z;
417 if (!updateDisplaySize()) {
418 zoom = old_zoom;
419 return false;
420 }
421 return true;
422 }
423 return false;
424}
425
426void AnalyzerPanel::onPaint(wxPaintEvent &) {
427 wxBitmap bmp(wxImage(getDisplayWidth(), getDisplayHeight(), pixels, true));
428 wxBufferedPaintDC dc(this, bmp);
429}
430
431class AnalyzerFrame : public wxFrame {
432 DECLARE_EVENT_TABLE()
433
434 private:
435 AnalyzerPanel *panel;
436 const bool bit_accounting;
437
438 wxMenu *fileMenu;
439 wxMenu *viewMenu;
440 wxMenu *playbackMenu;
441
442 public:
443 AnalyzerFrame(const bool bit_accounting); // NOLINT
444
445 void onOpen(wxCommandEvent &event); // NOLINT
446 void onClose(wxCommandEvent &event); // NOLINT
447 void onQuit(wxCommandEvent &event); // NOLINT
448
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400449 void onTogglePadding(wxCommandEvent &event); // NOLINT
450 void onZoomIn(wxCommandEvent &event); // NOLINT
451 void onZoomOut(wxCommandEvent &event); // NOLINT
452 void onActualSize(wxCommandEvent &event); // NOLINT
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400453
Nathan E. Egge625bc262017-02-22 10:42:54 -0500454 void onToggleViewMenuCheckBox(wxCommandEvent &event); // NOLINT
455 void onResetAndToggleViewMenuCheckBox(wxCommandEvent &event); // NOLINT
456
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400457 void onNextFrame(wxCommandEvent &event); // NOLINT
458 void onGotoFrame(wxCommandEvent &event); // NOLINT
459 void onRestart(wxCommandEvent &event); // NOLINT
460
461 void onAbout(wxCommandEvent &event); // NOLINT
462
463 bool open(const wxString &path);
464 bool setZoom(int zoom);
Nathan E. Egge625bc262017-02-22 10:42:54 -0500465 void updateViewMenu();
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400466};
467
468enum {
469 wxID_NEXT_FRAME = 6000,
Nathan E. Egge625bc262017-02-22 10:42:54 -0500470 wxID_SHOW_Y,
471 wxID_SHOW_U,
472 wxID_SHOW_V,
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400473 wxID_GOTO_FRAME,
474 wxID_RESTART,
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400475 wxID_ACTUAL_SIZE,
476 wxID_PADDING
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400477};
478
479BEGIN_EVENT_TABLE(AnalyzerFrame, wxFrame)
480EVT_MENU(wxID_OPEN, AnalyzerFrame::onOpen)
481EVT_MENU(wxID_CLOSE, AnalyzerFrame::onClose)
482EVT_MENU(wxID_EXIT, AnalyzerFrame::onQuit)
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400483EVT_MENU(wxID_PADDING, AnalyzerFrame::onTogglePadding)
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400484EVT_MENU(wxID_ZOOM_IN, AnalyzerFrame::onZoomIn)
485EVT_MENU(wxID_ZOOM_OUT, AnalyzerFrame::onZoomOut)
486EVT_MENU(wxID_ACTUAL_SIZE, AnalyzerFrame::onActualSize)
Nathan E. Egge625bc262017-02-22 10:42:54 -0500487EVT_MENU(wxID_SHOW_Y, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
488EVT_MENU(wxID_SHOW_U, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
489EVT_MENU(wxID_SHOW_V, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400490EVT_MENU(wxID_NEXT_FRAME, AnalyzerFrame::onNextFrame)
491EVT_MENU(wxID_GOTO_FRAME, AnalyzerFrame::onGotoFrame)
492EVT_MENU(wxID_RESTART, AnalyzerFrame::onRestart)
493EVT_MENU(wxID_ABOUT, AnalyzerFrame::onAbout)
494END_EVENT_TABLE()
495
496AnalyzerFrame::AnalyzerFrame(const bool bit_accounting)
497 : wxFrame(NULL, wxID_ANY, _("AV1 Stream Analyzer"), wxDefaultPosition,
498 wxDefaultSize, wxDEFAULT_FRAME_STYLE),
499 panel(NULL), bit_accounting(bit_accounting) {
500 wxMenuBar *mb = new wxMenuBar();
501
502 fileMenu = new wxMenu();
503 fileMenu->Append(wxID_OPEN, _("&Open...\tCtrl-O"), _("Open daala file"));
504 fileMenu->Append(wxID_CLOSE, _("&Close\tCtrl-W"), _("Close daala file"));
505 fileMenu->Enable(wxID_CLOSE, false);
506 fileMenu->Append(wxID_EXIT, _("E&xit\tCtrl-Q"), _("Quit this program"));
507 mb->Append(fileMenu, _("&File"));
508
509 wxAcceleratorEntry entries[2];
510 entries[0].Set(wxACCEL_CTRL, (int)'=', wxID_ZOOM_IN);
511 entries[1].Set(wxACCEL_CTRL | wxACCEL_SHIFT, (int)'-', wxID_ZOOM_OUT);
512 wxAcceleratorTable accel(2, entries);
513 this->SetAcceleratorTable(accel);
514
515 viewMenu = new wxMenu();
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400516 +viewMenu->Append(wxID_PADDING, _("Toggle padding\tCtrl-p"),
517 _("Show padding"));
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400518 viewMenu->Append(wxID_ZOOM_IN, _("Zoom-In\tCtrl-+"), _("Double image size"));
519 viewMenu->Append(wxID_ZOOM_OUT, _("Zoom-Out\tCtrl--"), _("Half image size"));
520 viewMenu->Append(wxID_ACTUAL_SIZE, _("Actual size\tCtrl-0"),
521 _("Actual size of the frame"));
Nathan E. Egge625bc262017-02-22 10:42:54 -0500522 viewMenu->AppendSeparator();
523 viewMenu->AppendCheckItem(wxID_SHOW_Y, _("&Y plane\tCtrl-Y"),
524 _("Show Y plane"));
525 viewMenu->AppendCheckItem(wxID_SHOW_U, _("&U plane\tCtrl-U"),
526 _("Show U plane"));
527 viewMenu->AppendCheckItem(wxID_SHOW_V, _("&V plane\tCtrl-V"),
528 _("Show V plane"));
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400529 mb->Append(viewMenu, _("&View"));
530
531 playbackMenu = new wxMenu();
532 playbackMenu->Append(wxID_NEXT_FRAME, _("Next frame\tCtrl-."),
533 _("Go to next frame"));
534 /*playbackMenu->Append(wxID_RESTART, _("&Restart\tCtrl-R"),
535 _("Set video to frame 0"));
536 playbackMenu->Append(wxID_GOTO_FRAME, _("Jump to Frame\tCtrl-J"),
537 _("Go to frame number"));*/
538 mb->Append(playbackMenu, _("&Playback"));
539
540 wxMenu *helpMenu = new wxMenu();
541 helpMenu->Append(wxID_ABOUT, _("&About...\tF1"), _("Show about dialog"));
542 mb->Append(helpMenu, _("&Help"));
543
544 SetMenuBar(mb);
545
546 CreateStatusBar(1);
547}
548
549void AnalyzerFrame::onOpen(wxCommandEvent &WXUNUSED(event)) {
550 wxFileDialog openFileDialog(this, _("Open file"), wxEmptyString,
551 wxEmptyString, _("AV1 files (*.ivf)|*.ivf"),
552 wxFD_OPEN | wxFD_FILE_MUST_EXIST);
553 if (openFileDialog.ShowModal() != wxID_CANCEL) {
554 open(openFileDialog.GetPath());
555 }
556}
557
558void AnalyzerFrame::onClose(wxCommandEvent &WXUNUSED(event)) {}
559
560void AnalyzerFrame::onQuit(wxCommandEvent &WXUNUSED(event)) { Close(true); }
561
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400562void AnalyzerFrame::onTogglePadding(wxCommandEvent &WXUNUSED(event)) {
563 panel->togglePadding();
564 SetClientSize(panel->GetSize());
565 panel->render();
566 panel->Refresh();
567}
568
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400569void AnalyzerFrame::onZoomIn(wxCommandEvent &WXUNUSED(event)) {
570 setZoom(panel->getZoom() + 1);
571}
572
573void AnalyzerFrame::onZoomOut(wxCommandEvent &WXUNUSED(event)) {
574 setZoom(panel->getZoom() - 1);
575}
576
577void AnalyzerFrame::onActualSize(wxCommandEvent &WXUNUSED(event)) {
578 setZoom(MIN_ZOOM);
579}
580
Nathan E. Egge625bc262017-02-22 10:42:54 -0500581void AnalyzerFrame::onToggleViewMenuCheckBox(wxCommandEvent &event) { // NOLINT
582 GetMenuBar()->Check(event.GetId(), event.IsChecked());
583 updateViewMenu();
584}
585
586void AnalyzerFrame::onResetAndToggleViewMenuCheckBox(
587 wxCommandEvent &event) { // NOLINT
588 int id = event.GetId();
589 if (id != wxID_SHOW_Y && id != wxID_SHOW_U && id != wxID_SHOW_V) {
590 GetMenuBar()->Check(wxID_SHOW_Y, true);
591 GetMenuBar()->Check(wxID_SHOW_U, true);
592 GetMenuBar()->Check(wxID_SHOW_V, true);
593 }
594 onToggleViewMenuCheckBox(event);
595}
596
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400597void AnalyzerFrame::onNextFrame(wxCommandEvent &WXUNUSED(event)) {
598 panel->nextFrame();
599 panel->Refresh(false);
600}
601
602void AnalyzerFrame::onGotoFrame(wxCommandEvent &WXUNUSED(event)) {}
603
604void AnalyzerFrame::onRestart(wxCommandEvent &WXUNUSED(event)) {}
605
606void AnalyzerFrame::onAbout(wxCommandEvent &WXUNUSED(event)) {
607 wxAboutDialogInfo info;
608 info.SetName(_("AV1 Bitstream Analyzer"));
609 info.SetVersion(_("0.1-beta"));
610 info.SetDescription(
611 _("This program implements a bitstream analyzer for AV1"));
612 info.SetCopyright(
613 wxT("(C) 2017 Alliance for Open Media <negge@mozilla.com>"));
614 wxAboutBox(info);
615}
616
617bool AnalyzerFrame::open(const wxString &path) {
618 panel = new AnalyzerPanel(this, path, bit_accounting);
619 if (panel->open(path)) {
620 SetClientSize(panel->GetSize());
621 return true;
622 } else {
623 delete panel;
624 return false;
625 }
626}
627
628bool AnalyzerFrame::setZoom(int zoom) {
629 if (panel->setZoom(zoom)) {
630 GetMenuBar()->Enable(wxID_ACTUAL_SIZE, zoom != MIN_ZOOM);
631 GetMenuBar()->Enable(wxID_ZOOM_IN, zoom != MAX_ZOOM);
632 GetMenuBar()->Enable(wxID_ZOOM_OUT, zoom != MIN_ZOOM);
633 SetClientSize(panel->GetSize());
634 panel->render();
635 panel->Refresh();
636 return true;
637 }
638 return false;
639}
640
Nathan E. Egge625bc262017-02-22 10:42:54 -0500641void AnalyzerFrame::updateViewMenu() {
642 panel->setShowPlane(GetMenuBar()->IsChecked(wxID_SHOW_Y), OD_LUMA_MASK);
643 panel->setShowPlane(GetMenuBar()->IsChecked(wxID_SHOW_U), OD_CB_MASK);
644 panel->setShowPlane(GetMenuBar()->IsChecked(wxID_SHOW_V), OD_CR_MASK);
645 SetClientSize(panel->GetSize());
646 panel->render();
647 panel->Refresh(false);
648}
649
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400650class Analyzer : public wxApp {
651 private:
652 AnalyzerFrame *frame;
653
654 public:
655 void OnInitCmdLine(wxCmdLineParser &parser); // NOLINT
656 bool OnCmdLineParsed(wxCmdLineParser &parser); // NOLINT
657};
658
659static const wxCmdLineEntryDesc CMD_LINE_DESC[] = {
660 { wxCMD_LINE_SWITCH, _("h"), _("help"), _("Display this help and exit."),
661 wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
662 { wxCMD_LINE_SWITCH, _("a"), _("bit-accounting"), _("Enable bit accounting"),
663 wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
664 { wxCMD_LINE_PARAM, NULL, NULL, _("input.ivf"), wxCMD_LINE_VAL_STRING,
665 wxCMD_LINE_PARAM_OPTIONAL },
666 { wxCMD_LINE_NONE }
667};
668
669void Analyzer::OnInitCmdLine(wxCmdLineParser &parser) { // NOLINT
670 parser.SetDesc(CMD_LINE_DESC);
671 parser.SetSwitchChars(_("-"));
672}
673
674bool Analyzer::OnCmdLineParsed(wxCmdLineParser &parser) { // NOLINT
675 bool bit_accounting = parser.Found(_("a"));
676 if (bit_accounting && !CONFIG_ACCOUNTING) {
677 fprintf(stderr,
678 "Bit accounting support not found. "
679 "Recompile with:\n./configure --enable-accounting\n");
680 return false;
681 }
682 frame = new AnalyzerFrame(parser.Found(_("a")));
683 frame->Show();
684 if (parser.GetParamCount() > 0) {
685 return frame->open(parser.GetParam(0));
686 }
687 return true;
688}
689
690void usage_exit(void) {
691 fprintf(stderr, "uhh\n");
692 exit(EXIT_FAILURE);
693}
694
695IMPLEMENT_APP(Analyzer)