12 #include <wx/aboutdlg.h>
13 #include <wx/cmdline.h>
14 #include <wx/dcbuffer.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"
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))
29 OD_LUMA_MASK = 1 << 0,
32 OD_ALL_MASK = OD_LUMA_MASK | OD_CB_MASK | OD_CR_MASK
40 AvxVideoReader *reader;
41 const AvxVideoInfo *info;
44 insp_frame_data frame_data;
58 bool open(
const wxString &path);
62 int getWidthPadding()
const;
63 int getHeightPadding()
const;
66 int getHeight()
const;
69 bool setInspectionCallback();
71 static void inspect(
void *decoder,
void *data);
74 AV1Decoder::AV1Decoder()
75 : reader(NULL), info(NULL), decoder(NULL), show_padding(false), image(NULL),
78 AV1Decoder::~AV1Decoder() {}
80 void AV1Decoder::togglePadding() { show_padding = !show_padding; }
82 bool AV1Decoder::open(
const wxString &path) {
83 reader = aom_video_reader_open(path.mb_str());
85 fprintf(stderr,
"Failed to open %s for reading.", path.mb_str().data());
89 info = aom_video_reader_get_info(reader);
90 decoder = get_aom_decoder_by_fourcc(info->codec_fourcc);
92 fprintf(stderr,
"Unknown input codec.");
97 fprintf(stderr,
"Failed to initialize decoder.");
100 ifd_init(&frame_data, info->frame_width, info->frame_height);
101 setInspectionCallback();
105 void AV1Decoder::close() {}
107 bool AV1Decoder::step() {
108 if (aom_video_reader_read_frame(reader)) {
110 const unsigned char *frame_data;
111 frame_data = aom_video_reader_get_frame(reader, &frame_size);
113 fprintf(stderr,
"Failed to decode frame.");
128 int AV1Decoder::getWidth()
const {
129 return info->frame_width + 2 * getWidthPadding();
132 int AV1Decoder::getWidthPadding()
const {
133 return show_padding ? AOMMAX(info->frame_width + 16,
134 ALIGN_POWER_OF_TWO(info->frame_width, 6)) -
139 int AV1Decoder::getHeight()
const {
140 return info->frame_height + 2 * getHeightPadding();
143 int AV1Decoder::getHeightPadding()
const {
144 return show_padding ? AOMMAX(info->frame_height + 16,
145 ALIGN_POWER_OF_TWO(info->frame_height, 6)) -
150 bool AV1Decoder::getAccountingStruct(
Accounting **accounting) {
155 bool AV1Decoder::setInspectionCallback() {
163 void AV1Decoder::inspect(
void *pbi,
void *data) {
164 AV1Decoder *decoder = (AV1Decoder *)data;
165 ifd_inspect(&decoder->frame_data, pbi, 0);
171 class AnalyzerPanel :
public wxPanel {
172 DECLARE_EVENT_TABLE()
179 unsigned char *pixels;
181 const
bool bit_accounting;
187 int getDisplayWidth() const;
188 int getDisplayHeight() const;
190 bool updateDisplaySize();
192 void computeBitsPerPixel();
195 AnalyzerPanel(wxWindow *parent, const wxString &path,
196 const
bool bit_accounting);
199 bool open(const wxString &path);
202 void togglePadding();
207 bool setZoom(
int zoom);
209 void setShowPlane(
bool show_plane,
int mask);
211 void onPaint(wxPaintEvent &event);
214 BEGIN_EVENT_TABLE(AnalyzerPanel, wxPanel)
215 EVT_PAINT(AnalyzerPanel::onPaint)
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) {}
223 AnalyzerPanel::~AnalyzerPanel() { close(); }
225 void AnalyzerPanel::setShowPlane(
bool show_plane,
int mask) {
233 void AnalyzerPanel::render() {
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++) {
270 yval = *(y16 - y_width_padding);
271 cbval = *(cb16 - cb_width_padding);
272 crval = *(cr16 - cr_width_padding);
274 yval = *(y - y_width_padding);
275 cbval = *(cb - cb_width_padding);
276 crval = *(cr - cr_width_padding);
279 if (pmask & OD_LUMA_MASK) {
284 cbval = ((pmask & OD_CB_MASK) >> 1) * (cbval - 128);
285 crval = ((pmask & OD_CR_MASK) >> 2) * (crval - 128);
289 (int32_t)OD_DIV_ROUND(
290 2916394880000LL * yval + 4490222169144LL * crval, 9745792000LL),
293 (int32_t)OD_DIV_ROUND(2916394880000LL * yval -
294 534117096223LL * cbval -
295 1334761232047LL * crval,
300 (int32_t)OD_DIV_ROUND(
301 2916394880000LL * yval + 5290866304968LL * cbval, 9745792000LL),
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);
330 cb_row16 += dc & cb_stride;
331 cr_row16 += dc & cr_stride;
334 cb_row += dc & cb_stride;
335 cr_row += dc & cr_stride;
337 p_row += zoom * p_stride;
341 void AnalyzerPanel::computeBitsPerPixel() {
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;
353 for (
int i = 0; i < acct->syms.num_syms; i++) {
355 s = &acct->syms.syms[i];
356 totals_q3[s->id] += s->bits;
357 sym_count[s->id] += s->samples;
359 printf(
"=== Frame: %-3i ===\n", decoder.frame - 1);
360 for (
int i = 0; i < acct->syms.dictionary.num_strs; 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]);
369 void AnalyzerPanel::togglePadding() {
370 decoder.togglePadding();
374 bool AnalyzerPanel::nextFrame() {
375 if (decoder.step()) {
382 void AnalyzerPanel::refresh() {
383 if (bit_accounting) {
384 computeBitsPerPixel();
389 int AnalyzerPanel::getDisplayWidth()
const {
return zoom * decoder.getWidth(); }
391 int AnalyzerPanel::getDisplayHeight()
const {
392 return zoom * decoder.getHeight();
395 bool AnalyzerPanel::updateDisplaySize() {
396 unsigned char *p = (
unsigned char *)malloc(
397 sizeof(*p) * 3 * getDisplayWidth() * getDisplayHeight());
403 SetSize(getDisplayWidth(), getDisplayHeight());
407 bool AnalyzerPanel::open(
const wxString &path) {
408 if (!decoder.open(path)) {
411 if (!setZoom(MIN_ZOOM)) {
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");
431 void AnalyzerPanel::close() {
439 int AnalyzerPanel::getZoom()
const {
return zoom; }
441 bool AnalyzerPanel::setZoom(
int z) {
442 if (z <= MAX_ZOOM && z >= MIN_ZOOM && zoom != z) {
445 if (!updateDisplaySize()) {
454 void AnalyzerPanel::onPaint(wxPaintEvent &) {
455 wxBitmap bmp(wxImage(getDisplayWidth(), getDisplayHeight(), pixels,
true));
456 wxBufferedPaintDC dc(
this, bmp);
459 class AnalyzerFrame :
public wxFrame {
460 DECLARE_EVENT_TABLE()
463 AnalyzerPanel *panel;
464 const
bool bit_accounting;
468 wxMenu *playbackMenu;
471 AnalyzerFrame(const
bool bit_accounting);
473 void onOpen(wxCommandEvent &event);
474 void onClose(wxCommandEvent &event);
475 void onQuit(wxCommandEvent &event);
477 void onTogglePadding(wxCommandEvent &event);
478 void onZoomIn(wxCommandEvent &event);
479 void onZoomOut(wxCommandEvent &event);
480 void onActualSize(wxCommandEvent &event);
482 void onToggleViewMenuCheckBox(wxCommandEvent &event);
483 void onResetAndToggleViewMenuCheckBox(wxCommandEvent &event);
485 void onNextFrame(wxCommandEvent &event);
486 void onGotoFrame(wxCommandEvent &event);
487 void onRestart(wxCommandEvent &event);
489 void onAbout(wxCommandEvent &event);
491 bool open(const wxString &path);
492 bool setZoom(
int zoom);
493 void updateViewMenu();
497 wxID_NEXT_FRAME = 6000,
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)
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();
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"));
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);
543 viewMenu =
new wxMenu();
544 +viewMenu->Append(wxID_PADDING, _(
"Toggle padding\tCtrl-p"),
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"),
553 viewMenu->AppendCheckItem(wxID_SHOW_U, _(
"&U plane\tCtrl-U"),
555 viewMenu->AppendCheckItem(wxID_SHOW_V, _(
"&V plane\tCtrl-V"),
557 mb->Append(viewMenu, _(
"&View"));
559 playbackMenu =
new wxMenu();
560 playbackMenu->Append(wxID_NEXT_FRAME, _(
"Next frame\tCtrl-."),
561 _(
"Go to next frame"));
566 mb->Append(playbackMenu, _(
"&Playback"));
568 wxMenu *helpMenu =
new wxMenu();
569 helpMenu->Append(wxID_ABOUT, _(
"&About...\tF1"), _(
"Show about dialog"));
570 mb->Append(helpMenu, _(
"&Help"));
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());
586 void AnalyzerFrame::onClose(wxCommandEvent &WXUNUSED(event)) {}
588 void AnalyzerFrame::onQuit(wxCommandEvent &WXUNUSED(event)) { Close(
true); }
590 void AnalyzerFrame::onTogglePadding(wxCommandEvent &WXUNUSED(event)) {
591 panel->togglePadding();
592 SetClientSize(panel->GetSize());
597 void AnalyzerFrame::onZoomIn(wxCommandEvent &WXUNUSED(event)) {
598 setZoom(panel->getZoom() + 1);
601 void AnalyzerFrame::onZoomOut(wxCommandEvent &WXUNUSED(event)) {
602 setZoom(panel->getZoom() - 1);
605 void AnalyzerFrame::onActualSize(wxCommandEvent &WXUNUSED(event)) {
609 void AnalyzerFrame::onToggleViewMenuCheckBox(wxCommandEvent &event) {
610 GetMenuBar()->Check(event.GetId(),
event.IsChecked());
614 void AnalyzerFrame::onResetAndToggleViewMenuCheckBox(
615 wxCommandEvent &event) {
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);
622 onToggleViewMenuCheckBox(event);
625 void AnalyzerFrame::onNextFrame(wxCommandEvent &WXUNUSED(event)) {
627 panel->Refresh(
false);
630 void AnalyzerFrame::onGotoFrame(wxCommandEvent &WXUNUSED(event)) {}
632 void AnalyzerFrame::onRestart(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"));
639 _(
"This program implements a bitstream analyzer for AV1"));
641 wxT(
"(C) 2017 Alliance for Open Media <negge@mozilla.com>"));
645 bool AnalyzerFrame::open(
const wxString &path) {
646 panel =
new AnalyzerPanel(
this, path, bit_accounting);
647 if (panel->open(path)) {
648 SetClientSize(panel->GetSize());
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());
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());
675 panel->Refresh(
false);
678 class Analyzer :
public wxApp {
680 AnalyzerFrame *frame;
683 void OnInitCmdLine(wxCmdLineParser &parser);
684 bool OnCmdLineParsed(wxCmdLineParser &parser);
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 },
697 void Analyzer::OnInitCmdLine(wxCmdLineParser &parser) {
698 parser.SetDesc(CMD_LINE_DESC);
699 parser.SetSwitchChars(_(
"-"));
702 bool Analyzer::OnCmdLineParsed(wxCmdLineParser &parser) {
703 bool bit_accounting = parser.Found(_(
"a"));
704 if (bit_accounting && !CONFIG_ACCOUNTING) {
706 "Bit accounting support not found. "
707 "Recompile with:\n./cmake -DCONFIG_ACCOUNTING=1\n");
710 frame =
new AnalyzerFrame(parser.Found(_(
"a")));
712 if (parser.GetParamCount() > 0) {
713 return frame->open(parser.GetParam(0));
718 void usage_exit(
void) {
719 fprintf(stderr,
"uhh\n");
723 IMPLEMENT_APP(Analyzer)
Operation completed without error.
Definition: aom_codec.h:157
aom_inspect_cb inspect_cb
Definition: aomdx.h:66
aom_image_t * aom_codec_get_frame(aom_codec_ctx_t *ctx, aom_codec_iter_t *iter)
Decoded frames iterator.
unsigned char * planes[3]
Definition: aom_image.h:213
Codec context structure.
Definition: aom_codec.h:298
#define AOM_IMG_FMT_HIGHBITDEPTH
Definition: aom_image.h:38
unsigned int y_chroma_shift
Definition: aom_image.h:204
Describes the decoder algorithm interface to applications.
Image Descriptor.
Definition: aom_image.h:180
aom_codec_err_t aom_codec_decode(aom_codec_ctx_t *ctx, const uint8_t *data, size_t data_sz, void *user_priv)
Decode data.
const struct aom_codec_iface aom_codec_iface_t
Codec interface structure.
Definition: aom_codec.h:254
#define aom_codec_dec_init(ctx, iface, cfg, flags)
Convenience macro for aom_codec_dec_init_ver()
Definition: aom_decoder.h:129
const char * aom_codec_iface_name(aom_codec_iface_t *iface)
Return the name for a given interface.
Codec control function to set an aom_inspect_cb callback that is invoked each time a frame is decoded...
Definition: aomdx.h:379
void * inspect_ctx
Definition: aomdx.h:69
Structure to hold inspection callback and context.
Definition: aomdx.h:64
const void * aom_codec_iter_t
Iterator.
Definition: aom_codec.h:288
unsigned int x_chroma_shift
Definition: aom_image.h:203
Provides definitions for using AOM or AV1 within the aom Decoder interface.
int stride[3]
Definition: aom_image.h:214
aom_codec_err_t aom_codec_control(aom_codec_ctx_t *ctx, int ctrl_id,...)
Algorithm Control.
aom_img_fmt_t fmt
Definition: aom_image.h:181
struct Accounting Accounting
Definition: aomdx.h:50
Codec control function to retrieve a pointer to the Accounting struct, takes Accounting** as paramete...
Definition: aomdx.h:287