tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

webmenc.cc (7807B)


      1 /*
      2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved.
      3 *
      4 * This source code is subject to the terms of the BSD 2 Clause License and
      5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
      6 * was not distributed with this source code in the LICENSE file, you can
      7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
      8 * Media Patent License 1.0 was not distributed with this source code in the
      9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
     10 */
     11 
     12 #include "common/webmenc.h"
     13 
     14 #include <stdio.h>
     15 #include <string.h>
     16 
     17 #include <memory>
     18 #include <new>
     19 #include <string>
     20 
     21 #include "common/av1_config.h"
     22 #include "third_party/libwebm/mkvmuxer/mkvmuxer.h"
     23 #include "third_party/libwebm/mkvmuxer/mkvmuxerutil.h"
     24 #include "third_party/libwebm/mkvmuxer/mkvwriter.h"
     25 
     26 namespace {
     27 const uint64_t kDebugTrackUid = 0xDEADBEEF;
     28 const int kVideoTrackNumber = 1;
     29 
     30 // Simplistic mechanism to detect if an argv parameter refers to
     31 // an input or output file. Returns the total number of arguments that
     32 // should be skipped.
     33 int skip_input_output_arg(const char *arg, const char *input_fname) {
     34  if (strcmp(arg, input_fname) == 0) {
     35    return 1;
     36  }
     37  if (strcmp(arg, "-o") == 0 || strcmp(arg, "--output") == 0) {
     38    return 2;
     39  }
     40  if (strncmp(arg, "--output=", strlen("--output=")) == 0) {
     41    return 1;
     42  }
     43  return 0;
     44 }
     45 
     46 }  // namespace
     47 
     48 char *extract_encoder_settings(const char *version, const char **argv, int argc,
     49                               const char *input_fname) {
     50  // + 9 for "version:" prefix and for null terminator.
     51  size_t total_size = strlen(version) + 9;
     52  int i = 1;
     53  while (i < argc) {
     54    int num_skip = skip_input_output_arg(argv[i], input_fname);
     55    i += num_skip;
     56    if (num_skip == 0) {
     57      total_size += strlen(argv[i]) + 1;  // + 1 is for space separator.
     58      ++i;
     59    }
     60  }
     61  char *result = static_cast<char *>(malloc(total_size));
     62  if (result == nullptr) {
     63    return nullptr;
     64  }
     65  char *cur = result;
     66  cur += snprintf(cur, total_size, "version:%s", version);
     67  i = 1;
     68  while (i < argc) {
     69    int num_skip = skip_input_output_arg(argv[i], input_fname);
     70    i += num_skip;
     71    if (num_skip == 0) {
     72      cur += snprintf(cur, total_size, " %s", argv[i]);
     73      ++i;
     74    }
     75  }
     76  *cur = '\0';
     77  return result;
     78 }
     79 
     80 int write_webm_file_header(struct WebmOutputContext *webm_ctx,
     81                           aom_codec_ctx_t *encoder_ctx,
     82                           const aom_codec_enc_cfg_t *cfg,
     83                           stereo_format_t stereo_fmt, unsigned int fourcc,
     84                           const struct AvxRational *par,
     85                           const char *encoder_settings) {
     86  std::unique_ptr<mkvmuxer::MkvWriter> writer(
     87      new (std::nothrow) mkvmuxer::MkvWriter(webm_ctx->stream));
     88  std::unique_ptr<mkvmuxer::Segment> segment(new (std::nothrow)
     89                                                 mkvmuxer::Segment());
     90  if (writer == nullptr || segment == nullptr) {
     91    fprintf(stderr, "webmenc> mkvmuxer objects alloc failed, out of memory?\n");
     92    return -1;
     93  }
     94 
     95  bool ok = segment->Init(writer.get());
     96  if (!ok) {
     97    fprintf(stderr, "webmenc> mkvmuxer Init failed.\n");
     98    return -1;
     99  }
    100 
    101  segment->set_mode(mkvmuxer::Segment::kFile);
    102  segment->OutputCues(true);
    103 
    104  mkvmuxer::SegmentInfo *const info = segment->GetSegmentInfo();
    105  if (!info) {
    106    fprintf(stderr, "webmenc> Cannot retrieve Segment Info.\n");
    107    return -1;
    108  }
    109 
    110  const uint64_t kTimecodeScale = 1000000;
    111  info->set_timecode_scale(kTimecodeScale);
    112  std::string version = "aomenc";
    113  if (!webm_ctx->debug) {
    114    version.append(std::string(" ") + aom_codec_version_str());
    115  }
    116  info->set_writing_app(version.c_str());
    117 
    118  const uint64_t video_track_id =
    119      segment->AddVideoTrack(static_cast<int>(cfg->g_w),
    120                             static_cast<int>(cfg->g_h), kVideoTrackNumber);
    121  mkvmuxer::VideoTrack *const video_track = static_cast<mkvmuxer::VideoTrack *>(
    122      segment->GetTrackByNumber(video_track_id));
    123 
    124  if (!video_track) {
    125    fprintf(stderr, "webmenc> Video track creation failed.\n");
    126    return -1;
    127  }
    128 
    129  ok = false;
    130  aom_fixed_buf_t *obu_sequence_header =
    131      aom_codec_get_global_headers(encoder_ctx);
    132  if (obu_sequence_header) {
    133    Av1Config av1_config;
    134    if (get_av1config_from_obu(
    135            reinterpret_cast<const uint8_t *>(obu_sequence_header->buf),
    136            obu_sequence_header->sz, false, &av1_config) == 0) {
    137      uint8_t av1_config_buffer[4] = { 0 };
    138      size_t bytes_written = 0;
    139      if (write_av1config(&av1_config, sizeof(av1_config_buffer),
    140                          &bytes_written, av1_config_buffer) == 0) {
    141        ok = video_track->SetCodecPrivate(av1_config_buffer,
    142                                          sizeof(av1_config_buffer));
    143      }
    144    }
    145    free(obu_sequence_header->buf);
    146    free(obu_sequence_header);
    147  }
    148  if (!ok) {
    149    fprintf(stderr, "webmenc> Unable to set AV1 config.\n");
    150    return -1;
    151  }
    152 
    153  ok = video_track->SetStereoMode(stereo_fmt);
    154  if (!ok) {
    155    fprintf(stderr, "webmenc> Unable to set stereo mode.\n");
    156    return -1;
    157  }
    158 
    159  if (fourcc != AV1_FOURCC) {
    160    fprintf(stderr, "webmenc> Unsupported codec (unknown 4 CC).\n");
    161    return -1;
    162  }
    163  video_track->set_codec_id("V_AV1");
    164 
    165  if (par->numerator > 1 || par->denominator > 1) {
    166    // TODO(fgalligan): Add support of DisplayUnit, Display Aspect Ratio type
    167    // to WebM format.
    168    const uint64_t display_width = static_cast<uint64_t>(
    169        ((cfg->g_w * par->numerator * 1.0) / par->denominator) + .5);
    170    video_track->set_display_width(display_width);
    171    video_track->set_display_height(cfg->g_h);
    172  }
    173 
    174  if (encoder_settings != nullptr) {
    175    mkvmuxer::Tag *tag = segment->AddTag();
    176    if (tag == nullptr) {
    177      fprintf(stderr,
    178              "webmenc> Unable to allocate memory for encoder settings tag.\n");
    179      return -1;
    180    }
    181    ok = tag->add_simple_tag("ENCODER_SETTINGS", encoder_settings);
    182    if (!ok) {
    183      fprintf(stderr,
    184              "webmenc> Unable to allocate memory for encoder settings tag.\n");
    185      return -1;
    186    }
    187  }
    188 
    189  if (webm_ctx->debug) {
    190    video_track->set_uid(kDebugTrackUid);
    191  }
    192 
    193  webm_ctx->writer = writer.release();
    194  webm_ctx->segment = segment.release();
    195  return 0;
    196 }
    197 
    198 int write_webm_block(struct WebmOutputContext *webm_ctx,
    199                     const aom_codec_enc_cfg_t *cfg,
    200                     const aom_codec_cx_pkt_t *pkt) {
    201  if (!webm_ctx->segment) {
    202    fprintf(stderr, "webmenc> segment is NULL.\n");
    203    return -1;
    204  }
    205  mkvmuxer::Segment *const segment =
    206      reinterpret_cast<mkvmuxer::Segment *>(webm_ctx->segment);
    207  int64_t pts_ns = pkt->data.frame.pts * 1000000000ll * cfg->g_timebase.num /
    208                   cfg->g_timebase.den;
    209  if (pts_ns <= webm_ctx->last_pts_ns) pts_ns = webm_ctx->last_pts_ns + 1000000;
    210  webm_ctx->last_pts_ns = pts_ns;
    211 
    212  if (!segment->AddFrame(static_cast<uint8_t *>(pkt->data.frame.buf),
    213                         pkt->data.frame.sz, kVideoTrackNumber, pts_ns,
    214                         pkt->data.frame.flags & AOM_FRAME_IS_KEY)) {
    215    fprintf(stderr, "webmenc> AddFrame failed.\n");
    216    return -1;
    217  }
    218  return 0;
    219 }
    220 
    221 int write_webm_file_footer(struct WebmOutputContext *webm_ctx) {
    222  if (!webm_ctx->writer || !webm_ctx->segment) {
    223    fprintf(stderr, "webmenc> segment or writer NULL.\n");
    224    return -1;
    225  }
    226  mkvmuxer::MkvWriter *const writer =
    227      reinterpret_cast<mkvmuxer::MkvWriter *>(webm_ctx->writer);
    228  mkvmuxer::Segment *const segment =
    229      reinterpret_cast<mkvmuxer::Segment *>(webm_ctx->segment);
    230  const bool ok = segment->Finalize();
    231  delete segment;
    232  delete writer;
    233  webm_ctx->writer = NULL;
    234  webm_ctx->segment = NULL;
    235 
    236  if (!ok) {
    237    fprintf(stderr, "webmenc> Segment::Finalize failed.\n");
    238    return -1;
    239  }
    240 
    241  return 0;
    242 }