summaryrefslogtreecommitdiffstats
path: root/third_party/aom/common/webmenc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/aom/common/webmenc.cc')
-rw-r--r--third_party/aom/common/webmenc.cc242
1 files changed, 242 insertions, 0 deletions
diff --git a/third_party/aom/common/webmenc.cc b/third_party/aom/common/webmenc.cc
new file mode 100644
index 0000000000..bb754e8119
--- /dev/null
+++ b/third_party/aom/common/webmenc.cc
@@ -0,0 +1,242 @@
+/*
+ * 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 "common/webmenc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <memory>
+#include <new>
+#include <string>
+
+#include "common/av1_config.h"
+#include "third_party/libwebm/mkvmuxer/mkvmuxer.h"
+#include "third_party/libwebm/mkvmuxer/mkvmuxerutil.h"
+#include "third_party/libwebm/mkvmuxer/mkvwriter.h"
+
+namespace {
+const uint64_t kDebugTrackUid = 0xDEADBEEF;
+const int kVideoTrackNumber = 1;
+
+// Simplistic mechanism to detect if an argv parameter refers to
+// an input or output file. Returns the total number of arguments that
+// should be skipped.
+int skip_input_output_arg(const char *arg, const char *input_fname) {
+ if (strcmp(arg, input_fname) == 0) {
+ return 1;
+ }
+ if (strcmp(arg, "-o") == 0 || strcmp(arg, "--output") == 0) {
+ return 2;
+ }
+ if (strncmp(arg, "--output=", strlen("--output=")) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace
+
+char *extract_encoder_settings(const char *version, const char **argv, int argc,
+ const char *input_fname) {
+ // + 9 for "version:" prefix and for null terminator.
+ size_t total_size = strlen(version) + 9;
+ int i = 1;
+ while (i < argc) {
+ int num_skip = skip_input_output_arg(argv[i], input_fname);
+ i += num_skip;
+ if (num_skip == 0) {
+ total_size += strlen(argv[i]) + 1; // + 1 is for space separator.
+ ++i;
+ }
+ }
+ char *result = static_cast<char *>(malloc(total_size));
+ if (result == nullptr) {
+ return nullptr;
+ }
+ char *cur = result;
+ cur += snprintf(cur, total_size, "version:%s", version);
+ i = 1;
+ while (i < argc) {
+ int num_skip = skip_input_output_arg(argv[i], input_fname);
+ i += num_skip;
+ if (num_skip == 0) {
+ cur += snprintf(cur, total_size, " %s", argv[i]);
+ ++i;
+ }
+ }
+ *cur = '\0';
+ return result;
+}
+
+int write_webm_file_header(struct WebmOutputContext *webm_ctx,
+ aom_codec_ctx_t *encoder_ctx,
+ const aom_codec_enc_cfg_t *cfg,
+ stereo_format_t stereo_fmt, unsigned int fourcc,
+ const struct AvxRational *par,
+ const char *encoder_settings) {
+ std::unique_ptr<mkvmuxer::MkvWriter> writer(
+ new (std::nothrow) mkvmuxer::MkvWriter(webm_ctx->stream));
+ std::unique_ptr<mkvmuxer::Segment> segment(new (std::nothrow)
+ mkvmuxer::Segment());
+ if (writer == nullptr || segment == nullptr) {
+ fprintf(stderr, "webmenc> mkvmuxer objects alloc failed, out of memory?\n");
+ return -1;
+ }
+
+ bool ok = segment->Init(writer.get());
+ if (!ok) {
+ fprintf(stderr, "webmenc> mkvmuxer Init failed.\n");
+ return -1;
+ }
+
+ segment->set_mode(mkvmuxer::Segment::kFile);
+ segment->OutputCues(true);
+
+ mkvmuxer::SegmentInfo *const info = segment->GetSegmentInfo();
+ if (!info) {
+ fprintf(stderr, "webmenc> Cannot retrieve Segment Info.\n");
+ return -1;
+ }
+
+ const uint64_t kTimecodeScale = 1000000;
+ info->set_timecode_scale(kTimecodeScale);
+ std::string version = "aomenc";
+ if (!webm_ctx->debug) {
+ version.append(std::string(" ") + aom_codec_version_str());
+ }
+ info->set_writing_app(version.c_str());
+
+ const uint64_t video_track_id =
+ segment->AddVideoTrack(static_cast<int>(cfg->g_w),
+ static_cast<int>(cfg->g_h), kVideoTrackNumber);
+ mkvmuxer::VideoTrack *const video_track = static_cast<mkvmuxer::VideoTrack *>(
+ segment->GetTrackByNumber(video_track_id));
+
+ if (!video_track) {
+ fprintf(stderr, "webmenc> Video track creation failed.\n");
+ return -1;
+ }
+
+ ok = false;
+ aom_fixed_buf_t *obu_sequence_header =
+ aom_codec_get_global_headers(encoder_ctx);
+ if (obu_sequence_header) {
+ Av1Config av1_config;
+ if (get_av1config_from_obu(
+ reinterpret_cast<const uint8_t *>(obu_sequence_header->buf),
+ obu_sequence_header->sz, false, &av1_config) == 0) {
+ uint8_t av1_config_buffer[4] = { 0 };
+ size_t bytes_written = 0;
+ if (write_av1config(&av1_config, sizeof(av1_config_buffer),
+ &bytes_written, av1_config_buffer) == 0) {
+ ok = video_track->SetCodecPrivate(av1_config_buffer,
+ sizeof(av1_config_buffer));
+ }
+ }
+ free(obu_sequence_header->buf);
+ free(obu_sequence_header);
+ }
+ if (!ok) {
+ fprintf(stderr, "webmenc> Unable to set AV1 config.\n");
+ return -1;
+ }
+
+ ok = video_track->SetStereoMode(stereo_fmt);
+ if (!ok) {
+ fprintf(stderr, "webmenc> Unable to set stereo mode.\n");
+ return -1;
+ }
+
+ if (fourcc != AV1_FOURCC) {
+ fprintf(stderr, "webmenc> Unsupported codec (unknown 4 CC).\n");
+ return -1;
+ }
+ video_track->set_codec_id("V_AV1");
+
+ if (par->numerator > 1 || par->denominator > 1) {
+ // TODO(fgalligan): Add support of DisplayUnit, Display Aspect Ratio type
+ // to WebM format.
+ const uint64_t display_width = static_cast<uint64_t>(
+ ((cfg->g_w * par->numerator * 1.0) / par->denominator) + .5);
+ video_track->set_display_width(display_width);
+ video_track->set_display_height(cfg->g_h);
+ }
+
+ if (encoder_settings != nullptr) {
+ mkvmuxer::Tag *tag = segment->AddTag();
+ if (tag == nullptr) {
+ fprintf(stderr,
+ "webmenc> Unable to allocate memory for encoder settings tag.\n");
+ return -1;
+ }
+ ok = tag->add_simple_tag("ENCODER_SETTINGS", encoder_settings);
+ if (!ok) {
+ fprintf(stderr,
+ "webmenc> Unable to allocate memory for encoder settings tag.\n");
+ return -1;
+ }
+ }
+
+ if (webm_ctx->debug) {
+ video_track->set_uid(kDebugTrackUid);
+ }
+
+ webm_ctx->writer = writer.release();
+ webm_ctx->segment = segment.release();
+ return 0;
+}
+
+int write_webm_block(struct WebmOutputContext *webm_ctx,
+ const aom_codec_enc_cfg_t *cfg,
+ const aom_codec_cx_pkt_t *pkt) {
+ if (!webm_ctx->segment) {
+ fprintf(stderr, "webmenc> segment is NULL.\n");
+ return -1;
+ }
+ mkvmuxer::Segment *const segment =
+ reinterpret_cast<mkvmuxer::Segment *>(webm_ctx->segment);
+ int64_t pts_ns = pkt->data.frame.pts * 1000000000ll * cfg->g_timebase.num /
+ cfg->g_timebase.den;
+ if (pts_ns <= webm_ctx->last_pts_ns) pts_ns = webm_ctx->last_pts_ns + 1000000;
+ webm_ctx->last_pts_ns = pts_ns;
+
+ if (!segment->AddFrame(static_cast<uint8_t *>(pkt->data.frame.buf),
+ pkt->data.frame.sz, kVideoTrackNumber, pts_ns,
+ pkt->data.frame.flags & AOM_FRAME_IS_KEY)) {
+ fprintf(stderr, "webmenc> AddFrame failed.\n");
+ return -1;
+ }
+ return 0;
+}
+
+int write_webm_file_footer(struct WebmOutputContext *webm_ctx) {
+ if (!webm_ctx->writer || !webm_ctx->segment) {
+ fprintf(stderr, "webmenc> segment or writer NULL.\n");
+ return -1;
+ }
+ mkvmuxer::MkvWriter *const writer =
+ reinterpret_cast<mkvmuxer::MkvWriter *>(webm_ctx->writer);
+ mkvmuxer::Segment *const segment =
+ reinterpret_cast<mkvmuxer::Segment *>(webm_ctx->segment);
+ const bool ok = segment->Finalize();
+ delete segment;
+ delete writer;
+ webm_ctx->writer = NULL;
+ webm_ctx->segment = NULL;
+
+ if (!ok) {
+ fprintf(stderr, "webmenc> Segment::Finalize failed.\n");
+ return -1;
+ }
+
+ return 0;
+}