xref: /aosp_15_r20/external/libaom/common/webmenc.cc (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
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