mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 00:38:39 +09:00
Import aom library
This is the reference implementation for the Alliance for Open Media's av1 video code. The commit used was 4d668d7feb1f8abd809d1bca0418570a7f142a36.
This commit is contained in:
parent
eb8cd130a8
commit
edc8d83307
989 changed files with 470949 additions and 0 deletions
695
third_party/aom/examples/analyzer.cc
vendored
Normal file
695
third_party/aom/examples/analyzer.cc
vendored
Normal file
|
|
@ -0,0 +1,695 @@
|
|||
/*
|
||||
* Copyright (c) 2017, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
#include <wx/wx.h>
|
||||
#include <wx/aboutdlg.h>
|
||||
#include <wx/cmdline.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include "./tools_common.h"
|
||||
#include "./video_reader.h"
|
||||
#include "aom/aom_decoder.h"
|
||||
#include "aom/aomdx.h"
|
||||
#include "av1/decoder/accounting.h"
|
||||
#include "av1/common/onyxc_int.h"
|
||||
#include "av1/decoder/inspection.h"
|
||||
|
||||
#define OD_SIGNMASK(a) (-((a) < 0))
|
||||
#define OD_FLIPSIGNI(a, b) (((a) + OD_SIGNMASK(b)) ^ OD_SIGNMASK(b))
|
||||
#define OD_DIV_ROUND(x, y) (((x) + OD_FLIPSIGNI((y) >> 1, x)) / (y))
|
||||
|
||||
enum {
|
||||
OD_LUMA_MASK = 1 << 0,
|
||||
OD_CB_MASK = 1 << 1,
|
||||
OD_CR_MASK = 1 << 2,
|
||||
OD_ALL_MASK = OD_LUMA_MASK | OD_CB_MASK | OD_CR_MASK
|
||||
};
|
||||
|
||||
class AV1Decoder {
|
||||
private:
|
||||
FILE *input;
|
||||
wxString path;
|
||||
|
||||
AvxVideoReader *reader;
|
||||
const AvxVideoInfo *info;
|
||||
const AvxInterface *decoder;
|
||||
|
||||
insp_frame_data frame_data;
|
||||
|
||||
aom_codec_ctx_t codec;
|
||||
bool show_padding;
|
||||
|
||||
public:
|
||||
aom_image_t *image;
|
||||
int frame;
|
||||
|
||||
int plane_mask;
|
||||
|
||||
AV1Decoder();
|
||||
~AV1Decoder();
|
||||
|
||||
bool open(const wxString &path);
|
||||
void close();
|
||||
bool step();
|
||||
|
||||
int getWidthPadding() const;
|
||||
int getHeightPadding() const;
|
||||
void togglePadding();
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
|
||||
bool getAccountingStruct(Accounting **acct);
|
||||
bool setInspectionCallback();
|
||||
|
||||
static void inspect(void *decoder, void *data);
|
||||
};
|
||||
|
||||
AV1Decoder::AV1Decoder()
|
||||
: reader(NULL), info(NULL), decoder(NULL), show_padding(false), image(NULL),
|
||||
frame(0) {}
|
||||
|
||||
AV1Decoder::~AV1Decoder() {}
|
||||
|
||||
void AV1Decoder::togglePadding() { show_padding = !show_padding; }
|
||||
|
||||
bool AV1Decoder::open(const wxString &path) {
|
||||
reader = aom_video_reader_open(path.mb_str());
|
||||
if (!reader) {
|
||||
fprintf(stderr, "Failed to open %s for reading.", path.mb_str().data());
|
||||
return false;
|
||||
}
|
||||
this->path = path;
|
||||
info = aom_video_reader_get_info(reader);
|
||||
decoder = get_aom_decoder_by_fourcc(info->codec_fourcc);
|
||||
if (!decoder) {
|
||||
fprintf(stderr, "Unknown input codec.");
|
||||
return false;
|
||||
}
|
||||
printf("Using %s\n", aom_codec_iface_name(decoder->codec_interface()));
|
||||
if (aom_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0)) {
|
||||
fprintf(stderr, "Failed to initialize decoder.");
|
||||
return false;
|
||||
}
|
||||
ifd_init(&frame_data, info->frame_width, info->frame_height);
|
||||
setInspectionCallback();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AV1Decoder::close() {}
|
||||
|
||||
bool AV1Decoder::step() {
|
||||
if (aom_video_reader_read_frame(reader)) {
|
||||
size_t frame_size;
|
||||
const unsigned char *frame_data;
|
||||
frame_data = aom_video_reader_get_frame(reader, &frame_size);
|
||||
if (aom_codec_decode(&codec, frame_data, frame_size, NULL, 0)) {
|
||||
fprintf(stderr, "Failed to decode frame.");
|
||||
return false;
|
||||
} else {
|
||||
aom_codec_iter_t iter = NULL;
|
||||
image = aom_codec_get_frame(&codec, &iter);
|
||||
if (image != NULL) {
|
||||
frame++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int AV1Decoder::getWidth() const {
|
||||
return info->frame_width + 2 * getWidthPadding();
|
||||
}
|
||||
|
||||
int AV1Decoder::getWidthPadding() const {
|
||||
return show_padding
|
||||
? AOMMAX(info->frame_width + 16,
|
||||
ALIGN_POWER_OF_TWO(info->frame_width, 6)) -
|
||||
info->frame_width
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AV1Decoder::getHeight() const {
|
||||
return info->frame_height + 2 * getHeightPadding();
|
||||
}
|
||||
|
||||
int AV1Decoder::getHeightPadding() const {
|
||||
return show_padding
|
||||
? AOMMAX(info->frame_height + 16,
|
||||
ALIGN_POWER_OF_TWO(info->frame_height, 6)) -
|
||||
info->frame_height
|
||||
: 0;
|
||||
}
|
||||
|
||||
bool AV1Decoder::getAccountingStruct(Accounting **accounting) {
|
||||
return aom_codec_control(&codec, AV1_GET_ACCOUNTING, accounting) ==
|
||||
AOM_CODEC_OK;
|
||||
}
|
||||
|
||||
bool AV1Decoder::setInspectionCallback() {
|
||||
aom_inspect_init ii;
|
||||
ii.inspect_cb = AV1Decoder::inspect;
|
||||
ii.inspect_ctx = (void *)this;
|
||||
return aom_codec_control(&codec, AV1_SET_INSPECTION_CALLBACK, &ii) ==
|
||||
AOM_CODEC_OK;
|
||||
}
|
||||
|
||||
void AV1Decoder::inspect(void *pbi, void *data) {
|
||||
AV1Decoder *decoder = (AV1Decoder *)data;
|
||||
ifd_inspect(&decoder->frame_data, pbi);
|
||||
}
|
||||
|
||||
#define MIN_ZOOM (1)
|
||||
#define MAX_ZOOM (4)
|
||||
|
||||
class AnalyzerPanel : public wxPanel {
|
||||
DECLARE_EVENT_TABLE()
|
||||
|
||||
private:
|
||||
AV1Decoder decoder;
|
||||
const wxString path;
|
||||
|
||||
int zoom;
|
||||
unsigned char *pixels;
|
||||
|
||||
const bool bit_accounting;
|
||||
double *bpp_q3;
|
||||
|
||||
int plane_mask;
|
||||
|
||||
// The display size is the decode size, scaled by the zoom.
|
||||
int getDisplayWidth() const;
|
||||
int getDisplayHeight() const;
|
||||
|
||||
bool updateDisplaySize();
|
||||
|
||||
void computeBitsPerPixel();
|
||||
|
||||
public:
|
||||
AnalyzerPanel(wxWindow *parent, const wxString &path,
|
||||
const bool bit_accounting);
|
||||
~AnalyzerPanel();
|
||||
|
||||
bool open(const wxString &path);
|
||||
void close();
|
||||
void render();
|
||||
void togglePadding();
|
||||
bool nextFrame();
|
||||
void refresh();
|
||||
|
||||
int getZoom() const;
|
||||
bool setZoom(int zoom);
|
||||
|
||||
void setShowPlane(bool show_plane, int mask);
|
||||
|
||||
void onPaint(wxPaintEvent &event); // NOLINT
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(AnalyzerPanel, wxPanel)
|
||||
EVT_PAINT(AnalyzerPanel::onPaint)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
AnalyzerPanel::AnalyzerPanel(wxWindow *parent, const wxString &path,
|
||||
const bool bit_accounting)
|
||||
: wxPanel(parent), path(path), zoom(0), pixels(NULL),
|
||||
bit_accounting(bit_accounting), bpp_q3(NULL), plane_mask(OD_ALL_MASK) {}
|
||||
|
||||
AnalyzerPanel::~AnalyzerPanel() { close(); }
|
||||
|
||||
void AnalyzerPanel::setShowPlane(bool show_plane, int mask) {
|
||||
if (show_plane) {
|
||||
plane_mask |= mask;
|
||||
} else {
|
||||
plane_mask &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
void AnalyzerPanel::render() {
|
||||
aom_image_t *img = decoder.image;
|
||||
int y_stride = img->stride[0];
|
||||
int cb_stride = img->stride[1];
|
||||
int cr_stride = img->stride[2];
|
||||
int p_stride = 3 * getDisplayWidth();
|
||||
unsigned char *y_row = img->planes[0];
|
||||
unsigned char *cb_row = img->planes[1];
|
||||
unsigned char *cr_row = img->planes[2];
|
||||
unsigned char *p_row = pixels;
|
||||
int y_width_padding = decoder.getWidthPadding();
|
||||
int cb_width_padding = y_width_padding >> 1;
|
||||
int cr_width_padding = y_width_padding >> 1;
|
||||
int y_height_padding = decoder.getHeightPadding();
|
||||
int cb_height_padding = y_height_padding >> 1;
|
||||
int cr_height_padding = y_height_padding >> 1;
|
||||
for (int j = 0; j < decoder.getHeight(); j++) {
|
||||
unsigned char *y = y_row - y_stride * y_height_padding;
|
||||
unsigned char *cb = cb_row - cb_stride * cb_height_padding;
|
||||
unsigned char *cr = cr_row - cr_stride * cr_height_padding;
|
||||
unsigned char *p = p_row;
|
||||
for (int i = 0; i < decoder.getWidth(); i++) {
|
||||
int64_t yval;
|
||||
int64_t cbval;
|
||||
int64_t crval;
|
||||
int pmask;
|
||||
unsigned rval;
|
||||
unsigned gval;
|
||||
unsigned bval;
|
||||
yval = *(y - y_width_padding);
|
||||
cbval = *(cb - cb_width_padding);
|
||||
crval = *(cr - cr_width_padding);
|
||||
pmask = plane_mask;
|
||||
if (pmask & OD_LUMA_MASK) {
|
||||
yval -= 16;
|
||||
} else {
|
||||
yval = 128;
|
||||
}
|
||||
cbval = ((pmask & OD_CB_MASK) >> 1) * (cbval - 128);
|
||||
crval = ((pmask & OD_CR_MASK) >> 2) * (crval - 128);
|
||||
/*This is intentionally slow and very accurate.*/
|
||||
rval = OD_CLAMPI(0, (int32_t)OD_DIV_ROUND(
|
||||
2916394880000LL * yval + 4490222169144LL * crval,
|
||||
9745792000LL),
|
||||
65535);
|
||||
gval = OD_CLAMPI(0, (int32_t)OD_DIV_ROUND(2916394880000LL * yval -
|
||||
534117096223LL * cbval -
|
||||
1334761232047LL * crval,
|
||||
9745792000LL),
|
||||
65535);
|
||||
bval = OD_CLAMPI(0, (int32_t)OD_DIV_ROUND(
|
||||
2916394880000LL * yval + 5290866304968LL * cbval,
|
||||
9745792000LL),
|
||||
65535);
|
||||
unsigned char *px_row = p;
|
||||
for (int v = 0; v < zoom; v++) {
|
||||
unsigned char *px = px_row;
|
||||
for (int u = 0; u < zoom; u++) {
|
||||
*(px + 0) = (unsigned char)(rval >> 8);
|
||||
*(px + 1) = (unsigned char)(gval >> 8);
|
||||
*(px + 2) = (unsigned char)(bval >> 8);
|
||||
px += 3;
|
||||
}
|
||||
px_row += p_stride;
|
||||
}
|
||||
int dc = ((y - y_row) & 1) | (1 - img->x_chroma_shift);
|
||||
y++;
|
||||
cb += dc;
|
||||
cr += dc;
|
||||
p += zoom * 3;
|
||||
}
|
||||
int dc = -((j & 1) | (1 - img->y_chroma_shift));
|
||||
y_row += y_stride;
|
||||
cb_row += dc & cb_stride;
|
||||
cr_row += dc & cr_stride;
|
||||
p_row += zoom * p_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void AnalyzerPanel::computeBitsPerPixel() {
|
||||
Accounting *acct;
|
||||
double bpp_total;
|
||||
int totals_q3[MAX_SYMBOL_TYPES] = { 0 };
|
||||
int sym_count[MAX_SYMBOL_TYPES] = { 0 };
|
||||
decoder.getAccountingStruct(&acct);
|
||||
for (int j = 0; j < decoder.getHeight(); j++) {
|
||||
for (int i = 0; i < decoder.getWidth(); i++) {
|
||||
bpp_q3[j * decoder.getWidth() + i] = 0.0;
|
||||
}
|
||||
}
|
||||
bpp_total = 0;
|
||||
for (int i = 0; i < acct->syms.num_syms; i++) {
|
||||
AccountingSymbol *s;
|
||||
s = &acct->syms.syms[i];
|
||||
totals_q3[s->id] += s->bits;
|
||||
sym_count[s->id] += s->samples;
|
||||
}
|
||||
printf("=== Frame: %-3i ===\n", decoder.frame - 1);
|
||||
for (int i = 0; i < acct->syms.dictionary.num_strs; i++) {
|
||||
if (totals_q3[i]) {
|
||||
printf("%30s = %10.3f (%f bit/symbol)\n", acct->syms.dictionary.strs[i],
|
||||
(float)totals_q3[i] / 8, (float)totals_q3[i] / 8 / sym_count[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void AnalyzerPanel::togglePadding() {
|
||||
decoder.togglePadding();
|
||||
updateDisplaySize();
|
||||
}
|
||||
|
||||
bool AnalyzerPanel::nextFrame() {
|
||||
if (decoder.step()) {
|
||||
refresh();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnalyzerPanel::refresh() {
|
||||
if (bit_accounting) {
|
||||
computeBitsPerPixel();
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
int AnalyzerPanel::getDisplayWidth() const { return zoom * decoder.getWidth(); }
|
||||
|
||||
int AnalyzerPanel::getDisplayHeight() const {
|
||||
return zoom * decoder.getHeight();
|
||||
}
|
||||
|
||||
bool AnalyzerPanel::updateDisplaySize() {
|
||||
unsigned char *p = (unsigned char *)malloc(
|
||||
sizeof(*p) * 3 * getDisplayWidth() * getDisplayHeight());
|
||||
if (p == NULL) {
|
||||
return false;
|
||||
}
|
||||
free(pixels);
|
||||
pixels = p;
|
||||
SetSize(getDisplayWidth(), getDisplayHeight());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AnalyzerPanel::open(const wxString &path) {
|
||||
if (!decoder.open(path)) {
|
||||
return false;
|
||||
}
|
||||
if (!setZoom(MIN_ZOOM)) {
|
||||
return false;
|
||||
}
|
||||
if (bit_accounting) {
|
||||
bpp_q3 = (double *)malloc(sizeof(*bpp_q3) * decoder.getWidth() *
|
||||
decoder.getHeight());
|
||||
if (bpp_q3 == NULL) {
|
||||
fprintf(stderr, "Could not allocate memory for bit accounting\n");
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!nextFrame()) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
SetFocus();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnalyzerPanel::close() {
|
||||
decoder.close();
|
||||
free(pixels);
|
||||
pixels = NULL;
|
||||
free(bpp_q3);
|
||||
bpp_q3 = NULL;
|
||||
}
|
||||
|
||||
int AnalyzerPanel::getZoom() const { return zoom; }
|
||||
|
||||
bool AnalyzerPanel::setZoom(int z) {
|
||||
if (z <= MAX_ZOOM && z >= MIN_ZOOM && zoom != z) {
|
||||
int old_zoom = zoom;
|
||||
zoom = z;
|
||||
if (!updateDisplaySize()) {
|
||||
zoom = old_zoom;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnalyzerPanel::onPaint(wxPaintEvent &) {
|
||||
wxBitmap bmp(wxImage(getDisplayWidth(), getDisplayHeight(), pixels, true));
|
||||
wxBufferedPaintDC dc(this, bmp);
|
||||
}
|
||||
|
||||
class AnalyzerFrame : public wxFrame {
|
||||
DECLARE_EVENT_TABLE()
|
||||
|
||||
private:
|
||||
AnalyzerPanel *panel;
|
||||
const bool bit_accounting;
|
||||
|
||||
wxMenu *fileMenu;
|
||||
wxMenu *viewMenu;
|
||||
wxMenu *playbackMenu;
|
||||
|
||||
public:
|
||||
AnalyzerFrame(const bool bit_accounting); // NOLINT
|
||||
|
||||
void onOpen(wxCommandEvent &event); // NOLINT
|
||||
void onClose(wxCommandEvent &event); // NOLINT
|
||||
void onQuit(wxCommandEvent &event); // NOLINT
|
||||
|
||||
void onTogglePadding(wxCommandEvent &event); // NOLINT
|
||||
void onZoomIn(wxCommandEvent &event); // NOLINT
|
||||
void onZoomOut(wxCommandEvent &event); // NOLINT
|
||||
void onActualSize(wxCommandEvent &event); // NOLINT
|
||||
|
||||
void onToggleViewMenuCheckBox(wxCommandEvent &event); // NOLINT
|
||||
void onResetAndToggleViewMenuCheckBox(wxCommandEvent &event); // NOLINT
|
||||
|
||||
void onNextFrame(wxCommandEvent &event); // NOLINT
|
||||
void onGotoFrame(wxCommandEvent &event); // NOLINT
|
||||
void onRestart(wxCommandEvent &event); // NOLINT
|
||||
|
||||
void onAbout(wxCommandEvent &event); // NOLINT
|
||||
|
||||
bool open(const wxString &path);
|
||||
bool setZoom(int zoom);
|
||||
void updateViewMenu();
|
||||
};
|
||||
|
||||
enum {
|
||||
wxID_NEXT_FRAME = 6000,
|
||||
wxID_SHOW_Y,
|
||||
wxID_SHOW_U,
|
||||
wxID_SHOW_V,
|
||||
wxID_GOTO_FRAME,
|
||||
wxID_RESTART,
|
||||
wxID_ACTUAL_SIZE,
|
||||
wxID_PADDING
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(AnalyzerFrame, wxFrame)
|
||||
EVT_MENU(wxID_OPEN, AnalyzerFrame::onOpen)
|
||||
EVT_MENU(wxID_CLOSE, AnalyzerFrame::onClose)
|
||||
EVT_MENU(wxID_EXIT, AnalyzerFrame::onQuit)
|
||||
EVT_MENU(wxID_PADDING, AnalyzerFrame::onTogglePadding)
|
||||
EVT_MENU(wxID_ZOOM_IN, AnalyzerFrame::onZoomIn)
|
||||
EVT_MENU(wxID_ZOOM_OUT, AnalyzerFrame::onZoomOut)
|
||||
EVT_MENU(wxID_ACTUAL_SIZE, AnalyzerFrame::onActualSize)
|
||||
EVT_MENU(wxID_SHOW_Y, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
|
||||
EVT_MENU(wxID_SHOW_U, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
|
||||
EVT_MENU(wxID_SHOW_V, AnalyzerFrame::onResetAndToggleViewMenuCheckBox)
|
||||
EVT_MENU(wxID_NEXT_FRAME, AnalyzerFrame::onNextFrame)
|
||||
EVT_MENU(wxID_GOTO_FRAME, AnalyzerFrame::onGotoFrame)
|
||||
EVT_MENU(wxID_RESTART, AnalyzerFrame::onRestart)
|
||||
EVT_MENU(wxID_ABOUT, AnalyzerFrame::onAbout)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
AnalyzerFrame::AnalyzerFrame(const bool bit_accounting)
|
||||
: wxFrame(NULL, wxID_ANY, _("AV1 Stream Analyzer"), wxDefaultPosition,
|
||||
wxDefaultSize, wxDEFAULT_FRAME_STYLE),
|
||||
panel(NULL), bit_accounting(bit_accounting) {
|
||||
wxMenuBar *mb = new wxMenuBar();
|
||||
|
||||
fileMenu = new wxMenu();
|
||||
fileMenu->Append(wxID_OPEN, _("&Open...\tCtrl-O"), _("Open daala file"));
|
||||
fileMenu->Append(wxID_CLOSE, _("&Close\tCtrl-W"), _("Close daala file"));
|
||||
fileMenu->Enable(wxID_CLOSE, false);
|
||||
fileMenu->Append(wxID_EXIT, _("E&xit\tCtrl-Q"), _("Quit this program"));
|
||||
mb->Append(fileMenu, _("&File"));
|
||||
|
||||
wxAcceleratorEntry entries[2];
|
||||
entries[0].Set(wxACCEL_CTRL, (int)'=', wxID_ZOOM_IN);
|
||||
entries[1].Set(wxACCEL_CTRL | wxACCEL_SHIFT, (int)'-', wxID_ZOOM_OUT);
|
||||
wxAcceleratorTable accel(2, entries);
|
||||
this->SetAcceleratorTable(accel);
|
||||
|
||||
viewMenu = new wxMenu();
|
||||
+viewMenu->Append(wxID_PADDING, _("Toggle padding\tCtrl-p"),
|
||||
_("Show padding"));
|
||||
viewMenu->Append(wxID_ZOOM_IN, _("Zoom-In\tCtrl-+"), _("Double image size"));
|
||||
viewMenu->Append(wxID_ZOOM_OUT, _("Zoom-Out\tCtrl--"), _("Half image size"));
|
||||
viewMenu->Append(wxID_ACTUAL_SIZE, _("Actual size\tCtrl-0"),
|
||||
_("Actual size of the frame"));
|
||||
viewMenu->AppendSeparator();
|
||||
viewMenu->AppendCheckItem(wxID_SHOW_Y, _("&Y plane\tCtrl-Y"),
|
||||
_("Show Y plane"));
|
||||
viewMenu->AppendCheckItem(wxID_SHOW_U, _("&U plane\tCtrl-U"),
|
||||
_("Show U plane"));
|
||||
viewMenu->AppendCheckItem(wxID_SHOW_V, _("&V plane\tCtrl-V"),
|
||||
_("Show V plane"));
|
||||
mb->Append(viewMenu, _("&View"));
|
||||
|
||||
playbackMenu = new wxMenu();
|
||||
playbackMenu->Append(wxID_NEXT_FRAME, _("Next frame\tCtrl-."),
|
||||
_("Go to next frame"));
|
||||
/*playbackMenu->Append(wxID_RESTART, _("&Restart\tCtrl-R"),
|
||||
_("Set video to frame 0"));
|
||||
playbackMenu->Append(wxID_GOTO_FRAME, _("Jump to Frame\tCtrl-J"),
|
||||
_("Go to frame number"));*/
|
||||
mb->Append(playbackMenu, _("&Playback"));
|
||||
|
||||
wxMenu *helpMenu = new wxMenu();
|
||||
helpMenu->Append(wxID_ABOUT, _("&About...\tF1"), _("Show about dialog"));
|
||||
mb->Append(helpMenu, _("&Help"));
|
||||
|
||||
SetMenuBar(mb);
|
||||
|
||||
CreateStatusBar(1);
|
||||
}
|
||||
|
||||
void AnalyzerFrame::onOpen(wxCommandEvent &WXUNUSED(event)) {
|
||||
wxFileDialog openFileDialog(this, _("Open file"), wxEmptyString,
|
||||
wxEmptyString, _("AV1 files (*.ivf)|*.ivf"),
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (openFileDialog.ShowModal() != wxID_CANCEL) {
|
||||
open(openFileDialog.GetPath());
|
||||
}
|
||||
}
|
||||
|
||||
void AnalyzerFrame::onClose(wxCommandEvent &WXUNUSED(event)) {}
|
||||
|
||||
void AnalyzerFrame::onQuit(wxCommandEvent &WXUNUSED(event)) { Close(true); }
|
||||
|
||||
void AnalyzerFrame::onTogglePadding(wxCommandEvent &WXUNUSED(event)) {
|
||||
panel->togglePadding();
|
||||
SetClientSize(panel->GetSize());
|
||||
panel->render();
|
||||
panel->Refresh();
|
||||
}
|
||||
|
||||
void AnalyzerFrame::onZoomIn(wxCommandEvent &WXUNUSED(event)) {
|
||||
setZoom(panel->getZoom() + 1);
|
||||
}
|
||||
|
||||
void AnalyzerFrame::onZoomOut(wxCommandEvent &WXUNUSED(event)) {
|
||||
setZoom(panel->getZoom() - 1);
|
||||
}
|
||||
|
||||
void AnalyzerFrame::onActualSize(wxCommandEvent &WXUNUSED(event)) {
|
||||
setZoom(MIN_ZOOM);
|
||||
}
|
||||
|
||||
void AnalyzerFrame::onToggleViewMenuCheckBox(wxCommandEvent &event) { // NOLINT
|
||||
GetMenuBar()->Check(event.GetId(), event.IsChecked());
|
||||
updateViewMenu();
|
||||
}
|
||||
|
||||
void AnalyzerFrame::onResetAndToggleViewMenuCheckBox(
|
||||
wxCommandEvent &event) { // NOLINT
|
||||
int id = event.GetId();
|
||||
if (id != wxID_SHOW_Y && id != wxID_SHOW_U && id != wxID_SHOW_V) {
|
||||
GetMenuBar()->Check(wxID_SHOW_Y, true);
|
||||
GetMenuBar()->Check(wxID_SHOW_U, true);
|
||||
GetMenuBar()->Check(wxID_SHOW_V, true);
|
||||
}
|
||||
onToggleViewMenuCheckBox(event);
|
||||
}
|
||||
|
||||
void AnalyzerFrame::onNextFrame(wxCommandEvent &WXUNUSED(event)) {
|
||||
panel->nextFrame();
|
||||
panel->Refresh(false);
|
||||
}
|
||||
|
||||
void AnalyzerFrame::onGotoFrame(wxCommandEvent &WXUNUSED(event)) {}
|
||||
|
||||
void AnalyzerFrame::onRestart(wxCommandEvent &WXUNUSED(event)) {}
|
||||
|
||||
void AnalyzerFrame::onAbout(wxCommandEvent &WXUNUSED(event)) {
|
||||
wxAboutDialogInfo info;
|
||||
info.SetName(_("AV1 Bitstream Analyzer"));
|
||||
info.SetVersion(_("0.1-beta"));
|
||||
info.SetDescription(
|
||||
_("This program implements a bitstream analyzer for AV1"));
|
||||
info.SetCopyright(
|
||||
wxT("(C) 2017 Alliance for Open Media <negge@mozilla.com>"));
|
||||
wxAboutBox(info);
|
||||
}
|
||||
|
||||
bool AnalyzerFrame::open(const wxString &path) {
|
||||
panel = new AnalyzerPanel(this, path, bit_accounting);
|
||||
if (panel->open(path)) {
|
||||
SetClientSize(panel->GetSize());
|
||||
return true;
|
||||
} else {
|
||||
delete panel;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AnalyzerFrame::setZoom(int zoom) {
|
||||
if (panel->setZoom(zoom)) {
|
||||
GetMenuBar()->Enable(wxID_ACTUAL_SIZE, zoom != MIN_ZOOM);
|
||||
GetMenuBar()->Enable(wxID_ZOOM_IN, zoom != MAX_ZOOM);
|
||||
GetMenuBar()->Enable(wxID_ZOOM_OUT, zoom != MIN_ZOOM);
|
||||
SetClientSize(panel->GetSize());
|
||||
panel->render();
|
||||
panel->Refresh();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnalyzerFrame::updateViewMenu() {
|
||||
panel->setShowPlane(GetMenuBar()->IsChecked(wxID_SHOW_Y), OD_LUMA_MASK);
|
||||
panel->setShowPlane(GetMenuBar()->IsChecked(wxID_SHOW_U), OD_CB_MASK);
|
||||
panel->setShowPlane(GetMenuBar()->IsChecked(wxID_SHOW_V), OD_CR_MASK);
|
||||
SetClientSize(panel->GetSize());
|
||||
panel->render();
|
||||
panel->Refresh(false);
|
||||
}
|
||||
|
||||
class Analyzer : public wxApp {
|
||||
private:
|
||||
AnalyzerFrame *frame;
|
||||
|
||||
public:
|
||||
void OnInitCmdLine(wxCmdLineParser &parser); // NOLINT
|
||||
bool OnCmdLineParsed(wxCmdLineParser &parser); // NOLINT
|
||||
};
|
||||
|
||||
static const wxCmdLineEntryDesc CMD_LINE_DESC[] = {
|
||||
{ wxCMD_LINE_SWITCH, _("h"), _("help"), _("Display this help and exit."),
|
||||
wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
|
||||
{ wxCMD_LINE_SWITCH, _("a"), _("bit-accounting"), _("Enable bit accounting"),
|
||||
wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
|
||||
{ wxCMD_LINE_PARAM, NULL, NULL, _("input.ivf"), wxCMD_LINE_VAL_STRING,
|
||||
wxCMD_LINE_PARAM_OPTIONAL },
|
||||
{ wxCMD_LINE_NONE }
|
||||
};
|
||||
|
||||
void Analyzer::OnInitCmdLine(wxCmdLineParser &parser) { // NOLINT
|
||||
parser.SetDesc(CMD_LINE_DESC);
|
||||
parser.SetSwitchChars(_("-"));
|
||||
}
|
||||
|
||||
bool Analyzer::OnCmdLineParsed(wxCmdLineParser &parser) { // NOLINT
|
||||
bool bit_accounting = parser.Found(_("a"));
|
||||
if (bit_accounting && !CONFIG_ACCOUNTING) {
|
||||
fprintf(stderr,
|
||||
"Bit accounting support not found. "
|
||||
"Recompile with:\n./configure --enable-accounting\n");
|
||||
return false;
|
||||
}
|
||||
frame = new AnalyzerFrame(parser.Found(_("a")));
|
||||
frame->Show();
|
||||
if (parser.GetParamCount() > 0) {
|
||||
return frame->open(parser.GetParam(0));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "uhh\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
IMPLEMENT_APP(Analyzer)
|
||||
323
third_party/aom/examples/aom_cx_set_ref.c
vendored
Normal file
323
third_party/aom/examples/aom_cx_set_ref.c
vendored
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
// AV1 Set Reference Frame
|
||||
// ============================
|
||||
//
|
||||
// This is an example demonstrating how to overwrite the AV1 encoder's
|
||||
// internal reference frame. In the sample we set the last frame to the
|
||||
// current frame. This technique could be used to bounce between two cameras.
|
||||
//
|
||||
// The decoder would also have to set the reference frame to the same value
|
||||
// on the same frame, or the video will become corrupt. The 'test_decode'
|
||||
// variable is set to 1 in this example that tests if the encoder and decoder
|
||||
// results are matching.
|
||||
//
|
||||
// Usage
|
||||
// -----
|
||||
// This example encodes a raw video. And the last argument passed in specifies
|
||||
// the frame number to update the reference frame on. For example, run
|
||||
// examples/aom_cx_set_ref av1 352 288 in.yuv out.ivf 4 30
|
||||
// The parameter is parsed as follows:
|
||||
//
|
||||
//
|
||||
// Extra Variables
|
||||
// ---------------
|
||||
// This example maintains the frame number passed on the command line
|
||||
// in the `update_frame_num` variable.
|
||||
//
|
||||
//
|
||||
// Configuration
|
||||
// -------------
|
||||
//
|
||||
// The reference frame is updated on the frame specified on the command
|
||||
// line.
|
||||
//
|
||||
// Observing The Effects
|
||||
// ---------------------
|
||||
// The encoder and decoder results should be matching when the same reference
|
||||
// frame setting operation is done in both encoder and decoder. Otherwise,
|
||||
// the encoder/decoder mismatch would be seen.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aom/aomcx.h"
|
||||
#include "aom/aom_decoder.h"
|
||||
#include "aom/aom_encoder.h"
|
||||
#include "examples/encoder_util.h"
|
||||
#include "./tools_common.h"
|
||||
#include "./video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit() {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <codec> <width> <height> <infile> <outfile> "
|
||||
"<frame> <limit(optional)>\n",
|
||||
exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void testing_decode(aom_codec_ctx_t *encoder, aom_codec_ctx_t *decoder,
|
||||
unsigned int frame_out, int *mismatch_seen) {
|
||||
aom_image_t enc_img, dec_img;
|
||||
struct av1_ref_frame ref_enc, ref_dec;
|
||||
|
||||
if (*mismatch_seen) return;
|
||||
|
||||
ref_enc.idx = 0;
|
||||
ref_dec.idx = 0;
|
||||
if (aom_codec_control(encoder, AV1_GET_REFERENCE, &ref_enc))
|
||||
die_codec(encoder, "Failed to get encoder reference frame");
|
||||
enc_img = ref_enc.img;
|
||||
if (aom_codec_control(decoder, AV1_GET_REFERENCE, &ref_dec))
|
||||
die_codec(decoder, "Failed to get decoder reference frame");
|
||||
dec_img = ref_dec.img;
|
||||
|
||||
if (!aom_compare_img(&enc_img, &dec_img)) {
|
||||
int y[4], u[4], v[4];
|
||||
|
||||
*mismatch_seen = 1;
|
||||
|
||||
aom_find_mismatch(&enc_img, &dec_img, y, u, v);
|
||||
printf(
|
||||
"Encode/decode mismatch on frame %d at"
|
||||
" Y[%d, %d] {%d/%d},"
|
||||
" U[%d, %d] {%d/%d},"
|
||||
" V[%d, %d] {%d/%d}",
|
||||
frame_out, y[0], y[1], y[2], y[3], u[0], u[1], u[2], u[3], v[0], v[1],
|
||||
v[2], v[3]);
|
||||
}
|
||||
|
||||
aom_img_free(&enc_img);
|
||||
aom_img_free(&dec_img);
|
||||
}
|
||||
|
||||
static int encode_frame(aom_codec_ctx_t *ecodec, aom_image_t *img,
|
||||
unsigned int frame_in, AvxVideoWriter *writer,
|
||||
int test_decode, aom_codec_ctx_t *dcodec,
|
||||
unsigned int *frame_out, int *mismatch_seen) {
|
||||
int got_pkts = 0;
|
||||
aom_codec_iter_t iter = NULL;
|
||||
const aom_codec_cx_pkt_t *pkt = NULL;
|
||||
int got_data;
|
||||
const aom_codec_err_t res =
|
||||
aom_codec_encode(ecodec, img, frame_in, 1, 0, AOM_DL_GOOD_QUALITY);
|
||||
if (res != AOM_CODEC_OK) die_codec(ecodec, "Failed to encode frame");
|
||||
|
||||
got_data = 0;
|
||||
|
||||
while ((pkt = aom_codec_get_cx_data(ecodec, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
|
||||
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
|
||||
const int keyframe = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0;
|
||||
|
||||
if (!(pkt->data.frame.flags & AOM_FRAME_IS_FRAGMENT)) {
|
||||
*frame_out += 1;
|
||||
}
|
||||
|
||||
if (!aom_video_writer_write_frame(writer, pkt->data.frame.buf,
|
||||
pkt->data.frame.sz,
|
||||
pkt->data.frame.pts)) {
|
||||
die_codec(ecodec, "Failed to write compressed frame");
|
||||
}
|
||||
printf(keyframe ? "K" : ".");
|
||||
fflush(stdout);
|
||||
got_data = 1;
|
||||
|
||||
// Decode 1 frame.
|
||||
if (test_decode) {
|
||||
if (aom_codec_decode(dcodec, pkt->data.frame.buf,
|
||||
(unsigned int)pkt->data.frame.sz, NULL, 0))
|
||||
die_codec(dcodec, "Failed to decode frame.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mismatch checking
|
||||
if (got_data && test_decode) {
|
||||
testing_decode(ecodec, dcodec, *frame_out, mismatch_seen);
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile = NULL;
|
||||
// Encoder
|
||||
aom_codec_ctx_t ecodec;
|
||||
aom_codec_enc_cfg_t cfg;
|
||||
unsigned int frame_in = 0;
|
||||
aom_image_t raw;
|
||||
aom_codec_err_t res;
|
||||
AvxVideoInfo info;
|
||||
AvxVideoWriter *writer = NULL;
|
||||
const AvxInterface *encoder = NULL;
|
||||
|
||||
// Test encoder/decoder mismatch.
|
||||
int test_decode = 1;
|
||||
// Decoder
|
||||
aom_codec_ctx_t dcodec;
|
||||
unsigned int frame_out = 0;
|
||||
|
||||
// The frame number to set reference frame on
|
||||
unsigned int update_frame_num = 0;
|
||||
int mismatch_seen = 0;
|
||||
|
||||
const int fps = 30;
|
||||
const int bitrate = 500;
|
||||
|
||||
const char *codec_arg = NULL;
|
||||
const char *width_arg = NULL;
|
||||
const char *height_arg = NULL;
|
||||
const char *infile_arg = NULL;
|
||||
const char *outfile_arg = NULL;
|
||||
const char *update_frame_num_arg = NULL;
|
||||
unsigned int limit = 0;
|
||||
exec_name = argv[0];
|
||||
|
||||
// Clear explicitly, as simply assigning "{ 0 }" generates
|
||||
// "missing-field-initializers" warning in some compilers.
|
||||
memset(&ecodec, 0, sizeof(ecodec));
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
if (argc < 7) die("Invalid number of arguments");
|
||||
|
||||
codec_arg = argv[1];
|
||||
width_arg = argv[2];
|
||||
height_arg = argv[3];
|
||||
infile_arg = argv[4];
|
||||
outfile_arg = argv[5];
|
||||
update_frame_num_arg = argv[6];
|
||||
|
||||
encoder = get_aom_encoder_by_name(codec_arg);
|
||||
if (!encoder) die("Unsupported codec.");
|
||||
|
||||
update_frame_num = (unsigned int)strtoul(update_frame_num_arg, NULL, 0);
|
||||
// In AV1, the reference buffers (cm->buffer_pool->frame_bufs[i].buf) are
|
||||
// allocated while calling aom_codec_encode(), thus, setting reference for
|
||||
// 1st frame isn't supported.
|
||||
if (update_frame_num <= 1) {
|
||||
die("Couldn't parse frame number '%s'\n", update_frame_num_arg);
|
||||
}
|
||||
|
||||
if (argc > 7) {
|
||||
limit = (unsigned int)strtoul(argv[7], NULL, 0);
|
||||
if (update_frame_num > limit)
|
||||
die("Update frame number couldn't larger than limit\n");
|
||||
}
|
||||
|
||||
info.codec_fourcc = encoder->fourcc;
|
||||
info.frame_width = (int)strtol(width_arg, NULL, 0);
|
||||
info.frame_height = (int)strtol(height_arg, NULL, 0);
|
||||
info.time_base.numerator = 1;
|
||||
info.time_base.denominator = fps;
|
||||
|
||||
if (info.frame_width <= 0 || info.frame_height <= 0 ||
|
||||
(info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
|
||||
die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
|
||||
}
|
||||
|
||||
if (!aom_img_alloc(&raw, AOM_IMG_FMT_I420, info.frame_width,
|
||||
info.frame_height, 1)) {
|
||||
die("Failed to allocate image.");
|
||||
}
|
||||
|
||||
printf("Using %s\n", aom_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
res = aom_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) die_codec(&ecodec, "Failed to get default codec config.");
|
||||
|
||||
cfg.g_w = info.frame_width;
|
||||
cfg.g_h = info.frame_height;
|
||||
cfg.g_timebase.num = info.time_base.numerator;
|
||||
cfg.g_timebase.den = info.time_base.denominator;
|
||||
cfg.rc_target_bitrate = bitrate;
|
||||
cfg.g_lag_in_frames = 3;
|
||||
|
||||
writer = aom_video_writer_open(outfile_arg, kContainerIVF, &info);
|
||||
if (!writer) die("Failed to open %s for writing.", outfile_arg);
|
||||
|
||||
if (!(infile = fopen(infile_arg, "rb")))
|
||||
die("Failed to open %s for reading.", infile_arg);
|
||||
|
||||
if (aom_codec_enc_init(&ecodec, encoder->codec_interface(), &cfg, 0))
|
||||
die_codec(&ecodec, "Failed to initialize encoder");
|
||||
|
||||
// Disable alt_ref.
|
||||
if (aom_codec_control(&ecodec, AOME_SET_ENABLEAUTOALTREF, 0))
|
||||
die_codec(&ecodec, "Failed to set enable auto alt ref");
|
||||
|
||||
if (test_decode) {
|
||||
const AvxInterface *decoder = get_aom_decoder_by_name(codec_arg);
|
||||
if (aom_codec_dec_init(&dcodec, decoder->codec_interface(), NULL, 0))
|
||||
die_codec(&dcodec, "Failed to initialize decoder.");
|
||||
}
|
||||
|
||||
// Encode frames.
|
||||
while (aom_img_read(&raw, infile)) {
|
||||
if (limit && frame_in >= limit) break;
|
||||
if (update_frame_num > 1 && frame_out + 1 == update_frame_num) {
|
||||
aom_ref_frame_t ref;
|
||||
ref.frame_type = AOM_LAST_FRAME;
|
||||
ref.img = raw;
|
||||
// Set reference frame in encoder.
|
||||
if (aom_codec_control(&ecodec, AOM_SET_REFERENCE, &ref))
|
||||
die_codec(&ecodec, "Failed to set reference frame");
|
||||
printf(" <SET_REF>");
|
||||
|
||||
// If set_reference in decoder is commented out, the enc/dec mismatch
|
||||
// would be seen.
|
||||
if (test_decode) {
|
||||
if (aom_codec_control(&dcodec, AOM_SET_REFERENCE, &ref))
|
||||
die_codec(&dcodec, "Failed to set reference frame");
|
||||
}
|
||||
}
|
||||
|
||||
encode_frame(&ecodec, &raw, frame_in, writer, test_decode, &dcodec,
|
||||
&frame_out, &mismatch_seen);
|
||||
frame_in++;
|
||||
if (mismatch_seen) break;
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
if (!mismatch_seen)
|
||||
while (encode_frame(&ecodec, NULL, frame_in, writer, test_decode, &dcodec,
|
||||
&frame_out, &mismatch_seen)) {
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fclose(infile);
|
||||
printf("Processed %d frames.\n", frame_out);
|
||||
|
||||
if (test_decode) {
|
||||
if (!mismatch_seen)
|
||||
printf("Encoder/decoder results are matching.\n");
|
||||
else
|
||||
printf("Encoder/decoder results are NOT matching.\n");
|
||||
}
|
||||
|
||||
if (test_decode)
|
||||
if (aom_codec_destroy(&dcodec))
|
||||
die_codec(&dcodec, "Failed to destroy decoder");
|
||||
|
||||
aom_img_free(&raw);
|
||||
if (aom_codec_destroy(&ecodec))
|
||||
die_codec(&ecodec, "Failed to destroy encoder.");
|
||||
|
||||
aom_video_writer_close(writer);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
133
third_party/aom/examples/decode_to_md5.c
vendored
Normal file
133
third_party/aom/examples/decode_to_md5.c
vendored
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
// Frame-by-frame MD5 Checksum
|
||||
// ===========================
|
||||
//
|
||||
// This example builds upon the simple decoder loop to show how checksums
|
||||
// of the decoded output can be generated. These are used for validating
|
||||
// decoder implementations against the reference implementation, for example.
|
||||
//
|
||||
// MD5 algorithm
|
||||
// -------------
|
||||
// The Message-Digest 5 (MD5) is a well known hash function. We have provided
|
||||
// an implementation derived from the RSA Data Security, Inc. MD5 Message-Digest
|
||||
// Algorithm for your use. Our implmentation only changes the interface of this
|
||||
// reference code. You must include the `md5_utils.h` header for access to these
|
||||
// functions.
|
||||
//
|
||||
// Processing The Decoded Data
|
||||
// ---------------------------
|
||||
// Each row of the image is passed to the MD5 accumulator. First the Y plane
|
||||
// is processed, then U, then V. It is important to honor the image's `stride`
|
||||
// values.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aom/aomdx.h"
|
||||
#include "aom/aom_decoder.h"
|
||||
|
||||
#include "../md5_utils.h"
|
||||
#include "../tools_common.h"
|
||||
#include "../video_reader.h"
|
||||
#include "./aom_config.h"
|
||||
|
||||
static void get_image_md5(const aom_image_t *img, unsigned char digest[16]) {
|
||||
int plane, y;
|
||||
MD5Context md5;
|
||||
|
||||
MD5Init(&md5);
|
||||
|
||||
for (plane = 0; plane < 3; ++plane) {
|
||||
const unsigned char *buf = img->planes[plane];
|
||||
const int stride = img->stride[plane];
|
||||
const int w = plane ? (img->d_w + 1) >> 1 : img->d_w;
|
||||
const int h = plane ? (img->d_h + 1) >> 1 : img->d_h;
|
||||
|
||||
for (y = 0; y < h; ++y) {
|
||||
MD5Update(&md5, buf, w);
|
||||
buf += stride;
|
||||
}
|
||||
}
|
||||
|
||||
MD5Final(digest, &md5);
|
||||
}
|
||||
|
||||
static void print_md5(FILE *stream, unsigned char digest[16]) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; ++i) fprintf(stream, "%02x", digest[i]);
|
||||
}
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "Usage: %s <infile> <outfile>\n", exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int frame_cnt = 0;
|
||||
FILE *outfile = NULL;
|
||||
aom_codec_ctx_t codec;
|
||||
AvxVideoReader *reader = NULL;
|
||||
const AvxVideoInfo *info = NULL;
|
||||
const AvxInterface *decoder = NULL;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc != 3) die("Invalid number of arguments.");
|
||||
|
||||
reader = aom_video_reader_open(argv[1]);
|
||||
if (!reader) die("Failed to open %s for reading.", argv[1]);
|
||||
|
||||
if (!(outfile = fopen(argv[2], "wb")))
|
||||
die("Failed to open %s for writing.", argv[2]);
|
||||
|
||||
info = aom_video_reader_get_info(reader);
|
||||
|
||||
decoder = get_aom_decoder_by_fourcc(info->codec_fourcc);
|
||||
if (!decoder) die("Unknown input codec.");
|
||||
|
||||
printf("Using %s\n", aom_codec_iface_name(decoder->codec_interface()));
|
||||
|
||||
if (aom_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0))
|
||||
die_codec(&codec, "Failed to initialize decoder");
|
||||
|
||||
while (aom_video_reader_read_frame(reader)) {
|
||||
aom_codec_iter_t iter = NULL;
|
||||
aom_image_t *img = NULL;
|
||||
size_t frame_size = 0;
|
||||
const unsigned char *frame =
|
||||
aom_video_reader_get_frame(reader, &frame_size);
|
||||
if (aom_codec_decode(&codec, frame, (unsigned int)frame_size, NULL, 0))
|
||||
die_codec(&codec, "Failed to decode frame");
|
||||
|
||||
while ((img = aom_codec_get_frame(&codec, &iter)) != NULL) {
|
||||
unsigned char digest[16];
|
||||
|
||||
get_image_md5(img, digest);
|
||||
print_md5(outfile, digest);
|
||||
fprintf(outfile, " img-%dx%d-%04d.i420\n", img->d_w, img->d_h,
|
||||
++frame_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Processed %d frames.\n", frame_cnt);
|
||||
if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
aom_video_reader_close(reader);
|
||||
|
||||
fclose(outfile);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
149
third_party/aom/examples/decode_with_drops.c
vendored
Normal file
149
third_party/aom/examples/decode_with_drops.c
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
// Decode With Drops Example
|
||||
// =========================
|
||||
//
|
||||
// This is an example utility which drops a series of frames, as specified
|
||||
// on the command line. This is useful for observing the error recovery
|
||||
// features of the codec.
|
||||
//
|
||||
// Usage
|
||||
// -----
|
||||
// This example adds a single argument to the `simple_decoder` example,
|
||||
// which specifies the range or pattern of frames to drop. The parameter is
|
||||
// parsed as follows:
|
||||
//
|
||||
// Dropping A Range Of Frames
|
||||
// --------------------------
|
||||
// To drop a range of frames, specify the starting frame and the ending
|
||||
// frame to drop, separated by a dash. The following command will drop
|
||||
// frames 5 through 10 (base 1).
|
||||
//
|
||||
// $ ./decode_with_drops in.ivf out.i420 5-10
|
||||
//
|
||||
//
|
||||
// Dropping A Pattern Of Frames
|
||||
// ----------------------------
|
||||
// To drop a pattern of frames, specify the number of frames to drop and
|
||||
// the number of frames after which to repeat the pattern, separated by
|
||||
// a forward-slash. The following command will drop 3 of 7 frames.
|
||||
// Specifically, it will decode 4 frames, then drop 3 frames, and then
|
||||
// repeat.
|
||||
//
|
||||
// $ ./decode_with_drops in.ivf out.i420 3/7
|
||||
//
|
||||
//
|
||||
// Extra Variables
|
||||
// ---------------
|
||||
// This example maintains the pattern passed on the command line in the
|
||||
// `n`, `m`, and `is_range` variables:
|
||||
//
|
||||
//
|
||||
// Making The Drop Decision
|
||||
// ------------------------
|
||||
// The example decides whether to drop the frame based on the current
|
||||
// frame number, immediately before decoding the frame.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aom/aomdx.h"
|
||||
#include "aom/aom_decoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_reader.h"
|
||||
#include "./aom_config.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "Usage: %s <infile> <outfile> <N-M|N/M>\n", exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int frame_cnt = 0;
|
||||
FILE *outfile = NULL;
|
||||
aom_codec_ctx_t codec;
|
||||
const AvxInterface *decoder = NULL;
|
||||
AvxVideoReader *reader = NULL;
|
||||
const AvxVideoInfo *info = NULL;
|
||||
int n = 0;
|
||||
int m = 0;
|
||||
int is_range = 0;
|
||||
char *nptr = NULL;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc != 4) die("Invalid number of arguments.");
|
||||
|
||||
reader = aom_video_reader_open(argv[1]);
|
||||
if (!reader) die("Failed to open %s for reading.", argv[1]);
|
||||
|
||||
if (!(outfile = fopen(argv[2], "wb")))
|
||||
die("Failed to open %s for writing.", argv[2]);
|
||||
|
||||
n = (int)strtol(argv[3], &nptr, 0);
|
||||
m = (int)strtol(nptr + 1, NULL, 0);
|
||||
is_range = (*nptr == '-');
|
||||
if (!n || !m || (*nptr != '-' && *nptr != '/'))
|
||||
die("Couldn't parse pattern %s.\n", argv[3]);
|
||||
|
||||
info = aom_video_reader_get_info(reader);
|
||||
|
||||
decoder = get_aom_decoder_by_fourcc(info->codec_fourcc);
|
||||
if (!decoder) die("Unknown input codec.");
|
||||
|
||||
printf("Using %s\n", aom_codec_iface_name(decoder->codec_interface()));
|
||||
|
||||
if (aom_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0))
|
||||
die_codec(&codec, "Failed to initialize decoder.");
|
||||
|
||||
while (aom_video_reader_read_frame(reader)) {
|
||||
aom_codec_iter_t iter = NULL;
|
||||
aom_image_t *img = NULL;
|
||||
size_t frame_size = 0;
|
||||
int skip;
|
||||
const unsigned char *frame =
|
||||
aom_video_reader_get_frame(reader, &frame_size);
|
||||
if (aom_codec_decode(&codec, frame, (unsigned int)frame_size, NULL, 0))
|
||||
die_codec(&codec, "Failed to decode frame.");
|
||||
|
||||
++frame_cnt;
|
||||
|
||||
skip = (is_range && frame_cnt >= n && frame_cnt <= m) ||
|
||||
(!is_range && m - (frame_cnt - 1) % m <= n);
|
||||
|
||||
if (!skip) {
|
||||
putc('.', stdout);
|
||||
|
||||
while ((img = aom_codec_get_frame(&codec, &iter)) != NULL)
|
||||
aom_img_write(img, outfile);
|
||||
} else {
|
||||
putc('X', stdout);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
printf("Processed %d frames.\n", frame_cnt);
|
||||
if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
printf("Play: ffplay -f rawvideo -pix_fmt yuv420p -s %dx%d %s\n",
|
||||
info->frame_width, info->frame_height, argv[2]);
|
||||
|
||||
aom_video_reader_close(reader);
|
||||
fclose(outfile);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
139
third_party/aom/examples/encoder_util.c
vendored
Normal file
139
third_party/aom/examples/encoder_util.c
vendored
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2017, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
// Utility functions used by encoder binaries.
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./encoder_util.h"
|
||||
#include "aom/aom_integer.h"
|
||||
|
||||
#define mmin(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
static void find_mismatch_plane(const aom_image_t *const img1,
|
||||
const aom_image_t *const img2, int plane,
|
||||
int use_highbitdepth, int loc[4]) {
|
||||
const unsigned char *const p1 = img1->planes[plane];
|
||||
const int p1_stride = img1->stride[plane] >> use_highbitdepth;
|
||||
const unsigned char *const p2 = img2->planes[plane];
|
||||
const int p2_stride = img2->stride[plane] >> use_highbitdepth;
|
||||
const uint32_t bsize = 64;
|
||||
const int is_y_plane = (plane == AOM_PLANE_Y);
|
||||
const uint32_t bsizex = is_y_plane ? bsize : bsize >> img1->x_chroma_shift;
|
||||
const uint32_t bsizey = is_y_plane ? bsize : bsize >> img1->y_chroma_shift;
|
||||
const uint32_t c_w =
|
||||
is_y_plane ? img1->d_w
|
||||
: (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
|
||||
const uint32_t c_h =
|
||||
is_y_plane ? img1->d_h
|
||||
: (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
|
||||
assert(img1->d_w == img2->d_w && img1->d_h == img2->d_h);
|
||||
assert(img1->x_chroma_shift == img2->x_chroma_shift &&
|
||||
img1->y_chroma_shift == img2->y_chroma_shift);
|
||||
loc[0] = loc[1] = loc[2] = loc[3] = -1;
|
||||
int match = 1;
|
||||
uint32_t i, j;
|
||||
for (i = 0; match && i < c_h; i += bsizey) {
|
||||
for (j = 0; match && j < c_w; j += bsizex) {
|
||||
const int si =
|
||||
is_y_plane ? mmin(i + bsizey, c_h) - i : mmin(i + bsizey, c_h - i);
|
||||
const int sj =
|
||||
is_y_plane ? mmin(j + bsizex, c_w) - j : mmin(j + bsizex, c_w - j);
|
||||
int k, l;
|
||||
for (k = 0; match && k < si; ++k) {
|
||||
for (l = 0; match && l < sj; ++l) {
|
||||
const int row = i + k;
|
||||
const int col = j + l;
|
||||
const int offset1 = row * p1_stride + col;
|
||||
const int offset2 = row * p2_stride + col;
|
||||
const int val1 = use_highbitdepth
|
||||
? p1[2 * offset1] | (p1[2 * offset1 + 1] << 8)
|
||||
: p1[offset1];
|
||||
const int val2 = use_highbitdepth
|
||||
? p2[2 * offset2] | (p2[2 * offset2 + 1] << 8)
|
||||
: p2[offset2];
|
||||
if (val1 != val2) {
|
||||
loc[0] = row;
|
||||
loc[1] = col;
|
||||
loc[2] = val1;
|
||||
loc[3] = val2;
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void find_mismatch_helper(const aom_image_t *const img1,
|
||||
const aom_image_t *const img2,
|
||||
int use_highbitdepth, int yloc[4], int uloc[4],
|
||||
int vloc[4]) {
|
||||
#if !CONFIG_HIGHBITDEPTH
|
||||
assert(!use_highbitdepth);
|
||||
#endif // !CONFIG_HIGHBITDEPTH
|
||||
find_mismatch_plane(img1, img2, AOM_PLANE_Y, use_highbitdepth, yloc);
|
||||
find_mismatch_plane(img1, img2, AOM_PLANE_U, use_highbitdepth, uloc);
|
||||
find_mismatch_plane(img1, img2, AOM_PLANE_V, use_highbitdepth, vloc);
|
||||
}
|
||||
|
||||
#if CONFIG_HIGHBITDEPTH
|
||||
void aom_find_mismatch_high(const aom_image_t *const img1,
|
||||
const aom_image_t *const img2, int yloc[4],
|
||||
int uloc[4], int vloc[4]) {
|
||||
find_mismatch_helper(img1, img2, 1, yloc, uloc, vloc);
|
||||
}
|
||||
#endif
|
||||
|
||||
void aom_find_mismatch(const aom_image_t *const img1,
|
||||
const aom_image_t *const img2, int yloc[4], int uloc[4],
|
||||
int vloc[4]) {
|
||||
find_mismatch_helper(img1, img2, 0, yloc, uloc, vloc);
|
||||
}
|
||||
|
||||
int aom_compare_img(const aom_image_t *const img1,
|
||||
const aom_image_t *const img2) {
|
||||
uint32_t l_w = img1->d_w;
|
||||
uint32_t c_w = (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
|
||||
const uint32_t c_h =
|
||||
(img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
|
||||
uint32_t i;
|
||||
int match = 1;
|
||||
|
||||
match &= (img1->fmt == img2->fmt);
|
||||
match &= (img1->d_w == img2->d_w);
|
||||
match &= (img1->d_h == img2->d_h);
|
||||
#if CONFIG_HIGHBITDEPTH
|
||||
if (img1->fmt & AOM_IMG_FMT_HIGHBITDEPTH) {
|
||||
l_w *= 2;
|
||||
c_w *= 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < img1->d_h; ++i)
|
||||
match &= (memcmp(img1->planes[AOM_PLANE_Y] + i * img1->stride[AOM_PLANE_Y],
|
||||
img2->planes[AOM_PLANE_Y] + i * img2->stride[AOM_PLANE_Y],
|
||||
l_w) == 0);
|
||||
|
||||
for (i = 0; i < c_h; ++i)
|
||||
match &= (memcmp(img1->planes[AOM_PLANE_U] + i * img1->stride[AOM_PLANE_U],
|
||||
img2->planes[AOM_PLANE_U] + i * img2->stride[AOM_PLANE_U],
|
||||
c_w) == 0);
|
||||
|
||||
for (i = 0; i < c_h; ++i)
|
||||
match &= (memcmp(img1->planes[AOM_PLANE_V] + i * img1->stride[AOM_PLANE_V],
|
||||
img2->planes[AOM_PLANE_V] + i * img2->stride[AOM_PLANE_V],
|
||||
c_w) == 0);
|
||||
|
||||
return match;
|
||||
}
|
||||
36
third_party/aom/examples/encoder_util.h
vendored
Normal file
36
third_party/aom/examples/encoder_util.h
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2017, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
// Utility functions used by encoder binaries.
|
||||
|
||||
#ifndef EXAMPLES_ENCODER_UTIL_H_
|
||||
#define EXAMPLES_ENCODER_UTIL_H_
|
||||
|
||||
#include "./aom_config.h"
|
||||
#include "aom/aom_image.h"
|
||||
|
||||
// Returns mismatch location (?loc[0],?loc[1]) and the values at that location
|
||||
// in img1 (?loc[2]) and img2 (?loc[3]).
|
||||
#if CONFIG_HIGHBITDEPTH
|
||||
void aom_find_mismatch_high(const aom_image_t *const img1,
|
||||
const aom_image_t *const img2, int yloc[4],
|
||||
int uloc[4], int vloc[4]);
|
||||
#endif // CONFIG_HIGHBITDEPTH
|
||||
|
||||
void aom_find_mismatch(const aom_image_t *const img1,
|
||||
const aom_image_t *const img2, int yloc[4], int uloc[4],
|
||||
int vloc[4]);
|
||||
|
||||
// Returns 1 if the two images match.
|
||||
int aom_compare_img(const aom_image_t *const img1,
|
||||
const aom_image_t *const img2);
|
||||
|
||||
#endif // EXAMPLES_ENCODER_UTIL_H_
|
||||
678
third_party/aom/examples/inspect.c
vendored
Normal file
678
third_party/aom/examples/inspect.c
vendored
Normal file
|
|
@ -0,0 +1,678 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
// Inspect Decoder
|
||||
// ================
|
||||
//
|
||||
// This is a simple decoder loop that writes JSON stats to stdout. This tool
|
||||
// can also be compiled with Emscripten and used as a library.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./args.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#else
|
||||
#define EMSCRIPTEN_KEEPALIVE
|
||||
#endif
|
||||
|
||||
#include "aom/aom_decoder.h"
|
||||
#include "aom/aomdx.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_reader.h"
|
||||
#include "./aom_config.h"
|
||||
// #include "av1/av1_dx_iface.c"
|
||||
#include "../av1/common/onyxc_int.h"
|
||||
#if CONFIG_ACCOUNTING
|
||||
#include "../av1/decoder/accounting.h"
|
||||
#endif
|
||||
#include "../av1/decoder/inspection.h"
|
||||
|
||||
#include "../video_common.h"
|
||||
|
||||
// Max JSON buffer size.
|
||||
const int MAX_BUFFER = 1024 * 1024 * 32;
|
||||
|
||||
typedef enum {
|
||||
ACCOUNTING_LAYER = 1,
|
||||
BLOCK_SIZE_LAYER = 1 << 1,
|
||||
TRANSFORM_SIZE_LAYER = 1 << 2,
|
||||
TRANSFORM_TYPE_LAYER = 1 << 3,
|
||||
MODE_LAYER = 1 << 4,
|
||||
SKIP_LAYER = 1 << 5,
|
||||
FILTER_LAYER = 1 << 6,
|
||||
CDEF_LAYER = 1 << 7,
|
||||
REFERENCE_FRAME_LAYER = 1 << 8,
|
||||
MOTION_VECTORS_LAYER = 1 << 9,
|
||||
UV_MODE_LAYER = 1 << 10,
|
||||
ALL_LAYERS = (1 << 11) - 1
|
||||
} LayerType;
|
||||
|
||||
static LayerType layers = 0;
|
||||
|
||||
static int stop_after = 0;
|
||||
static int compress = 0;
|
||||
|
||||
static const arg_def_t limit_arg =
|
||||
ARG_DEF(NULL, "limit", 1, "Stop decoding after n frames");
|
||||
static const arg_def_t dump_all_arg = ARG_DEF("A", "all", 0, "Dump All");
|
||||
static const arg_def_t compress_arg =
|
||||
ARG_DEF("x", "compress", 0, "Compress JSON using RLE");
|
||||
static const arg_def_t dump_accounting_arg =
|
||||
ARG_DEF("a", "accounting", 0, "Dump Accounting");
|
||||
static const arg_def_t dump_block_size_arg =
|
||||
ARG_DEF("bs", "blockSize", 0, "Dump Block Size");
|
||||
static const arg_def_t dump_motion_vectors_arg =
|
||||
ARG_DEF("mv", "motionVectors", 0, "Dump Motion Vectors");
|
||||
static const arg_def_t dump_transform_size_arg =
|
||||
ARG_DEF("ts", "transformSize", 0, "Dump Transform Size");
|
||||
static const arg_def_t dump_transform_type_arg =
|
||||
ARG_DEF("tt", "transformType", 0, "Dump Transform Type");
|
||||
static const arg_def_t dump_mode_arg = ARG_DEF("m", "mode", 0, "Dump Mode");
|
||||
static const arg_def_t dump_uv_mode_arg =
|
||||
ARG_DEF("uvm", "uv_mode", 0, "Dump UV Intra Prediction Modes");
|
||||
static const arg_def_t dump_skip_arg = ARG_DEF("s", "skip", 0, "Dump Skip");
|
||||
static const arg_def_t dump_filter_arg =
|
||||
ARG_DEF("f", "filter", 0, "Dump Filter");
|
||||
static const arg_def_t dump_cdef_arg = ARG_DEF("c", "cdef", 0, "Dump CDEF");
|
||||
static const arg_def_t dump_reference_frame_arg =
|
||||
ARG_DEF("r", "referenceFrame", 0, "Dump Reference Frame");
|
||||
static const arg_def_t usage_arg = ARG_DEF("h", "help", 0, "Help");
|
||||
|
||||
static const arg_def_t *main_args[] = { &limit_arg,
|
||||
&dump_all_arg,
|
||||
&compress_arg,
|
||||
#if CONFIG_ACCOUNTING
|
||||
&dump_accounting_arg,
|
||||
#endif
|
||||
&dump_block_size_arg,
|
||||
&dump_transform_size_arg,
|
||||
&dump_transform_type_arg,
|
||||
&dump_mode_arg,
|
||||
&dump_uv_mode_arg,
|
||||
&dump_skip_arg,
|
||||
&dump_filter_arg,
|
||||
#if CONFIG_CDEF
|
||||
&dump_cdef_arg,
|
||||
#endif
|
||||
&dump_reference_frame_arg,
|
||||
&dump_motion_vectors_arg,
|
||||
&usage_arg,
|
||||
NULL };
|
||||
#define ENUM(name) \
|
||||
{ #name, name }
|
||||
#define LAST_ENUM \
|
||||
{ NULL, 0 }
|
||||
typedef struct map_entry {
|
||||
const char *name;
|
||||
int value;
|
||||
} map_entry;
|
||||
|
||||
const map_entry refs_map[] = { ENUM(INTRA_FRAME), ENUM(LAST_FRAME),
|
||||
#if CONFIG_EXT_REFS
|
||||
ENUM(LAST2_FRAME), ENUM(LAST3_FRAME),
|
||||
ENUM(GOLDEN_FRAME), ENUM(BWDREF_FRAME),
|
||||
ENUM(ALTREF_FRAME),
|
||||
#else
|
||||
ENUM(GOLDEN_FRAME), ENUM(ALTREF_FRAME),
|
||||
#endif
|
||||
LAST_ENUM };
|
||||
|
||||
const map_entry block_size_map[] = {
|
||||
#if CONFIG_CB4X4
|
||||
ENUM(BLOCK_2X2), ENUM(BLOCK_2X4), ENUM(BLOCK_4X2),
|
||||
#endif
|
||||
ENUM(BLOCK_4X4), ENUM(BLOCK_4X8), ENUM(BLOCK_8X4),
|
||||
ENUM(BLOCK_8X8), ENUM(BLOCK_8X16), ENUM(BLOCK_16X8),
|
||||
ENUM(BLOCK_16X16), ENUM(BLOCK_16X32), ENUM(BLOCK_32X16),
|
||||
ENUM(BLOCK_32X32), ENUM(BLOCK_32X64), ENUM(BLOCK_64X32),
|
||||
ENUM(BLOCK_64X64),
|
||||
#if CONFIG_EXT_PARTITION
|
||||
ENUM(BLOCK_64X128), ENUM(BLOCK_128X64), ENUM(BLOCK_128X128),
|
||||
#endif
|
||||
LAST_ENUM
|
||||
};
|
||||
|
||||
const map_entry tx_size_map[] = {
|
||||
#if CONFIG_CB4X4
|
||||
ENUM(TX_2X2),
|
||||
#endif
|
||||
ENUM(TX_4X4), ENUM(TX_8X8), ENUM(TX_16X16), ENUM(TX_32X32),
|
||||
#if CONFIG_TX64X64
|
||||
ENUM(TX_64X64),
|
||||
#endif
|
||||
ENUM(TX_4X8), ENUM(TX_8X4), ENUM(TX_8X16), ENUM(TX_16X8),
|
||||
ENUM(TX_16X32), ENUM(TX_32X16), ENUM(TX_4X16), ENUM(TX_16X4),
|
||||
ENUM(TX_8X32), ENUM(TX_32X8), LAST_ENUM
|
||||
};
|
||||
|
||||
const map_entry tx_type_map[] = { ENUM(DCT_DCT),
|
||||
ENUM(ADST_DCT),
|
||||
ENUM(DCT_ADST),
|
||||
ENUM(ADST_ADST),
|
||||
#if CONFIG_EXT_TX
|
||||
ENUM(FLIPADST_DCT),
|
||||
ENUM(DCT_FLIPADST),
|
||||
ENUM(FLIPADST_FLIPADST),
|
||||
ENUM(ADST_FLIPADST),
|
||||
ENUM(FLIPADST_ADST),
|
||||
ENUM(IDTX),
|
||||
ENUM(V_DCT),
|
||||
ENUM(H_DCT),
|
||||
ENUM(V_ADST),
|
||||
ENUM(H_ADST),
|
||||
ENUM(V_FLIPADST),
|
||||
ENUM(H_FLIPADST),
|
||||
#endif
|
||||
LAST_ENUM };
|
||||
|
||||
const map_entry prediction_mode_map[] = {
|
||||
ENUM(DC_PRED), ENUM(V_PRED),
|
||||
ENUM(H_PRED), ENUM(D45_PRED),
|
||||
ENUM(D135_PRED), ENUM(D117_PRED),
|
||||
ENUM(D153_PRED), ENUM(D207_PRED),
|
||||
ENUM(D63_PRED),
|
||||
#if CONFIG_ALT_INTRA
|
||||
ENUM(SMOOTH_PRED),
|
||||
#endif
|
||||
ENUM(TM_PRED), ENUM(NEARESTMV),
|
||||
ENUM(NEARMV), ENUM(ZEROMV),
|
||||
ENUM(NEWMV),
|
||||
#if CONFIG_EXT_INTER
|
||||
ENUM(NEWFROMNEARMV), ENUM(NEAREST_NEARESTMV),
|
||||
ENUM(NEAREST_NEARMV), ENUM(NEAR_NEARESTMV),
|
||||
ENUM(NEAR_NEARMV), ENUM(NEAREST_NEWMV),
|
||||
ENUM(NEW_NEARESTMV), ENUM(NEAR_NEWMV),
|
||||
ENUM(NEW_NEARMV), ENUM(ZERO_ZEROMV),
|
||||
ENUM(NEW_NEWMV),
|
||||
#endif
|
||||
ENUM(INTRA_INVALID), LAST_ENUM
|
||||
};
|
||||
|
||||
#define NO_SKIP 0
|
||||
#define SKIP 1
|
||||
|
||||
const map_entry skip_map[] = { ENUM(SKIP), ENUM(NO_SKIP), LAST_ENUM };
|
||||
|
||||
const map_entry config_map[] = { ENUM(MI_SIZE), LAST_ENUM };
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
insp_frame_data frame_data;
|
||||
int frame_count = 0;
|
||||
int decoded_frame_count = 0;
|
||||
aom_codec_ctx_t codec;
|
||||
AvxVideoReader *reader = NULL;
|
||||
const AvxVideoInfo *info = NULL;
|
||||
aom_image_t *img = NULL;
|
||||
|
||||
void on_frame_decoded_dump(char *json) {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM_({ Module.on_frame_decoded_json($0); }, json);
|
||||
#else
|
||||
printf("%s", json);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Writing out the JSON buffer using snprintf is very slow, especially when
|
||||
// compiled with emscripten, these functions speed things up quite a bit.
|
||||
int put_str(char *buffer, const char *str) {
|
||||
int i;
|
||||
for (i = 0; str[i] != '\0'; i++) {
|
||||
buffer[i] = str[i];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int put_num(char *buffer, char prefix, int num, char suffix) {
|
||||
int i = 0;
|
||||
char *buf = buffer;
|
||||
int is_neg = 0;
|
||||
if (prefix) {
|
||||
buf[i++] = prefix;
|
||||
}
|
||||
if (num == 0) {
|
||||
buf[i++] = '0';
|
||||
} else {
|
||||
if (num < 0) {
|
||||
num = -num;
|
||||
is_neg = 1;
|
||||
}
|
||||
int s = i;
|
||||
while (num != 0) {
|
||||
buf[i++] = '0' + (num % 10);
|
||||
num = num / 10;
|
||||
}
|
||||
if (is_neg) {
|
||||
buf[i++] = '-';
|
||||
}
|
||||
int e = i - 1;
|
||||
while (s < e) {
|
||||
int t = buf[s];
|
||||
buf[s] = buf[e];
|
||||
buf[e] = t;
|
||||
s++;
|
||||
e--;
|
||||
}
|
||||
}
|
||||
if (suffix) {
|
||||
buf[i++] = suffix;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int put_map(char *buffer, const map_entry *map) {
|
||||
char *buf = buffer;
|
||||
const map_entry *entry = map;
|
||||
while (entry->name != NULL) {
|
||||
*(buf++) = '"';
|
||||
buf += put_str(buf, entry->name);
|
||||
*(buf++) = '"';
|
||||
buf += put_num(buf, ':', entry->value, 0);
|
||||
entry++;
|
||||
if (entry->name != NULL) {
|
||||
*(buf++) = ',';
|
||||
}
|
||||
}
|
||||
return buf - buffer;
|
||||
}
|
||||
|
||||
int put_reference_frame(char *buffer) {
|
||||
const int mi_rows = frame_data.mi_rows;
|
||||
const int mi_cols = frame_data.mi_cols;
|
||||
char *buf = buffer;
|
||||
int r, c, t;
|
||||
buf += put_str(buf, " \"referenceFrameMap\": {");
|
||||
buf += put_map(buf, refs_map);
|
||||
buf += put_str(buf, "},\n");
|
||||
buf += put_str(buf, " \"referenceFrame\": [");
|
||||
for (r = 0; r < mi_rows; ++r) {
|
||||
*(buf++) = '[';
|
||||
for (c = 0; c < mi_cols; ++c) {
|
||||
insp_mi_data *mi = &frame_data.mi_grid[r * mi_cols + c];
|
||||
buf += put_num(buf, '[', mi->ref_frame[0], 0);
|
||||
buf += put_num(buf, ',', mi->ref_frame[1], ']');
|
||||
if (compress) { // RLE
|
||||
for (t = c + 1; t < mi_cols; ++t) {
|
||||
insp_mi_data *next_mi = &frame_data.mi_grid[r * mi_cols + t];
|
||||
if (mi->ref_frame[0] != next_mi->ref_frame[0] ||
|
||||
mi->ref_frame[1] != next_mi->ref_frame[1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (t - c > 1) {
|
||||
*(buf++) = ',';
|
||||
buf += put_num(buf, '[', t - c - 1, ']');
|
||||
c = t - 1;
|
||||
}
|
||||
}
|
||||
if (c < mi_cols - 1) *(buf++) = ',';
|
||||
}
|
||||
*(buf++) = ']';
|
||||
if (r < mi_rows - 1) *(buf++) = ',';
|
||||
}
|
||||
buf += put_str(buf, "],\n");
|
||||
return buf - buffer;
|
||||
}
|
||||
|
||||
int put_motion_vectors(char *buffer) {
|
||||
const int mi_rows = frame_data.mi_rows;
|
||||
const int mi_cols = frame_data.mi_cols;
|
||||
char *buf = buffer;
|
||||
int r, c, t;
|
||||
buf += put_str(buf, " \"motionVectors\": [");
|
||||
for (r = 0; r < mi_rows; ++r) {
|
||||
*(buf++) = '[';
|
||||
for (c = 0; c < mi_cols; ++c) {
|
||||
insp_mi_data *mi = &frame_data.mi_grid[r * mi_cols + c];
|
||||
buf += put_num(buf, '[', mi->mv[0].col, 0);
|
||||
buf += put_num(buf, ',', mi->mv[0].row, 0);
|
||||
buf += put_num(buf, ',', mi->mv[1].col, 0);
|
||||
buf += put_num(buf, ',', mi->mv[1].row, ']');
|
||||
if (compress) { // RLE
|
||||
for (t = c + 1; t < mi_cols; ++t) {
|
||||
insp_mi_data *next_mi = &frame_data.mi_grid[r * mi_cols + t];
|
||||
if (mi->mv[0].col != next_mi->mv[0].col ||
|
||||
mi->mv[0].row != next_mi->mv[0].row ||
|
||||
mi->mv[1].col != next_mi->mv[1].col ||
|
||||
mi->mv[1].row != next_mi->mv[1].row) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (t - c > 1) {
|
||||
*(buf++) = ',';
|
||||
buf += put_num(buf, '[', t - c - 1, ']');
|
||||
c = t - 1;
|
||||
}
|
||||
}
|
||||
if (c < mi_cols - 1) *(buf++) = ',';
|
||||
}
|
||||
*(buf++) = ']';
|
||||
if (r < mi_rows - 1) *(buf++) = ',';
|
||||
}
|
||||
buf += put_str(buf, "],\n");
|
||||
return buf - buffer;
|
||||
}
|
||||
|
||||
int put_block_info(char *buffer, const map_entry *map, const char *name,
|
||||
size_t offset) {
|
||||
const int mi_rows = frame_data.mi_rows;
|
||||
const int mi_cols = frame_data.mi_cols;
|
||||
char *buf = buffer;
|
||||
int r, c, t, v;
|
||||
if (map) {
|
||||
buf += snprintf(buf, MAX_BUFFER, " \"%sMap\": {", name);
|
||||
buf += put_map(buf, map);
|
||||
buf += put_str(buf, "},\n");
|
||||
}
|
||||
buf += snprintf(buf, MAX_BUFFER, " \"%s\": [", name);
|
||||
for (r = 0; r < mi_rows; ++r) {
|
||||
*(buf++) = '[';
|
||||
for (c = 0; c < mi_cols; ++c) {
|
||||
insp_mi_data *curr_mi = &frame_data.mi_grid[r * mi_cols + c];
|
||||
v = *(((int8_t *)curr_mi) + offset);
|
||||
buf += put_num(buf, 0, v, 0);
|
||||
if (compress) { // RLE
|
||||
for (t = c + 1; t < mi_cols; ++t) {
|
||||
insp_mi_data *next_mi = &frame_data.mi_grid[r * mi_cols + t];
|
||||
if (v != *(((int8_t *)next_mi) + offset)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (t - c > 1) {
|
||||
*(buf++) = ',';
|
||||
buf += put_num(buf, '[', t - c - 1, ']');
|
||||
c = t - 1;
|
||||
}
|
||||
}
|
||||
if (c < mi_cols - 1) *(buf++) = ',';
|
||||
}
|
||||
*(buf++) = ']';
|
||||
if (r < mi_rows - 1) *(buf++) = ',';
|
||||
}
|
||||
buf += put_str(buf, "],\n");
|
||||
return buf - buffer;
|
||||
}
|
||||
|
||||
#if CONFIG_ACCOUNTING
|
||||
int put_accounting(char *buffer) {
|
||||
char *buf = buffer;
|
||||
int i;
|
||||
const Accounting *accounting = frame_data.accounting;
|
||||
if (accounting == NULL) {
|
||||
printf("XXX\n");
|
||||
return 0;
|
||||
}
|
||||
const int num_syms = accounting->syms.num_syms;
|
||||
const int num_strs = accounting->syms.dictionary.num_strs;
|
||||
buf += put_str(buf, " \"symbolsMap\": [");
|
||||
for (i = 0; i < num_strs; i++) {
|
||||
buf += snprintf(buf, MAX_BUFFER, "\"%s\"",
|
||||
accounting->syms.dictionary.strs[i]);
|
||||
if (i < num_strs - 1) *(buf++) = ',';
|
||||
}
|
||||
buf += put_str(buf, "],\n");
|
||||
buf += put_str(buf, " \"symbols\": [\n ");
|
||||
AccountingSymbolContext context;
|
||||
context.x = -2;
|
||||
context.y = -2;
|
||||
AccountingSymbol *sym;
|
||||
for (i = 0; i < num_syms; i++) {
|
||||
sym = &accounting->syms.syms[i];
|
||||
if (memcmp(&context, &sym->context, sizeof(AccountingSymbolContext)) != 0) {
|
||||
buf += put_num(buf, '[', sym->context.x, 0);
|
||||
buf += put_num(buf, ',', sym->context.y, ']');
|
||||
} else {
|
||||
buf += put_num(buf, '[', sym->id, 0);
|
||||
buf += put_num(buf, ',', sym->bits, 0);
|
||||
buf += put_num(buf, ',', sym->samples, ']');
|
||||
}
|
||||
context = sym->context;
|
||||
if (i < num_syms - 1) *(buf++) = ',';
|
||||
}
|
||||
buf += put_str(buf, "],\n");
|
||||
return buf - buffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
void inspect(void *pbi, void *data) {
|
||||
/* Fetch frame data. */
|
||||
ifd_inspect(&frame_data, pbi);
|
||||
(void)data;
|
||||
// We allocate enough space and hope we don't write out of bounds. Totally
|
||||
// unsafe but this speeds things up, especially when compiled to Javascript.
|
||||
char *buffer = aom_malloc(MAX_BUFFER);
|
||||
char *buf = buffer;
|
||||
buf += put_str(buf, "{\n");
|
||||
if (layers & BLOCK_SIZE_LAYER) {
|
||||
buf += put_block_info(buf, block_size_map, "blockSize",
|
||||
offsetof(insp_mi_data, sb_type));
|
||||
}
|
||||
if (layers & TRANSFORM_SIZE_LAYER) {
|
||||
buf += put_block_info(buf, tx_size_map, "transformSize",
|
||||
offsetof(insp_mi_data, tx_size));
|
||||
}
|
||||
if (layers & TRANSFORM_TYPE_LAYER) {
|
||||
buf += put_block_info(buf, tx_type_map, "transformType",
|
||||
offsetof(insp_mi_data, tx_type));
|
||||
}
|
||||
if (layers & MODE_LAYER) {
|
||||
buf += put_block_info(buf, prediction_mode_map, "mode",
|
||||
offsetof(insp_mi_data, mode));
|
||||
}
|
||||
if (layers & UV_MODE_LAYER) {
|
||||
buf += put_block_info(buf, prediction_mode_map, "uv_mode",
|
||||
offsetof(insp_mi_data, uv_mode));
|
||||
}
|
||||
if (layers & SKIP_LAYER) {
|
||||
buf += put_block_info(buf, skip_map, "skip", offsetof(insp_mi_data, skip));
|
||||
}
|
||||
if (layers & FILTER_LAYER) {
|
||||
buf += put_block_info(buf, NULL, "filter", offsetof(insp_mi_data, filter));
|
||||
}
|
||||
#if CONFIG_CDEF
|
||||
if (layers & CDEF_LAYER) {
|
||||
buf += put_block_info(buf, NULL, "cdef_level",
|
||||
offsetof(insp_mi_data, cdef_level));
|
||||
buf += put_block_info(buf, NULL, "cdef_strength",
|
||||
offsetof(insp_mi_data, cdef_strength));
|
||||
}
|
||||
#endif
|
||||
if (layers & MOTION_VECTORS_LAYER) {
|
||||
buf += put_motion_vectors(buf);
|
||||
}
|
||||
if (layers & REFERENCE_FRAME_LAYER) {
|
||||
buf += put_reference_frame(buf);
|
||||
}
|
||||
#if CONFIG_ACCOUNTING
|
||||
if (layers & ACCOUNTING_LAYER) {
|
||||
buf += put_accounting(buf);
|
||||
}
|
||||
#endif
|
||||
buf += snprintf(buf, MAX_BUFFER, " \"frame\": %d,\n", decoded_frame_count);
|
||||
buf += snprintf(buf, MAX_BUFFER, " \"showFrame\": %d,\n",
|
||||
frame_data.show_frame);
|
||||
buf += snprintf(buf, MAX_BUFFER, " \"frameType\": %d,\n",
|
||||
frame_data.frame_type);
|
||||
buf += snprintf(buf, MAX_BUFFER, " \"baseQIndex\": %d,\n",
|
||||
frame_data.base_qindex);
|
||||
buf += snprintf(buf, MAX_BUFFER, " \"tileCols\": %d,\n",
|
||||
frame_data.tile_mi_cols);
|
||||
buf += snprintf(buf, MAX_BUFFER, " \"tileRows\": %d,\n",
|
||||
frame_data.tile_mi_rows);
|
||||
buf += put_str(buf, " \"config\": {");
|
||||
buf += put_map(buf, config_map);
|
||||
buf += put_str(buf, "},\n");
|
||||
buf += snprintf(buf, MAX_BUFFER, " \"configString\": \"%s\"\n",
|
||||
aom_codec_build_config());
|
||||
decoded_frame_count++;
|
||||
buf += put_str(buf, "},\n");
|
||||
*(buf++) = 0;
|
||||
on_frame_decoded_dump(buffer);
|
||||
aom_free(buffer);
|
||||
}
|
||||
|
||||
void ifd_init_cb() {
|
||||
aom_inspect_init ii;
|
||||
ii.inspect_cb = inspect;
|
||||
ii.inspect_ctx = NULL;
|
||||
aom_codec_control(&codec, AV1_SET_INSPECTION_CALLBACK, &ii);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int open_file(char *file) {
|
||||
if (file == NULL) {
|
||||
// The JS analyzer puts the .ivf file at this location.
|
||||
file = "/tmp/input.ivf";
|
||||
}
|
||||
reader = aom_video_reader_open(file);
|
||||
if (!reader) die("Failed to open %s for reading.", file);
|
||||
info = aom_video_reader_get_info(reader);
|
||||
const AvxInterface *decoder = get_aom_decoder_by_fourcc(info->codec_fourcc);
|
||||
if (!decoder) die("Unknown input codec.");
|
||||
fprintf(stderr, "Using %s\n",
|
||||
aom_codec_iface_name(decoder->codec_interface()));
|
||||
if (aom_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0))
|
||||
die_codec(&codec, "Failed to initialize decoder.");
|
||||
ifd_init(&frame_data, info->frame_width, info->frame_height);
|
||||
ifd_init_cb();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int read_frame() {
|
||||
if (!aom_video_reader_read_frame(reader)) return EXIT_FAILURE;
|
||||
img = NULL;
|
||||
aom_codec_iter_t iter = NULL;
|
||||
size_t frame_size = 0;
|
||||
const unsigned char *frame = aom_video_reader_get_frame(reader, &frame_size);
|
||||
if (aom_codec_decode(&codec, frame, (unsigned int)frame_size, NULL, 0) !=
|
||||
AOM_CODEC_OK) {
|
||||
die_codec(&codec, "Failed to decode frame.");
|
||||
}
|
||||
img = aom_codec_get_frame(&codec, &iter);
|
||||
if (img == NULL) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
++frame_count;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
const char *get_aom_codec_build_config() { return aom_codec_build_config(); }
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int get_bit_depth() { return img->bit_depth; }
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
unsigned char *get_plane(int plane) { return img->planes[plane]; }
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int get_plane_stride(int plane) { return img->stride[plane]; }
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int get_plane_width(int plane) { return aom_img_plane_width(img, plane); }
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int get_plane_height(int plane) { return aom_img_plane_height(img, plane); }
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int get_frame_width() { return info->frame_width; }
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int get_frame_height() { return info->frame_height; }
|
||||
|
||||
static void parse_args(char **argv) {
|
||||
char **argi, **argj;
|
||||
struct arg arg;
|
||||
(void)dump_accounting_arg;
|
||||
(void)dump_cdef_arg;
|
||||
for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) {
|
||||
arg.argv_step = 1;
|
||||
if (arg_match(&arg, &dump_block_size_arg, argi)) layers |= BLOCK_SIZE_LAYER;
|
||||
#if CONFIG_ACCOUNTING
|
||||
else if (arg_match(&arg, &dump_accounting_arg, argi))
|
||||
layers |= ACCOUNTING_LAYER;
|
||||
#endif
|
||||
else if (arg_match(&arg, &dump_transform_size_arg, argi))
|
||||
layers |= TRANSFORM_SIZE_LAYER;
|
||||
else if (arg_match(&arg, &dump_transform_type_arg, argi))
|
||||
layers |= TRANSFORM_TYPE_LAYER;
|
||||
else if (arg_match(&arg, &dump_mode_arg, argi))
|
||||
layers |= MODE_LAYER;
|
||||
else if (arg_match(&arg, &dump_uv_mode_arg, argi))
|
||||
layers |= UV_MODE_LAYER;
|
||||
else if (arg_match(&arg, &dump_skip_arg, argi))
|
||||
layers |= SKIP_LAYER;
|
||||
else if (arg_match(&arg, &dump_filter_arg, argi))
|
||||
layers |= FILTER_LAYER;
|
||||
#if CONFIG_CDEF
|
||||
else if (arg_match(&arg, &dump_cdef_arg, argi))
|
||||
layers |= CDEF_LAYER;
|
||||
#endif
|
||||
else if (arg_match(&arg, &dump_reference_frame_arg, argi))
|
||||
layers |= REFERENCE_FRAME_LAYER;
|
||||
else if (arg_match(&arg, &dump_motion_vectors_arg, argi))
|
||||
layers |= MOTION_VECTORS_LAYER;
|
||||
else if (arg_match(&arg, &dump_all_arg, argi))
|
||||
layers |= ALL_LAYERS;
|
||||
else if (arg_match(&arg, &compress_arg, argi))
|
||||
compress = 1;
|
||||
else if (arg_match(&arg, &usage_arg, argi))
|
||||
usage_exit();
|
||||
else if (arg_match(&arg, &limit_arg, argi))
|
||||
stop_after = arg_parse_uint(&arg);
|
||||
else
|
||||
argj++;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "Usage: %s src_filename <options>\n", exec_name);
|
||||
fprintf(stderr, "\nOptions:\n");
|
||||
arg_show_usage(stderr, main_args);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int main(int argc, char **argv) {
|
||||
exec_name = argv[0];
|
||||
parse_args(argv);
|
||||
if (argc >= 2) {
|
||||
open_file(argv[1]);
|
||||
printf("[\n");
|
||||
while (1) {
|
||||
if (stop_after && (decoded_frame_count >= stop_after)) break;
|
||||
if (read_frame()) break;
|
||||
}
|
||||
printf("null\n");
|
||||
printf("]");
|
||||
} else {
|
||||
usage_exit();
|
||||
}
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void quit() {
|
||||
if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec");
|
||||
aom_video_reader_close(reader);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void set_layers(LayerType v) { layers = v; }
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void set_compress(int v) { compress = v; }
|
||||
139
third_party/aom/examples/lossless_encoder.c
vendored
Normal file
139
third_party/aom/examples/lossless_encoder.c
vendored
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aom/aom_encoder.h"
|
||||
#include "aom/aomcx.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr,
|
||||
"lossless_encoder: Example demonstrating lossless "
|
||||
"encoding feature. Supports raw input only.\n");
|
||||
fprintf(stderr, "Usage: %s <width> <height> <infile> <outfile>\n", exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int encode_frame(aom_codec_ctx_t *codec, aom_image_t *img,
|
||||
int frame_index, int flags, AvxVideoWriter *writer) {
|
||||
int got_pkts = 0;
|
||||
aom_codec_iter_t iter = NULL;
|
||||
const aom_codec_cx_pkt_t *pkt = NULL;
|
||||
const aom_codec_err_t res =
|
||||
aom_codec_encode(codec, img, frame_index, 1, flags, AOM_DL_GOOD_QUALITY);
|
||||
if (res != AOM_CODEC_OK) die_codec(codec, "Failed to encode frame");
|
||||
|
||||
while ((pkt = aom_codec_get_cx_data(codec, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
|
||||
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
|
||||
const int keyframe = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0;
|
||||
if (!aom_video_writer_write_frame(writer, pkt->data.frame.buf,
|
||||
pkt->data.frame.sz,
|
||||
pkt->data.frame.pts)) {
|
||||
die_codec(codec, "Failed to write compressed frame");
|
||||
}
|
||||
printf(keyframe ? "K" : ".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile = NULL;
|
||||
aom_codec_ctx_t codec;
|
||||
aom_codec_enc_cfg_t cfg;
|
||||
int frame_count = 0;
|
||||
aom_image_t raw;
|
||||
aom_codec_err_t res;
|
||||
AvxVideoInfo info;
|
||||
AvxVideoWriter *writer = NULL;
|
||||
const AvxInterface *encoder = NULL;
|
||||
const int fps = 30;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
// Clear explicitly, as simply assigning "{ 0 }" generates
|
||||
// "missing-field-initializers" warning in some compilers.
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
if (argc < 5) die("Invalid number of arguments");
|
||||
|
||||
encoder = get_aom_encoder_by_name("av1");
|
||||
if (!encoder) die("Unsupported codec.");
|
||||
|
||||
info.codec_fourcc = encoder->fourcc;
|
||||
info.frame_width = (int)strtol(argv[1], NULL, 0);
|
||||
info.frame_height = (int)strtol(argv[2], NULL, 0);
|
||||
info.time_base.numerator = 1;
|
||||
info.time_base.denominator = fps;
|
||||
|
||||
if (info.frame_width <= 0 || info.frame_height <= 0 ||
|
||||
(info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
|
||||
die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
|
||||
}
|
||||
|
||||
if (!aom_img_alloc(&raw, AOM_IMG_FMT_I420, info.frame_width,
|
||||
info.frame_height, 1)) {
|
||||
die("Failed to allocate image.");
|
||||
}
|
||||
|
||||
printf("Using %s\n", aom_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
res = aom_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) die_codec(&codec, "Failed to get default codec config.");
|
||||
|
||||
cfg.g_w = info.frame_width;
|
||||
cfg.g_h = info.frame_height;
|
||||
cfg.g_timebase.num = info.time_base.numerator;
|
||||
cfg.g_timebase.den = info.time_base.denominator;
|
||||
|
||||
writer = aom_video_writer_open(argv[4], kContainerIVF, &info);
|
||||
if (!writer) die("Failed to open %s for writing.", argv[4]);
|
||||
|
||||
if (!(infile = fopen(argv[3], "rb")))
|
||||
die("Failed to open %s for reading.", argv[3]);
|
||||
|
||||
if (aom_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
if (aom_codec_control_(&codec, AV1E_SET_LOSSLESS, 1))
|
||||
die_codec(&codec, "Failed to use lossless mode");
|
||||
|
||||
// Encode frames.
|
||||
while (aom_img_read(&raw, infile)) {
|
||||
encode_frame(&codec, &raw, frame_count++, 0, writer);
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
while (encode_frame(&codec, NULL, -1, 0, writer)) {
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fclose(infile);
|
||||
printf("Processed %d frames.\n", frame_count);
|
||||
|
||||
aom_img_free(&raw);
|
||||
if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
aom_video_writer_close(writer);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
124
third_party/aom/examples/resize_util.c
vendored
Normal file
124
third_party/aom/examples/resize_util.c
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../av1/encoder/av1_resize.h"
|
||||
|
||||
static const char *exec_name = NULL;
|
||||
|
||||
static void usage() {
|
||||
printf("Usage:\n");
|
||||
printf("%s <input_yuv> <width>x<height> <target_width>x<target_height> ",
|
||||
exec_name);
|
||||
printf("<output_yuv> [<frames>]\n");
|
||||
}
|
||||
|
||||
void usage_exit(void) {
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int parse_dim(char *v, int *width, int *height) {
|
||||
char *x = strchr(v, 'x');
|
||||
if (x == NULL) x = strchr(v, 'X');
|
||||
if (x == NULL) return 0;
|
||||
*width = atoi(v);
|
||||
*height = atoi(&x[1]);
|
||||
if (*width <= 0 || *height <= 0)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char *fin, *fout;
|
||||
FILE *fpin, *fpout;
|
||||
uint8_t *inbuf, *outbuf;
|
||||
uint8_t *inbuf_u, *outbuf_u;
|
||||
uint8_t *inbuf_v, *outbuf_v;
|
||||
int f, frames;
|
||||
int width, height, target_width, target_height;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc < 5) {
|
||||
printf("Incorrect parameters:\n");
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
fin = argv[1];
|
||||
fout = argv[4];
|
||||
if (!parse_dim(argv[2], &width, &height)) {
|
||||
printf("Incorrect parameters: %s\n", argv[2]);
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
if (!parse_dim(argv[3], &target_width, &target_height)) {
|
||||
printf("Incorrect parameters: %s\n", argv[3]);
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
fpin = fopen(fin, "rb");
|
||||
if (fpin == NULL) {
|
||||
printf("Can't open file %s to read\n", fin);
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
fpout = fopen(fout, "wb");
|
||||
if (fpout == NULL) {
|
||||
printf("Can't open file %s to write\n", fout);
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
if (argc >= 6)
|
||||
frames = atoi(argv[5]);
|
||||
else
|
||||
frames = INT_MAX;
|
||||
|
||||
printf("Input size: %dx%d\n", width, height);
|
||||
printf("Target size: %dx%d, Frames: ", target_width, target_height);
|
||||
if (frames == INT_MAX)
|
||||
printf("All\n");
|
||||
else
|
||||
printf("%d\n", frames);
|
||||
|
||||
inbuf = (uint8_t *)malloc(width * height * 3 / 2);
|
||||
outbuf = (uint8_t *)malloc(target_width * target_height * 3 / 2);
|
||||
inbuf_u = inbuf + width * height;
|
||||
inbuf_v = inbuf_u + width * height / 4;
|
||||
outbuf_u = outbuf + target_width * target_height;
|
||||
outbuf_v = outbuf_u + target_width * target_height / 4;
|
||||
f = 0;
|
||||
while (f < frames) {
|
||||
if (fread(inbuf, width * height * 3 / 2, 1, fpin) != 1) break;
|
||||
av1_resize_frame420(inbuf, width, inbuf_u, inbuf_v, width / 2, height,
|
||||
width, outbuf, target_width, outbuf_u, outbuf_v,
|
||||
target_width / 2, target_height, target_width);
|
||||
fwrite(outbuf, target_width * target_height * 3 / 2, 1, fpout);
|
||||
f++;
|
||||
}
|
||||
printf("%d frames processed\n", f);
|
||||
fclose(fpin);
|
||||
fclose(fpout);
|
||||
|
||||
free(inbuf);
|
||||
free(outbuf);
|
||||
return 0;
|
||||
}
|
||||
210
third_party/aom/examples/set_maps.c
vendored
Normal file
210
third_party/aom/examples/set_maps.c
vendored
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
// AOM Set Active and ROI Maps
|
||||
// ===========================
|
||||
//
|
||||
// This is an example demonstrating how to control the AOM encoder's
|
||||
// ROI and Active maps.
|
||||
//
|
||||
// ROI (Reigon of Interest) maps are a way for the application to assign
|
||||
// each macroblock in the image to a region, and then set quantizer and
|
||||
// filtering parameters on that image.
|
||||
//
|
||||
// Active maps are a way for the application to specify on a
|
||||
// macroblock-by-macroblock basis whether there is any activity in that
|
||||
// macroblock.
|
||||
//
|
||||
//
|
||||
// Configuration
|
||||
// -------------
|
||||
// An ROI map is set on frame 22. If the width of the image in macroblocks
|
||||
// is evenly divisble by 4, then the output will appear to have distinct
|
||||
// columns, where the quantizer, loopfilter, and static threshold differ
|
||||
// from column to column.
|
||||
//
|
||||
// An active map is set on frame 33. If the width of the image in macroblocks
|
||||
// is evenly divisble by 4, then the output will appear to have distinct
|
||||
// columns, where one column will have motion and the next will not.
|
||||
//
|
||||
// The active map is cleared on frame 44.
|
||||
//
|
||||
// Observing The Effects
|
||||
// ---------------------
|
||||
// Use the `simple_decoder` example to decode this sample, and observe
|
||||
// the change in the image at frames 22, 33, and 44.
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aom/aomcx.h"
|
||||
#include "aom/aom_encoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "Usage: %s <codec> <width> <height> <infile> <outfile>\n",
|
||||
exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void set_active_map(const aom_codec_enc_cfg_t *cfg,
|
||||
aom_codec_ctx_t *codec) {
|
||||
unsigned int i;
|
||||
aom_active_map_t map = { 0, 0, 0 };
|
||||
|
||||
map.rows = (cfg->g_h + 15) / 16;
|
||||
map.cols = (cfg->g_w + 15) / 16;
|
||||
|
||||
map.active_map = (uint8_t *)malloc(map.rows * map.cols);
|
||||
for (i = 0; i < map.rows * map.cols; ++i) map.active_map[i] = i % 2;
|
||||
|
||||
if (aom_codec_control(codec, AOME_SET_ACTIVEMAP, &map))
|
||||
die_codec(codec, "Failed to set active map");
|
||||
|
||||
free(map.active_map);
|
||||
}
|
||||
|
||||
static void unset_active_map(const aom_codec_enc_cfg_t *cfg,
|
||||
aom_codec_ctx_t *codec) {
|
||||
aom_active_map_t map = { 0, 0, 0 };
|
||||
|
||||
map.rows = (cfg->g_h + 15) / 16;
|
||||
map.cols = (cfg->g_w + 15) / 16;
|
||||
map.active_map = NULL;
|
||||
|
||||
if (aom_codec_control(codec, AOME_SET_ACTIVEMAP, &map))
|
||||
die_codec(codec, "Failed to set active map");
|
||||
}
|
||||
|
||||
static int encode_frame(aom_codec_ctx_t *codec, aom_image_t *img,
|
||||
int frame_index, AvxVideoWriter *writer) {
|
||||
int got_pkts = 0;
|
||||
aom_codec_iter_t iter = NULL;
|
||||
const aom_codec_cx_pkt_t *pkt = NULL;
|
||||
const aom_codec_err_t res =
|
||||
aom_codec_encode(codec, img, frame_index, 1, 0, AOM_DL_GOOD_QUALITY);
|
||||
if (res != AOM_CODEC_OK) die_codec(codec, "Failed to encode frame");
|
||||
|
||||
while ((pkt = aom_codec_get_cx_data(codec, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
|
||||
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
|
||||
const int keyframe = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0;
|
||||
if (!aom_video_writer_write_frame(writer, pkt->data.frame.buf,
|
||||
pkt->data.frame.sz,
|
||||
pkt->data.frame.pts)) {
|
||||
die_codec(codec, "Failed to write compressed frame");
|
||||
}
|
||||
|
||||
printf(keyframe ? "K" : ".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile = NULL;
|
||||
aom_codec_ctx_t codec;
|
||||
aom_codec_enc_cfg_t cfg;
|
||||
int frame_count = 0;
|
||||
const int limit = 30;
|
||||
aom_image_t raw;
|
||||
aom_codec_err_t res;
|
||||
AvxVideoInfo info;
|
||||
AvxVideoWriter *writer = NULL;
|
||||
const AvxInterface *encoder = NULL;
|
||||
const int fps = 2; // TODO(dkovalev) add command line argument
|
||||
const double bits_per_pixel_per_frame = 0.067;
|
||||
|
||||
exec_name = argv[0];
|
||||
if (argc != 6) die("Invalid number of arguments");
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
encoder = get_aom_encoder_by_name(argv[1]);
|
||||
if (encoder == NULL) {
|
||||
die("Unsupported codec.");
|
||||
}
|
||||
assert(encoder != NULL);
|
||||
info.codec_fourcc = encoder->fourcc;
|
||||
info.frame_width = (int)strtol(argv[2], NULL, 0);
|
||||
info.frame_height = (int)strtol(argv[3], NULL, 0);
|
||||
info.time_base.numerator = 1;
|
||||
info.time_base.denominator = fps;
|
||||
|
||||
if (info.frame_width <= 0 || info.frame_height <= 0 ||
|
||||
(info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
|
||||
die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
|
||||
}
|
||||
|
||||
if (!aom_img_alloc(&raw, AOM_IMG_FMT_I420, info.frame_width,
|
||||
info.frame_height, 1)) {
|
||||
die("Failed to allocate image.");
|
||||
}
|
||||
|
||||
printf("Using %s\n", aom_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
res = aom_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) die_codec(&codec, "Failed to get default codec config.");
|
||||
|
||||
cfg.g_w = info.frame_width;
|
||||
cfg.g_h = info.frame_height;
|
||||
cfg.g_timebase.num = info.time_base.numerator;
|
||||
cfg.g_timebase.den = info.time_base.denominator;
|
||||
cfg.rc_target_bitrate =
|
||||
(unsigned int)(bits_per_pixel_per_frame * cfg.g_w * cfg.g_h * fps / 1000);
|
||||
cfg.g_lag_in_frames = 0;
|
||||
|
||||
writer = aom_video_writer_open(argv[5], kContainerIVF, &info);
|
||||
if (!writer) die("Failed to open %s for writing.", argv[5]);
|
||||
|
||||
if (!(infile = fopen(argv[4], "rb")))
|
||||
die("Failed to open %s for reading.", argv[4]);
|
||||
|
||||
if (aom_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
// Encode frames.
|
||||
while (aom_img_read(&raw, infile) && frame_count < limit) {
|
||||
++frame_count;
|
||||
|
||||
if (frame_count == 11) {
|
||||
set_active_map(&cfg, &codec);
|
||||
} else if (frame_count == 22) {
|
||||
unset_active_map(&cfg, &codec);
|
||||
}
|
||||
|
||||
encode_frame(&codec, &raw, frame_count, writer);
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
while (encode_frame(&codec, NULL, -1, writer)) {
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fclose(infile);
|
||||
printf("Processed %d frames.\n", frame_count);
|
||||
|
||||
aom_img_free(&raw);
|
||||
if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
aom_video_writer_close(writer);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
150
third_party/aom/examples/simple_decoder.c
vendored
Normal file
150
third_party/aom/examples/simple_decoder.c
vendored
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
// Simple Decoder
|
||||
// ==============
|
||||
//
|
||||
// This is an example of a simple decoder loop. It takes an input file
|
||||
// containing the compressed data (in IVF format), passes it through the
|
||||
// decoder, and writes the decompressed frames to disk. Other decoder
|
||||
// examples build upon this one.
|
||||
//
|
||||
// The details of the IVF format have been elided from this example for
|
||||
// simplicity of presentation, as IVF files will not generally be used by
|
||||
// your application. In general, an IVF file consists of a file header,
|
||||
// followed by a variable number of frames. Each frame consists of a frame
|
||||
// header followed by a variable length payload. The length of the payload
|
||||
// is specified in the first four bytes of the frame header. The payload is
|
||||
// the raw compressed data.
|
||||
//
|
||||
// Standard Includes
|
||||
// -----------------
|
||||
// For decoders, you only have to include `aom_decoder.h` and then any
|
||||
// header files for the specific codecs you use. In this case, we're using
|
||||
// aom.
|
||||
//
|
||||
// Initializing The Codec
|
||||
// ----------------------
|
||||
// The libaom decoder is initialized by the call to aom_codec_dec_init().
|
||||
// Determining the codec interface to use is handled by AvxVideoReader and the
|
||||
// functions prefixed with aom_video_reader_. Discussion of those functions is
|
||||
// beyond the scope of this example, but the main gist is to open the input file
|
||||
// and parse just enough of it to determine if it's a AVx file and which AVx
|
||||
// codec is contained within the file.
|
||||
// Note the NULL pointer passed to aom_codec_dec_init(). We do that in this
|
||||
// example because we want the algorithm to determine the stream configuration
|
||||
// (width/height) and allocate memory automatically.
|
||||
//
|
||||
// Decoding A Frame
|
||||
// ----------------
|
||||
// Once the frame has been read into memory, it is decoded using the
|
||||
// `aom_codec_decode` function. The call takes a pointer to the data
|
||||
// (`frame`) and the length of the data (`frame_size`). No application data
|
||||
// is associated with the frame in this example, so the `user_priv`
|
||||
// parameter is NULL. The `deadline` parameter is left at zero for this
|
||||
// example. This parameter is generally only used when doing adaptive post
|
||||
// processing.
|
||||
//
|
||||
// Codecs may produce a variable number of output frames for every call to
|
||||
// `aom_codec_decode`. These frames are retrieved by the
|
||||
// `aom_codec_get_frame` iterator function. The iterator variable `iter` is
|
||||
// initialized to NULL each time `aom_codec_decode` is called.
|
||||
// `aom_codec_get_frame` is called in a loop, returning a pointer to a
|
||||
// decoded image or NULL to indicate the end of list.
|
||||
//
|
||||
// Processing The Decoded Data
|
||||
// ---------------------------
|
||||
// In this example, we simply write the encoded data to disk. It is
|
||||
// important to honor the image's `stride` values.
|
||||
//
|
||||
// Cleanup
|
||||
// -------
|
||||
// The `aom_codec_destroy` call frees any memory allocated by the codec.
|
||||
//
|
||||
// Error Handling
|
||||
// --------------
|
||||
// This example does not special case any error return codes. If there was
|
||||
// an error, a descriptive message is printed and the program exits. With
|
||||
// few exceptions, aom_codec functions return an enumerated error status,
|
||||
// with the value `0` indicating success.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aom/aom_decoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_reader.h"
|
||||
#include "./aom_config.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "Usage: %s <infile> <outfile>\n", exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int frame_cnt = 0;
|
||||
FILE *outfile = NULL;
|
||||
aom_codec_ctx_t codec;
|
||||
AvxVideoReader *reader = NULL;
|
||||
const AvxInterface *decoder = NULL;
|
||||
const AvxVideoInfo *info = NULL;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc != 3) die("Invalid number of arguments.");
|
||||
|
||||
reader = aom_video_reader_open(argv[1]);
|
||||
if (!reader) die("Failed to open %s for reading.", argv[1]);
|
||||
|
||||
if (!(outfile = fopen(argv[2], "wb")))
|
||||
die("Failed to open %s for writing.", argv[2]);
|
||||
|
||||
info = aom_video_reader_get_info(reader);
|
||||
|
||||
decoder = get_aom_decoder_by_fourcc(info->codec_fourcc);
|
||||
if (!decoder) die("Unknown input codec.");
|
||||
|
||||
printf("Using %s\n", aom_codec_iface_name(decoder->codec_interface()));
|
||||
|
||||
if (aom_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0))
|
||||
die_codec(&codec, "Failed to initialize decoder.");
|
||||
|
||||
while (aom_video_reader_read_frame(reader)) {
|
||||
aom_codec_iter_t iter = NULL;
|
||||
aom_image_t *img = NULL;
|
||||
size_t frame_size = 0;
|
||||
const unsigned char *frame =
|
||||
aom_video_reader_get_frame(reader, &frame_size);
|
||||
if (aom_codec_decode(&codec, frame, (unsigned int)frame_size, NULL, 0))
|
||||
die_codec(&codec, "Failed to decode frame.");
|
||||
|
||||
while ((img = aom_codec_get_frame(&codec, &iter)) != NULL) {
|
||||
aom_img_write(img, outfile);
|
||||
++frame_cnt;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Processed %d frames.\n", frame_cnt);
|
||||
if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec");
|
||||
|
||||
printf("Play: ffplay -f rawvideo -pix_fmt yuv420p -s %dx%d %s\n",
|
||||
info->frame_width, info->frame_height, argv[2]);
|
||||
|
||||
aom_video_reader_close(reader);
|
||||
|
||||
fclose(outfile);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
250
third_party/aom/examples/simple_encoder.c
vendored
Normal file
250
third_party/aom/examples/simple_encoder.c
vendored
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
// Simple Encoder
|
||||
// ==============
|
||||
//
|
||||
// This is an example of a simple encoder loop. It takes an input file in
|
||||
// YV12 format, passes it through the encoder, and writes the compressed
|
||||
// frames to disk in IVF format. Other decoder examples build upon this
|
||||
// one.
|
||||
//
|
||||
// The details of the IVF format have been elided from this example for
|
||||
// simplicity of presentation, as IVF files will not generally be used by
|
||||
// your application. In general, an IVF file consists of a file header,
|
||||
// followed by a variable number of frames. Each frame consists of a frame
|
||||
// header followed by a variable length payload. The length of the payload
|
||||
// is specified in the first four bytes of the frame header. The payload is
|
||||
// the raw compressed data.
|
||||
//
|
||||
// Standard Includes
|
||||
// -----------------
|
||||
// For encoders, you only have to include `aom_encoder.h` and then any
|
||||
// header files for the specific codecs you use. In this case, we're using
|
||||
// aom.
|
||||
//
|
||||
// Getting The Default Configuration
|
||||
// ---------------------------------
|
||||
// Encoders have the notion of "usage profiles." For example, an encoder
|
||||
// may want to publish default configurations for both a video
|
||||
// conferencing application and a best quality offline encoder. These
|
||||
// obviously have very different default settings. Consult the
|
||||
// documentation for your codec to see if it provides any default
|
||||
// configurations. All codecs provide a default configuration, number 0,
|
||||
// which is valid for material in the vacinity of QCIF/QVGA.
|
||||
//
|
||||
// Updating The Configuration
|
||||
// ---------------------------------
|
||||
// Almost all applications will want to update the default configuration
|
||||
// with settings specific to their usage. Here we set the width and height
|
||||
// of the video file to that specified on the command line. We also scale
|
||||
// the default bitrate based on the ratio between the default resolution
|
||||
// and the resolution specified on the command line.
|
||||
//
|
||||
// Initializing The Codec
|
||||
// ----------------------
|
||||
// The encoder is initialized by the following code.
|
||||
//
|
||||
// Encoding A Frame
|
||||
// ----------------
|
||||
// The frame is read as a continuous block (size width * height * 3 / 2)
|
||||
// from the input file. If a frame was read (the input file has not hit
|
||||
// EOF) then the frame is passed to the encoder. Otherwise, a NULL
|
||||
// is passed, indicating the End-Of-Stream condition to the encoder. The
|
||||
// `frame_cnt` is reused as the presentation time stamp (PTS) and each
|
||||
// frame is shown for one frame-time in duration. The flags parameter is
|
||||
// unused in this example.
|
||||
|
||||
// Forced Keyframes
|
||||
// ----------------
|
||||
// Keyframes can be forced by setting the AOM_EFLAG_FORCE_KF bit of the
|
||||
// flags passed to `aom_codec_control()`. In this example, we force a
|
||||
// keyframe every <keyframe-interval> frames. Note, the output stream can
|
||||
// contain additional keyframes beyond those that have been forced using the
|
||||
// AOM_EFLAG_FORCE_KF flag because of automatic keyframe placement by the
|
||||
// encoder.
|
||||
//
|
||||
// Processing The Encoded Data
|
||||
// ---------------------------
|
||||
// Each packet of type `AOM_CODEC_CX_FRAME_PKT` contains the encoded data
|
||||
// for this frame. We write a IVF frame header, followed by the raw data.
|
||||
//
|
||||
// Cleanup
|
||||
// -------
|
||||
// The `aom_codec_destroy` call frees any memory allocated by the codec.
|
||||
//
|
||||
// Error Handling
|
||||
// --------------
|
||||
// This example does not special case any error return codes. If there was
|
||||
// an error, a descriptive message is printed and the program exits. With
|
||||
// few exeptions, aom_codec functions return an enumerated error status,
|
||||
// with the value `0` indicating success.
|
||||
//
|
||||
// Error Resiliency Features
|
||||
// -------------------------
|
||||
// Error resiliency is controlled by the g_error_resilient member of the
|
||||
// configuration structure. Use the `decode_with_drops` example to decode with
|
||||
// frames 5-10 dropped. Compare the output for a file encoded with this example
|
||||
// versus one encoded with the `simple_encoder` example.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aom/aom_encoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <codec> <width> <height> <infile> <outfile> "
|
||||
"<keyframe-interval> <error-resilient> <frames to encode>\n"
|
||||
"See comments in simple_encoder.c for more information.\n",
|
||||
exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int encode_frame(aom_codec_ctx_t *codec, aom_image_t *img,
|
||||
int frame_index, int flags, AvxVideoWriter *writer) {
|
||||
int got_pkts = 0;
|
||||
aom_codec_iter_t iter = NULL;
|
||||
const aom_codec_cx_pkt_t *pkt = NULL;
|
||||
const aom_codec_err_t res =
|
||||
aom_codec_encode(codec, img, frame_index, 1, flags, AOM_DL_GOOD_QUALITY);
|
||||
if (res != AOM_CODEC_OK) die_codec(codec, "Failed to encode frame");
|
||||
|
||||
while ((pkt = aom_codec_get_cx_data(codec, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
|
||||
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
|
||||
const int keyframe = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0;
|
||||
if (!aom_video_writer_write_frame(writer, pkt->data.frame.buf,
|
||||
pkt->data.frame.sz,
|
||||
pkt->data.frame.pts)) {
|
||||
die_codec(codec, "Failed to write compressed frame");
|
||||
}
|
||||
printf(keyframe ? "K" : ".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
// TODO(tomfinegan): Improve command line parsing and add args for bitrate/fps.
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile = NULL;
|
||||
aom_codec_ctx_t codec;
|
||||
aom_codec_enc_cfg_t cfg;
|
||||
int frame_count = 0;
|
||||
aom_image_t raw;
|
||||
aom_codec_err_t res;
|
||||
AvxVideoInfo info;
|
||||
AvxVideoWriter *writer = NULL;
|
||||
const AvxInterface *encoder = NULL;
|
||||
const int fps = 30;
|
||||
const int bitrate = 200;
|
||||
int keyframe_interval = 0;
|
||||
int max_frames = 0;
|
||||
int frames_encoded = 0;
|
||||
const char *codec_arg = NULL;
|
||||
const char *width_arg = NULL;
|
||||
const char *height_arg = NULL;
|
||||
const char *infile_arg = NULL;
|
||||
const char *outfile_arg = NULL;
|
||||
const char *keyframe_interval_arg = NULL;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
// Clear explicitly, as simply assigning "{ 0 }" generates
|
||||
// "missing-field-initializers" warning in some compilers.
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
if (argc != 9) die("Invalid number of arguments");
|
||||
|
||||
codec_arg = argv[1];
|
||||
width_arg = argv[2];
|
||||
height_arg = argv[3];
|
||||
infile_arg = argv[4];
|
||||
outfile_arg = argv[5];
|
||||
keyframe_interval_arg = argv[6];
|
||||
max_frames = (int)strtol(argv[8], NULL, 0);
|
||||
|
||||
encoder = get_aom_encoder_by_name(codec_arg);
|
||||
if (!encoder) die("Unsupported codec.");
|
||||
|
||||
info.codec_fourcc = encoder->fourcc;
|
||||
info.frame_width = (int)strtol(width_arg, NULL, 0);
|
||||
info.frame_height = (int)strtol(height_arg, NULL, 0);
|
||||
info.time_base.numerator = 1;
|
||||
info.time_base.denominator = fps;
|
||||
|
||||
if (info.frame_width <= 0 || info.frame_height <= 0 ||
|
||||
(info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
|
||||
die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
|
||||
}
|
||||
|
||||
if (!aom_img_alloc(&raw, AOM_IMG_FMT_I420, info.frame_width,
|
||||
info.frame_height, 1)) {
|
||||
die("Failed to allocate image.");
|
||||
}
|
||||
|
||||
keyframe_interval = (int)strtol(keyframe_interval_arg, NULL, 0);
|
||||
if (keyframe_interval < 0) die("Invalid keyframe interval value.");
|
||||
|
||||
printf("Using %s\n", aom_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
res = aom_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) die_codec(&codec, "Failed to get default codec config.");
|
||||
|
||||
cfg.g_w = info.frame_width;
|
||||
cfg.g_h = info.frame_height;
|
||||
cfg.g_timebase.num = info.time_base.numerator;
|
||||
cfg.g_timebase.den = info.time_base.denominator;
|
||||
cfg.rc_target_bitrate = bitrate;
|
||||
cfg.g_error_resilient = (aom_codec_er_flags_t)strtoul(argv[7], NULL, 0);
|
||||
|
||||
writer = aom_video_writer_open(outfile_arg, kContainerIVF, &info);
|
||||
if (!writer) die("Failed to open %s for writing.", outfile_arg);
|
||||
|
||||
if (!(infile = fopen(infile_arg, "rb")))
|
||||
die("Failed to open %s for reading.", infile_arg);
|
||||
|
||||
if (aom_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
// Encode frames.
|
||||
while (aom_img_read(&raw, infile)) {
|
||||
int flags = 0;
|
||||
if (keyframe_interval > 0 && frame_count % keyframe_interval == 0)
|
||||
flags |= AOM_EFLAG_FORCE_KF;
|
||||
encode_frame(&codec, &raw, frame_count++, flags, writer);
|
||||
frames_encoded++;
|
||||
if (max_frames > 0 && frames_encoded >= max_frames) break;
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
while (encode_frame(&codec, NULL, -1, 0, writer)) continue;
|
||||
|
||||
printf("\n");
|
||||
fclose(infile);
|
||||
printf("Processed %d frames.\n", frame_count);
|
||||
|
||||
aom_img_free(&raw);
|
||||
if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
aom_video_writer_close(writer);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
255
third_party/aom/examples/twopass_encoder.c
vendored
Normal file
255
third_party/aom/examples/twopass_encoder.c
vendored
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
// Two Pass Encoder
|
||||
// ================
|
||||
//
|
||||
// This is an example of a two pass encoder loop. It takes an input file in
|
||||
// YV12 format, passes it through the encoder twice, and writes the compressed
|
||||
// frames to disk in IVF format. It builds upon the simple_encoder example.
|
||||
//
|
||||
// Twopass Variables
|
||||
// -----------------
|
||||
// Twopass mode needs to track the current pass number and the buffer of
|
||||
// statistics packets.
|
||||
//
|
||||
// Updating The Configuration
|
||||
// ---------------------------------
|
||||
// In two pass mode, the configuration has to be updated on each pass. The
|
||||
// statistics buffer is passed on the last pass.
|
||||
//
|
||||
// Encoding A Frame
|
||||
// ----------------
|
||||
// Encoding a frame in two pass mode is identical to the simple encoder
|
||||
// example.
|
||||
//
|
||||
// Processing Statistics Packets
|
||||
// -----------------------------
|
||||
// Each packet of type `AOM_CODEC_CX_FRAME_PKT` contains the encoded data
|
||||
// for this frame. We write a IVF frame header, followed by the raw data.
|
||||
//
|
||||
//
|
||||
// Pass Progress Reporting
|
||||
// -----------------------------
|
||||
// It's sometimes helpful to see when each pass completes.
|
||||
//
|
||||
//
|
||||
// Clean-up
|
||||
// -----------------------------
|
||||
// Destruction of the encoder instance must be done on each pass. The
|
||||
// raw image should be destroyed at the end as usual.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aom/aom_encoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <codec> <width> <height> <infile> <outfile> "
|
||||
"<limit(optional)>\n",
|
||||
exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int get_frame_stats(aom_codec_ctx_t *ctx, const aom_image_t *img,
|
||||
aom_codec_pts_t pts, unsigned int duration,
|
||||
aom_enc_frame_flags_t flags, unsigned int deadline,
|
||||
aom_fixed_buf_t *stats) {
|
||||
int got_pkts = 0;
|
||||
aom_codec_iter_t iter = NULL;
|
||||
const aom_codec_cx_pkt_t *pkt = NULL;
|
||||
const aom_codec_err_t res =
|
||||
aom_codec_encode(ctx, img, pts, duration, flags, deadline);
|
||||
if (res != AOM_CODEC_OK) die_codec(ctx, "Failed to get frame stats.");
|
||||
|
||||
while ((pkt = aom_codec_get_cx_data(ctx, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
|
||||
if (pkt->kind == AOM_CODEC_STATS_PKT) {
|
||||
const uint8_t *const pkt_buf = pkt->data.twopass_stats.buf;
|
||||
const size_t pkt_size = pkt->data.twopass_stats.sz;
|
||||
stats->buf = realloc(stats->buf, stats->sz + pkt_size);
|
||||
memcpy((uint8_t *)stats->buf + stats->sz, pkt_buf, pkt_size);
|
||||
stats->sz += pkt_size;
|
||||
}
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
static int encode_frame(aom_codec_ctx_t *ctx, const aom_image_t *img,
|
||||
aom_codec_pts_t pts, unsigned int duration,
|
||||
aom_enc_frame_flags_t flags, unsigned int deadline,
|
||||
AvxVideoWriter *writer) {
|
||||
int got_pkts = 0;
|
||||
aom_codec_iter_t iter = NULL;
|
||||
const aom_codec_cx_pkt_t *pkt = NULL;
|
||||
const aom_codec_err_t res =
|
||||
aom_codec_encode(ctx, img, pts, duration, flags, deadline);
|
||||
if (res != AOM_CODEC_OK) die_codec(ctx, "Failed to encode frame.");
|
||||
|
||||
while ((pkt = aom_codec_get_cx_data(ctx, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
|
||||
const int keyframe = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0;
|
||||
|
||||
if (!aom_video_writer_write_frame(writer, pkt->data.frame.buf,
|
||||
pkt->data.frame.sz,
|
||||
pkt->data.frame.pts))
|
||||
die_codec(ctx, "Failed to write compressed frame.");
|
||||
printf(keyframe ? "K" : ".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
static aom_fixed_buf_t pass0(aom_image_t *raw, FILE *infile,
|
||||
const AvxInterface *encoder,
|
||||
const aom_codec_enc_cfg_t *cfg, int limit) {
|
||||
aom_codec_ctx_t codec;
|
||||
int frame_count = 0;
|
||||
aom_fixed_buf_t stats = { NULL, 0 };
|
||||
|
||||
if (aom_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
// Calculate frame statistics.
|
||||
while (aom_img_read(raw, infile) && frame_count < limit) {
|
||||
++frame_count;
|
||||
get_frame_stats(&codec, raw, frame_count, 1, 0, AOM_DL_GOOD_QUALITY,
|
||||
&stats);
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
while (get_frame_stats(&codec, NULL, frame_count, 1, 0, AOM_DL_GOOD_QUALITY,
|
||||
&stats)) {
|
||||
}
|
||||
|
||||
printf("Pass 0 complete. Processed %d frames.\n", frame_count);
|
||||
if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
static void pass1(aom_image_t *raw, FILE *infile, const char *outfile_name,
|
||||
const AvxInterface *encoder, const aom_codec_enc_cfg_t *cfg,
|
||||
int limit) {
|
||||
AvxVideoInfo info = { encoder->fourcc,
|
||||
cfg->g_w,
|
||||
cfg->g_h,
|
||||
{ cfg->g_timebase.num, cfg->g_timebase.den } };
|
||||
AvxVideoWriter *writer = NULL;
|
||||
aom_codec_ctx_t codec;
|
||||
int frame_count = 0;
|
||||
|
||||
writer = aom_video_writer_open(outfile_name, kContainerIVF, &info);
|
||||
if (!writer) die("Failed to open %s for writing", outfile_name);
|
||||
|
||||
if (aom_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
// Encode frames.
|
||||
while (aom_img_read(raw, infile) && frame_count < limit) {
|
||||
++frame_count;
|
||||
encode_frame(&codec, raw, frame_count, 1, 0, AOM_DL_GOOD_QUALITY, writer);
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
while (encode_frame(&codec, NULL, -1, 1, 0, AOM_DL_GOOD_QUALITY, writer)) {
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
aom_video_writer_close(writer);
|
||||
|
||||
printf("Pass 1 complete. Processed %d frames.\n", frame_count);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile = NULL;
|
||||
int w, h;
|
||||
aom_codec_ctx_t codec;
|
||||
aom_codec_enc_cfg_t cfg;
|
||||
aom_image_t raw;
|
||||
aom_codec_err_t res;
|
||||
aom_fixed_buf_t stats;
|
||||
|
||||
const AvxInterface *encoder = NULL;
|
||||
const int fps = 30; // TODO(dkovalev) add command line argument
|
||||
const int bitrate = 200; // kbit/s TODO(dkovalev) add command line argument
|
||||
const char *const codec_arg = argv[1];
|
||||
const char *const width_arg = argv[2];
|
||||
const char *const height_arg = argv[3];
|
||||
const char *const infile_arg = argv[4];
|
||||
const char *const outfile_arg = argv[5];
|
||||
int limit = 0;
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc < 6) die("Invalid number of arguments");
|
||||
|
||||
if (argc > 6) limit = (int)strtol(argv[6], NULL, 0);
|
||||
|
||||
if (limit == 0) limit = 100;
|
||||
|
||||
encoder = get_aom_encoder_by_name(codec_arg);
|
||||
if (!encoder) die("Unsupported codec.");
|
||||
|
||||
w = (int)strtol(width_arg, NULL, 0);
|
||||
h = (int)strtol(height_arg, NULL, 0);
|
||||
|
||||
if (w <= 0 || h <= 0 || (w % 2) != 0 || (h % 2) != 0)
|
||||
die("Invalid frame size: %dx%d", w, h);
|
||||
|
||||
if (!aom_img_alloc(&raw, AOM_IMG_FMT_I420, w, h, 1))
|
||||
die("Failed to allocate image", w, h);
|
||||
|
||||
printf("Using %s\n", aom_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
// Configuration
|
||||
res = aom_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) die_codec(&codec, "Failed to get default codec config.");
|
||||
|
||||
cfg.g_w = w;
|
||||
cfg.g_h = h;
|
||||
cfg.g_timebase.num = 1;
|
||||
cfg.g_timebase.den = fps;
|
||||
cfg.rc_target_bitrate = bitrate;
|
||||
|
||||
if (!(infile = fopen(infile_arg, "rb")))
|
||||
die("Failed to open %s for reading", infile_arg);
|
||||
|
||||
// Pass 0
|
||||
cfg.g_pass = AOM_RC_FIRST_PASS;
|
||||
stats = pass0(&raw, infile, encoder, &cfg, limit);
|
||||
|
||||
// Pass 1
|
||||
rewind(infile);
|
||||
cfg.g_pass = AOM_RC_LAST_PASS;
|
||||
cfg.rc_twopass_stats_in = stats;
|
||||
pass1(&raw, infile, outfile_arg, encoder, &cfg, limit);
|
||||
free(stats.buf);
|
||||
|
||||
aom_img_free(&raw);
|
||||
fclose(infile);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue