• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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