blob: 0e7ba1501e8f1b8f5d0c8d07c824d9b9d6bcf5f2 [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"
Luc Trudeau83fbd572017-04-21 11:24:34 -040019#include "av1/decoder/accounting.h"
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -040020#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);
Sean DuBois47cc2552018-01-23 07:44:16 +0000111 if (aom_codec_decode(&codec, frame_data, frame_size, NULL)) {
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400112 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 {
Johann3c30fb42018-02-08 14:33:20 -0800132 return show_padding ? AOMMAX(info->frame_width + 16,
133 ALIGN_POWER_OF_TWO(info->frame_width, 6)) -
134 info->frame_width
135 : 0;
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400136}
137
138int AV1Decoder::getHeight() const {
139 return info->frame_height + 2 * getHeightPadding();
140}
141
142int AV1Decoder::getHeightPadding() const {
Johann3c30fb42018-02-08 14:33:20 -0800143 return show_padding ? AOMMAX(info->frame_height + 16,
144 ALIGN_POWER_OF_TWO(info->frame_height, 6)) -
145 info->frame_height
146 : 0;
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400147}
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400148
149bool AV1Decoder::getAccountingStruct(Accounting **accounting) {
150 return aom_codec_control(&codec, AV1_GET_ACCOUNTING, accounting) ==
151 AOM_CODEC_OK;
152}
153
Nathan E. Egge2693ca52017-02-22 16:23:02 -0500154bool 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
162void AV1Decoder::inspect(void *pbi, void *data) {
163 AV1Decoder *decoder = (AV1Decoder *)data;
164 ifd_inspect(&decoder->frame_data, pbi);
165}
166
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400167#define MIN_ZOOM (1)
168#define MAX_ZOOM (4)
169
170class 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
Nathan E. Egge625bc262017-02-22 10:42:54 -0500183 int plane_mask;
184
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400185 // 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();
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400201 void togglePadding();
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400202 bool nextFrame();
203 void refresh();
204
205 int getZoom() const;
206 bool setZoom(int zoom);
207
Nathan E. Egge625bc262017-02-22 10:42:54 -0500208 void setShowPlane(bool show_plane, int mask);
209
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400210 void onPaint(wxPaintEvent &event); // NOLINT
211};
212
213BEGIN_EVENT_TABLE(AnalyzerPanel, wxPanel)
214EVT_PAINT(AnalyzerPanel::onPaint)
215END_EVENT_TABLE()
216
217AnalyzerPanel::AnalyzerPanel(wxWindow *parent, const wxString &path,
218 const bool bit_accounting)
219 : wxPanel(parent), path(path), zoom(0), pixels(NULL),
Nathan E. Egge625bc262017-02-22 10:42:54 -0500220 bit_accounting(bit_accounting), bpp_q3(NULL), plane_mask(OD_ALL_MASK) {}
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400221
222AnalyzerPanel::~AnalyzerPanel() { close(); }
223
Nathan E. Egge625bc262017-02-22 10:42:54 -0500224void AnalyzerPanel::setShowPlane(bool show_plane, int mask) {
225 if (show_plane) {
226 plane_mask |= mask;
227 } else {
228 plane_mask &= ~mask;
229 }
230}
231
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400232void AnalyzerPanel::render() {
233 aom_image_t *img = decoder.image;
Tristan Matthews86a67382017-06-28 14:28:32 -0400234 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;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400238 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];
Tristan Matthews86a67382017-06-28 14:28:32 -0400242 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);
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400245 unsigned char *p_row = pixels;
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400246 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;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400252 for (int j = 0; j < decoder.getHeight(); j++) {
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400253 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;
Tristan Matthews86a67382017-06-28 14:28:32 -0400256 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;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400259 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;
Nathan E. Egge625bc262017-02-22 10:42:54 -0500264 int pmask;
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400265 unsigned rval;
266 unsigned gval;
267 unsigned bval;
Tristan Matthews86a67382017-06-28 14:28:32 -0400268 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 }
Nathan E. Egge625bc262017-02-22 10:42:54 -0500277 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);
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400285 /*This is intentionally slow and very accurate.*/
Johann3c30fb42018-02-08 14:33:20 -0800286 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),
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400296 65535);
Johann3c30fb42018-02-08 14:33:20 -0800297 bval = OD_CLAMPI(
298 0,
299 (int32_t)OD_DIV_ROUND(
300 2916394880000LL * yval + 5290866304968LL * cbval, 9745792000LL),
301 65535);
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400302 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 }
Tristan Matthews86a67382017-06-28 14:28:32 -0400313 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 }
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400324 p += zoom * 3;
325 }
326 int dc = -((j & 1) | (1 - img->y_chroma_shift));
Tristan Matthews86a67382017-06-28 14:28:32 -0400327 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 }
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400336 p_row += zoom * p_stride;
337 }
338}
339
340void 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
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400368void AnalyzerPanel::togglePadding() {
369 decoder.togglePadding();
370 updateDisplaySize();
371}
372
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400373bool AnalyzerPanel::nextFrame() {
374 if (decoder.step()) {
375 refresh();
376 return true;
377 }
378 return false;
379}
380
381void AnalyzerPanel::refresh() {
382 if (bit_accounting) {
383 computeBitsPerPixel();
384 }
385 render();
386}
387
388int AnalyzerPanel::getDisplayWidth() const { return zoom * decoder.getWidth(); }
389
390int AnalyzerPanel::getDisplayHeight() const {
391 return zoom * decoder.getHeight();
392}
393
394bool 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
406bool 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
430void AnalyzerPanel::close() {
431 decoder.close();
432 free(pixels);
433 pixels = NULL;
434 free(bpp_q3);
435 bpp_q3 = NULL;
436}
437
438int AnalyzerPanel::getZoom() const { return zoom; }
439
440bool 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
453void AnalyzerPanel::onPaint(wxPaintEvent &) {
454 wxBitmap bmp(wxImage(getDisplayWidth(), getDisplayHeight(), pixels, true));
455 wxBufferedPaintDC dc(this, bmp);
456}
457
458class 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
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400476 void onTogglePadding(wxCommandEvent &event); // NOLINT
477 void onZoomIn(wxCommandEvent &event); // NOLINT
478 void onZoomOut(wxCommandEvent &event); // NOLINT
479 void onActualSize(wxCommandEvent &event); // NOLINT
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400480
Nathan E. Egge625bc262017-02-22 10:42:54 -0500481 void onToggleViewMenuCheckBox(wxCommandEvent &event); // NOLINT
482 void onResetAndToggleViewMenuCheckBox(wxCommandEvent &event); // NOLINT
483
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400484 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);
Nathan E. Egge625bc262017-02-22 10:42:54 -0500492 void updateViewMenu();
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400493};
494
495enum {
496 wxID_NEXT_FRAME = 6000,
Nathan E. Egge625bc262017-02-22 10:42:54 -0500497 wxID_SHOW_Y,
498 wxID_SHOW_U,
499 wxID_SHOW_V,
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400500 wxID_GOTO_FRAME,
501 wxID_RESTART,
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400502 wxID_ACTUAL_SIZE,
503 wxID_PADDING
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400504};
505
506BEGIN_EVENT_TABLE(AnalyzerFrame, wxFrame)
507EVT_MENU(wxID_OPEN, AnalyzerFrame::onOpen)
508EVT_MENU(wxID_CLOSE, AnalyzerFrame::onClose)
509EVT_MENU(wxID_EXIT, AnalyzerFrame::onQuit)
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400510EVT_MENU(wxID_PADDING, AnalyzerFrame::onTogglePadding)
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400511EVT_MENU(wxID_ZOOM_IN, AnalyzerFrame::onZoomIn)
512EVT_MENU(wxID_ZOOM_OUT, AnalyzerFrame::onZoomOut)
513EVT_MENU(wxID_ACTUAL_SIZE, AnalyzerFrame::onActualSize)
Nathan E. Egge625bc262017-02-22 10:42:54 -0500514EVT_MENU(wxID_SHOW_Y, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
515EVT_MENU(wxID_SHOW_U, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
516EVT_MENU(wxID_SHOW_V, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400517EVT_MENU(wxID_NEXT_FRAME, AnalyzerFrame::onNextFrame)
518EVT_MENU(wxID_GOTO_FRAME, AnalyzerFrame::onGotoFrame)
519EVT_MENU(wxID_RESTART, AnalyzerFrame::onRestart)
520EVT_MENU(wxID_ABOUT, AnalyzerFrame::onAbout)
521END_EVENT_TABLE()
522
523AnalyzerFrame::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 daala file"));
531 fileMenu->Append(wxID_CLOSE, _("&Close\tCtrl-W"), _("Close daala 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();
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400543 +viewMenu->Append(wxID_PADDING, _("Toggle padding\tCtrl-p"),
544 _("Show padding"));
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400545 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"));
Nathan E. Egge625bc262017-02-22 10:42:54 -0500549 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"));
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400556 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
576void 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
585void AnalyzerFrame::onClose(wxCommandEvent &WXUNUSED(event)) {}
586
587void AnalyzerFrame::onQuit(wxCommandEvent &WXUNUSED(event)) { Close(true); }
588
Tristan Matthews4d5182c2017-03-21 10:30:54 -0400589void AnalyzerFrame::onTogglePadding(wxCommandEvent &WXUNUSED(event)) {
590 panel->togglePadding();
591 SetClientSize(panel->GetSize());
592 panel->render();
593 panel->Refresh();
594}
595
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400596void AnalyzerFrame::onZoomIn(wxCommandEvent &WXUNUSED(event)) {
597 setZoom(panel->getZoom() + 1);
598}
599
600void AnalyzerFrame::onZoomOut(wxCommandEvent &WXUNUSED(event)) {
601 setZoom(panel->getZoom() - 1);
602}
603
604void AnalyzerFrame::onActualSize(wxCommandEvent &WXUNUSED(event)) {
605 setZoom(MIN_ZOOM);
606}
607
Nathan E. Egge625bc262017-02-22 10:42:54 -0500608void AnalyzerFrame::onToggleViewMenuCheckBox(wxCommandEvent &event) { // NOLINT
609 GetMenuBar()->Check(event.GetId(), event.IsChecked());
610 updateViewMenu();
611}
612
613void 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
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400624void AnalyzerFrame::onNextFrame(wxCommandEvent &WXUNUSED(event)) {
625 panel->nextFrame();
626 panel->Refresh(false);
627}
628
629void AnalyzerFrame::onGotoFrame(wxCommandEvent &WXUNUSED(event)) {}
630
631void AnalyzerFrame::onRestart(wxCommandEvent &WXUNUSED(event)) {}
632
633void 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
644bool 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
655bool 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
Nathan E. Egge625bc262017-02-22 10:42:54 -0500668void 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
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400677class 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
686static 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
696void Analyzer::OnInitCmdLine(wxCmdLineParser &parser) { // NOLINT
697 parser.SetDesc(CMD_LINE_DESC);
698 parser.SetSwitchChars(_("-"));
699}
700
701bool Analyzer::OnCmdLineParsed(wxCmdLineParser &parser) { // NOLINT
702 bool bit_accounting = parser.Found(_("a"));
703 if (bit_accounting && !CONFIG_ACCOUNTING) {
704 fprintf(stderr,
Johanne07a6752018-01-10 12:47:44 -0800705 "Bit accounting support not found. "
706 "Recompile with:\n./cmake -DCONFIG_ACCOUNTING=1\n");
Nathan E. Eggef4fa01e2016-10-11 14:20:20 -0400707 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
717void usage_exit(void) {
718 fprintf(stderr, "uhh\n");
719 exit(EXIT_FAILURE);
720}
721
722IMPLEMENT_APP(Analyzer)