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