webmenc.cc 7.6 KB

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