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
16 #include <string>
17
18 #include "common/av1_config.h"
19 #include "third_party/libwebm/mkvmuxer/mkvmuxer.h"
20 #include "third_party/libwebm/mkvmuxer/mkvmuxerutil.h"
21 #include "third_party/libwebm/mkvmuxer/mkvwriter.h"
22
23 namespace {
24 const uint64_t kDebugTrackUid = 0xDEADBEEF;
25 const int kVideoTrackNumber = 1;
26 } // namespace
27
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)28 int write_webm_file_header(struct WebmOutputContext *webm_ctx,
29 aom_codec_ctx_t *encoder_ctx,
30 const aom_codec_enc_cfg_t *cfg,
31 stereo_format_t stereo_fmt, unsigned int fourcc,
32 const struct AvxRational *par) {
33 mkvmuxer::MkvWriter *const writer = new mkvmuxer::MkvWriter(webm_ctx->stream);
34 mkvmuxer::Segment *const segment = new mkvmuxer::Segment();
35 if (!writer || !segment) {
36 fprintf(stderr, "webmenc> mkvmuxer objects alloc failed, out of memory?\n");
37 return -1;
38 }
39
40 bool ok = segment->Init(writer);
41 if (!ok) {
42 fprintf(stderr, "webmenc> mkvmuxer Init failed.\n");
43 return -1;
44 }
45
46 segment->set_mode(mkvmuxer::Segment::kFile);
47 segment->OutputCues(true);
48
49 mkvmuxer::SegmentInfo *const info = segment->GetSegmentInfo();
50 if (!info) {
51 fprintf(stderr, "webmenc> Cannot retrieve Segment Info.\n");
52 return -1;
53 }
54
55 const uint64_t kTimecodeScale = 1000000;
56 info->set_timecode_scale(kTimecodeScale);
57 std::string version = "aomenc";
58 if (!webm_ctx->debug) {
59 version.append(std::string(" ") + aom_codec_version_str());
60 }
61 info->set_writing_app(version.c_str());
62
63 const uint64_t video_track_id =
64 segment->AddVideoTrack(static_cast<int>(cfg->g_w),
65 static_cast<int>(cfg->g_h), kVideoTrackNumber);
66 mkvmuxer::VideoTrack *const video_track = static_cast<mkvmuxer::VideoTrack *>(
67 segment->GetTrackByNumber(video_track_id));
68
69 if (!video_track) {
70 fprintf(stderr, "webmenc> Video track creation failed.\n");
71 return -1;
72 }
73
74 ok = false;
75 aom_fixed_buf_t *obu_sequence_header =
76 aom_codec_get_global_headers(encoder_ctx);
77 if (obu_sequence_header) {
78 Av1Config av1_config;
79 if (get_av1config_from_obu(
80 reinterpret_cast<const uint8_t *>(obu_sequence_header->buf),
81 obu_sequence_header->sz, false, &av1_config) == 0) {
82 uint8_t av1_config_buffer[4] = { 0 };
83 size_t bytes_written = 0;
84 if (write_av1config(&av1_config, sizeof(av1_config_buffer),
85 &bytes_written, av1_config_buffer) == 0) {
86 ok = video_track->SetCodecPrivate(av1_config_buffer,
87 sizeof(av1_config_buffer));
88 }
89 }
90 free(obu_sequence_header->buf);
91 free(obu_sequence_header);
92 }
93 if (!ok) {
94 fprintf(stderr, "webmenc> Unable to set AV1 config.\n");
95 return -1;
96 }
97
98 ok = video_track->SetStereoMode(stereo_fmt);
99 if (!ok) {
100 fprintf(stderr, "webmenc> Unable to set stereo mode.\n");
101 return -1;
102 }
103
104 if (fourcc != AV1_FOURCC) {
105 fprintf(stderr, "webmenc> Unsupported codec (unknown 4 CC).\n");
106 return -1;
107 }
108 video_track->set_codec_id("V_AV1");
109
110 if (par->numerator > 1 || par->denominator > 1) {
111 // TODO(fgalligan): Add support of DisplayUnit, Display Aspect Ratio type
112 // to WebM format.
113 const uint64_t display_width = static_cast<uint64_t>(
114 ((cfg->g_w * par->numerator * 1.0) / par->denominator) + .5);
115 video_track->set_display_width(display_width);
116 video_track->set_display_height(cfg->g_h);
117 }
118
119 if (webm_ctx->debug) {
120 video_track->set_uid(kDebugTrackUid);
121 }
122
123 webm_ctx->writer = writer;
124 webm_ctx->segment = segment;
125
126 return 0;
127 }
128
write_webm_block(struct WebmOutputContext * webm_ctx,const aom_codec_enc_cfg_t * cfg,const aom_codec_cx_pkt_t * pkt)129 int write_webm_block(struct WebmOutputContext *webm_ctx,
130 const aom_codec_enc_cfg_t *cfg,
131 const aom_codec_cx_pkt_t *pkt) {
132 if (!webm_ctx->segment) {
133 fprintf(stderr, "webmenc> segment is NULL.\n");
134 return -1;
135 }
136 mkvmuxer::Segment *const segment =
137 reinterpret_cast<mkvmuxer::Segment *>(webm_ctx->segment);
138 int64_t pts_ns = pkt->data.frame.pts * 1000000000ll * cfg->g_timebase.num /
139 cfg->g_timebase.den;
140 if (pts_ns <= webm_ctx->last_pts_ns) pts_ns = webm_ctx->last_pts_ns + 1000000;
141 webm_ctx->last_pts_ns = pts_ns;
142
143 if (!segment->AddFrame(static_cast<uint8_t *>(pkt->data.frame.buf),
144 pkt->data.frame.sz, kVideoTrackNumber, pts_ns,
145 pkt->data.frame.flags & AOM_FRAME_IS_KEY)) {
146 fprintf(stderr, "webmenc> AddFrame failed.\n");
147 return -1;
148 }
149 return 0;
150 }
151
write_webm_file_footer(struct WebmOutputContext * webm_ctx)152 int write_webm_file_footer(struct WebmOutputContext *webm_ctx) {
153 if (!webm_ctx->writer || !webm_ctx->segment) {
154 fprintf(stderr, "webmenc> segment or writer NULL.\n");
155 return -1;
156 }
157 mkvmuxer::MkvWriter *const writer =
158 reinterpret_cast<mkvmuxer::MkvWriter *>(webm_ctx->writer);
159 mkvmuxer::Segment *const segment =
160 reinterpret_cast<mkvmuxer::Segment *>(webm_ctx->segment);
161 const bool ok = segment->Finalize();
162 delete segment;
163 delete writer;
164 webm_ctx->writer = NULL;
165 webm_ctx->segment = NULL;
166
167 if (!ok) {
168 fprintf(stderr, "webmenc> Segment::Finalize failed.\n");
169 return -1;
170 }
171
172 return 0;
173 }
174