]> granicus.if.org Git - libvpx/blob - webmenc.c
Merge "[BITSTREAM]Fix row tile mode_info pointer setup"
[libvpx] / webmenc.c
1 /*
2  *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 #include "webmenc.h"
11
12 #include <limits.h>
13 #include <string.h>
14
15 #include "third_party/libmkv/EbmlWriter.h"
16 #include "third_party/libmkv/EbmlIDs.h"
17
18 #if defined(_MSC_VER)
19 /* MSVS uses _f{seek,tell}i64 */
20 #define fseeko _fseeki64
21 #define ftello _ftelli64
22 #elif defined(_WIN32)
23 /* MinGW defines off_t as long, and uses f{seek,tell}o64/off64_t for large
24  * files */
25 #define fseeko fseeko64
26 #define ftello ftello64
27 #define off_t off64_t
28 #endif
29
30 #define LITERALU64(hi, lo) ((((uint64_t)hi) << 32) | lo)
31
32 void Ebml_Write(struct EbmlGlobal *glob,
33                 const void *buffer_in,
34                 unsigned long len) {
35   (void) fwrite(buffer_in, 1, len, glob->stream);
36 }
37
38 #define WRITE_BUFFER(s) \
39 for (i = len - 1; i >= 0; i--) { \
40   x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \
41   Ebml_Write(glob, &x, 1); \
42 }
43
44 void Ebml_Serialize(struct EbmlGlobal *glob,
45                     const void *buffer_in,
46                     int buffer_size,
47                     unsigned long len) {
48   char x;
49   int i;
50
51   /* buffer_size:
52    * 1 - int8_t;
53    * 2 - int16_t;
54    * 3 - int32_t;
55    * 4 - int64_t;
56    */
57   switch (buffer_size) {
58     case 1:
59       WRITE_BUFFER(int8_t)
60       break;
61     case 2:
62       WRITE_BUFFER(int16_t)
63       break;
64     case 4:
65       WRITE_BUFFER(int32_t)
66       break;
67     case 8:
68       WRITE_BUFFER(int64_t)
69       break;
70     default:
71       break;
72   }
73 }
74 #undef WRITE_BUFFER
75
76 /* Need a fixed size serializer for the track ID. libmkv provides a 64 bit
77  * one, but not a 32 bit one.
78  */
79 static void Ebml_SerializeUnsigned32(struct EbmlGlobal *glob,
80                                      unsigned int class_id,
81                                      uint64_t ui) {
82   const unsigned char sizeSerialized = 4 | 0x80;
83   Ebml_WriteID(glob, class_id);
84   Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1);
85   Ebml_Serialize(glob, &ui, sizeof(ui), 4);
86 }
87
88 static void Ebml_StartSubElement(struct EbmlGlobal *glob,
89                                  EbmlLoc *ebmlLoc,
90                                  unsigned int class_id) {
91   const uint64_t kEbmlUnknownLength = LITERALU64(0x01FFFFFF, 0xFFFFFFFF);
92   Ebml_WriteID(glob, class_id);
93   *ebmlLoc = ftello(glob->stream);
94   Ebml_Serialize(glob, &kEbmlUnknownLength, sizeof(kEbmlUnknownLength), 8);
95 }
96
97 static void Ebml_EndSubElement(struct EbmlGlobal *glob, EbmlLoc *ebmlLoc) {
98   off_t pos;
99   uint64_t size;
100
101   /* Save the current stream pointer. */
102   pos = ftello(glob->stream);
103
104   /* Calculate the size of this element. */
105   size = pos - *ebmlLoc - 8;
106   size |= LITERALU64(0x01000000, 0x00000000);
107
108   /* Seek back to the beginning of the element and write the new size. */
109   fseeko(glob->stream, *ebmlLoc, SEEK_SET);
110   Ebml_Serialize(glob, &size, sizeof(size), 8);
111
112   /* Reset the stream pointer. */
113   fseeko(glob->stream, pos, SEEK_SET);
114 }
115
116 void write_webm_seek_element(struct EbmlGlobal *ebml,
117                              unsigned int id,
118                              off_t pos) {
119   uint64_t offset = pos - ebml->position_reference;
120   EbmlLoc start;
121   Ebml_StartSubElement(ebml, &start, Seek);
122   Ebml_SerializeBinary(ebml, SeekID, id);
123   Ebml_SerializeUnsigned64(ebml, SeekPosition, offset);
124   Ebml_EndSubElement(ebml, &start);
125 }
126
127 void write_webm_seek_info(struct EbmlGlobal *ebml) {
128   off_t pos;
129   EbmlLoc start;
130   EbmlLoc startInfo;
131   uint64_t frame_time;
132   char version_string[64];
133
134   /* Save the current stream pointer. */
135   pos = ftello(ebml->stream);
136
137   if (ebml->seek_info_pos)
138     fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET);
139   else
140     ebml->seek_info_pos = pos;
141
142   Ebml_StartSubElement(ebml, &start, SeekHead);
143   write_webm_seek_element(ebml, Tracks, ebml->track_pos);
144   write_webm_seek_element(ebml, Cues, ebml->cue_pos);
145   write_webm_seek_element(ebml, Info, ebml->segment_info_pos);
146   Ebml_EndSubElement(ebml, &start);
147
148   /* Create and write the Segment Info. */
149   if (ebml->debug) {
150     strcpy(version_string, "vpxenc");
151   } else {
152     strcpy(version_string, "vpxenc ");
153     strncat(version_string,
154             vpx_codec_version_str(),
155             sizeof(version_string) - 1 - strlen(version_string));
156   }
157
158   frame_time = (uint64_t)1000 * ebml->framerate.den
159                / ebml->framerate.num;
160   ebml->segment_info_pos = ftello(ebml->stream);
161   Ebml_StartSubElement(ebml, &startInfo, Info);
162   Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000);
163   Ebml_SerializeFloat(ebml, Segment_Duration,
164                       (double)(ebml->last_pts_ms + frame_time));
165   Ebml_SerializeString(ebml, 0x4D80, version_string);
166   Ebml_SerializeString(ebml, 0x5741, version_string);
167   Ebml_EndSubElement(ebml, &startInfo);
168 }
169
170 void write_webm_file_header(struct EbmlGlobal *glob,
171                             const vpx_codec_enc_cfg_t *cfg,
172                             const struct vpx_rational *fps,
173                             stereo_format_t stereo_fmt,
174                             unsigned int fourcc) {
175   EbmlLoc start;
176   EbmlLoc trackStart;
177   EbmlLoc videoStart;
178   unsigned int trackNumber = 1;
179   uint64_t trackID = 0;
180   unsigned int pixelWidth = cfg->g_w;
181   unsigned int pixelHeight = cfg->g_h;
182
183   /* Write the EBML header. */
184   Ebml_StartSubElement(glob, &start, EBML);
185   Ebml_SerializeUnsigned(glob, EBMLVersion, 1);
186   Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1);
187   Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4);
188   Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8);
189   Ebml_SerializeString(glob, DocType, "webm");
190   Ebml_SerializeUnsigned(glob, DocTypeVersion, 2);
191   Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2);
192   Ebml_EndSubElement(glob, &start);
193
194   /* Open and begin writing the segment element. */
195   Ebml_StartSubElement(glob, &glob->startSegment, Segment);
196   glob->position_reference = ftello(glob->stream);
197   glob->framerate = *fps;
198   write_webm_seek_info(glob);
199
200   /* Open and write the Tracks element. */
201   glob->track_pos = ftello(glob->stream);
202   Ebml_StartSubElement(glob, &trackStart, Tracks);
203
204   /* Open and write the Track entry. */
205   Ebml_StartSubElement(glob, &start, TrackEntry);
206   Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber);
207   glob->track_id_pos = ftello(glob->stream);
208   Ebml_SerializeUnsigned32(glob, TrackUID, trackID);
209   Ebml_SerializeUnsigned(glob, TrackType, 1);
210   Ebml_SerializeString(glob, CodecID,
211                        fourcc == VP8_FOURCC ? "V_VP8" : "V_VP9");
212   Ebml_StartSubElement(glob, &videoStart, Video);
213   Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth);
214   Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight);
215   Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt);
216   Ebml_EndSubElement(glob, &videoStart);
217
218   /* Close Track entry. */
219   Ebml_EndSubElement(glob, &start);
220
221   /* Close Tracks element. */
222   Ebml_EndSubElement(glob, &trackStart);
223
224   /* Segment element remains open. */
225 }
226
227 void write_webm_block(struct EbmlGlobal *glob,
228                       const vpx_codec_enc_cfg_t *cfg,
229                       const vpx_codec_cx_pkt_t *pkt) {
230   unsigned int block_length;
231   unsigned char track_number;
232   uint16_t block_timecode = 0;
233   unsigned char flags;
234   int64_t pts_ms;
235   int start_cluster = 0, is_keyframe;
236
237   /* Calculate the PTS of this frame in milliseconds. */
238   pts_ms = pkt->data.frame.pts * 1000
239            * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den;
240
241   if (pts_ms <= glob->last_pts_ms)
242     pts_ms = glob->last_pts_ms + 1;
243
244   glob->last_pts_ms = pts_ms;
245
246   /* Calculate the relative time of this block. */
247   if (pts_ms - glob->cluster_timecode > SHRT_MAX)
248     start_cluster = 1;
249   else
250     block_timecode = (uint16_t)pts_ms - glob->cluster_timecode;
251
252   is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY);
253   if (start_cluster || is_keyframe) {
254     if (glob->cluster_open)
255       Ebml_EndSubElement(glob, &glob->startCluster);
256
257     /* Open the new cluster. */
258     block_timecode = 0;
259     glob->cluster_open = 1;
260     glob->cluster_timecode = (uint32_t)pts_ms;
261     glob->cluster_pos = ftello(glob->stream);
262     Ebml_StartSubElement(glob, &glob->startCluster, Cluster);
263     Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode);
264
265     /* Save a cue point if this is a keyframe. */
266     if (is_keyframe) {
267       struct cue_entry *cue, *new_cue_list;
268
269       new_cue_list = realloc(glob->cue_list,
270                              (glob->cues + 1) * sizeof(struct cue_entry));
271       if (new_cue_list)
272         glob->cue_list = new_cue_list;
273       else
274         fatal("Failed to realloc cue list.");
275
276       cue = &glob->cue_list[glob->cues];
277       cue->time = glob->cluster_timecode;
278       cue->loc = glob->cluster_pos;
279       glob->cues++;
280     }
281   }
282
283   /* Write the Simple Block. */
284   Ebml_WriteID(glob, SimpleBlock);
285
286   block_length = (unsigned int)pkt->data.frame.sz + 4;
287   block_length |= 0x10000000;
288   Ebml_Serialize(glob, &block_length, sizeof(block_length), 4);
289
290   track_number = 1;
291   track_number |= 0x80;
292   Ebml_Write(glob, &track_number, 1);
293
294   Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2);
295
296   flags = 0;
297   if (is_keyframe)
298     flags |= 0x80;
299   if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
300     flags |= 0x08;
301   Ebml_Write(glob, &flags, 1);
302
303   Ebml_Write(glob, pkt->data.frame.buf, (unsigned int)pkt->data.frame.sz);
304 }
305
306 void write_webm_file_footer(struct EbmlGlobal *glob, int hash) {
307   EbmlLoc start_cues;
308   EbmlLoc start_cue_point;
309   EbmlLoc start_cue_tracks;
310   unsigned int i;
311
312   if (glob->cluster_open)
313     Ebml_EndSubElement(glob, &glob->startCluster);
314
315   glob->cue_pos = ftello(glob->stream);
316   Ebml_StartSubElement(glob, &start_cues, Cues);
317
318   for (i = 0; i < glob->cues; i++) {
319     struct cue_entry *cue = &glob->cue_list[i];
320     Ebml_StartSubElement(glob, &start_cue_point, CuePoint);
321     Ebml_SerializeUnsigned(glob, CueTime, cue->time);
322
323     Ebml_StartSubElement(glob, &start_cue_tracks, CueTrackPositions);
324     Ebml_SerializeUnsigned(glob, CueTrack, 1);
325     Ebml_SerializeUnsigned64(glob, CueClusterPosition,
326                              cue->loc - glob->position_reference);
327     Ebml_EndSubElement(glob, &start_cue_tracks);
328
329     Ebml_EndSubElement(glob, &start_cue_point);
330   }
331
332   Ebml_EndSubElement(glob, &start_cues);
333
334   /* Close the Segment. */
335   Ebml_EndSubElement(glob, &glob->startSegment);
336
337   /* Patch up the seek info block. */
338   write_webm_seek_info(glob);
339
340   /* Patch up the track id. */
341   fseeko(glob->stream, glob->track_id_pos, SEEK_SET);
342   Ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash);
343
344   fseeko(glob->stream, 0, SEEK_END);
345 }