Index: source/libvpx/webmenc.c |
=================================================================== |
--- source/libvpx/webmenc.c (revision 0) |
+++ source/libvpx/webmenc.c (revision 0) |
@@ -0,0 +1,331 @@ |
+/* |
+ * Copyright (c) 2013 The WebM project authors. All Rights Reserved. |
+ * |
+ * Use of this source code is governed by a BSD-style license |
+ * that can be found in the LICENSE file in the root of the source |
+ * tree. An additional intellectual property rights grant can be found |
+ * in the file PATENTS. All contributing project authors may |
+ * be found in the AUTHORS file in the root of the source tree. |
+ */ |
+#include "webmenc.h" |
+ |
+#include <limits.h> |
+#include <string.h> |
+ |
+#include "third_party/libmkv/EbmlWriter.h" |
+#include "third_party/libmkv/EbmlIDs.h" |
+ |
+void Ebml_Write(struct EbmlGlobal *glob, |
+ const void *buffer_in, |
+ unsigned long len) { |
+ (void) fwrite(buffer_in, 1, len, glob->stream); |
+} |
+ |
+#define WRITE_BUFFER(s) \ |
+for (i = len - 1; i >= 0; i--) { \ |
+ x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \ |
+ Ebml_Write(glob, &x, 1); \ |
+} |
+ |
+void Ebml_Serialize(struct EbmlGlobal *glob, |
+ const void *buffer_in, |
+ int buffer_size, |
+ unsigned long len) { |
+ char x; |
+ int i; |
+ |
+ /* buffer_size: |
+ * 1 - int8_t; |
+ * 2 - int16_t; |
+ * 3 - int32_t; |
+ * 4 - int64_t; |
+ */ |
+ switch (buffer_size) { |
+ case 1: |
+ WRITE_BUFFER(int8_t) |
+ break; |
+ case 2: |
+ WRITE_BUFFER(int16_t) |
+ break; |
+ case 4: |
+ WRITE_BUFFER(int32_t) |
+ break; |
+ case 8: |
+ WRITE_BUFFER(int64_t) |
+ break; |
+ default: |
+ break; |
+ } |
+} |
+#undef WRITE_BUFFER |
+ |
+/* Need a fixed size serializer for the track ID. libmkv provides a 64 bit |
+ * one, but not a 32 bit one. |
+ */ |
+static void Ebml_SerializeUnsigned32(struct EbmlGlobal *glob, |
+ unsigned int class_id, |
+ uint64_t ui) { |
+ const unsigned char sizeSerialized = 4 | 0x80; |
+ Ebml_WriteID(glob, class_id); |
+ Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1); |
+ Ebml_Serialize(glob, &ui, sizeof(ui), 4); |
+} |
+ |
+static void Ebml_StartSubElement(struct EbmlGlobal *glob, |
+ EbmlLoc *ebmlLoc, |
+ unsigned int class_id) { |
+ const uint64_t kEbmlUnknownLength = LITERALU64(0x01FFFFFF, 0xFFFFFFFF); |
+ Ebml_WriteID(glob, class_id); |
+ *ebmlLoc = ftello(glob->stream); |
+ Ebml_Serialize(glob, &kEbmlUnknownLength, sizeof(kEbmlUnknownLength), 8); |
+} |
+ |
+static void Ebml_EndSubElement(struct EbmlGlobal *glob, EbmlLoc *ebmlLoc) { |
+ off_t pos; |
+ uint64_t size; |
+ |
+ /* Save the current stream pointer. */ |
+ pos = ftello(glob->stream); |
+ |
+ /* Calculate the size of this element. */ |
+ size = pos - *ebmlLoc - 8; |
+ size |= LITERALU64(0x01000000, 0x00000000); |
+ |
+ /* Seek back to the beginning of the element and write the new size. */ |
+ fseeko(glob->stream, *ebmlLoc, SEEK_SET); |
+ Ebml_Serialize(glob, &size, sizeof(size), 8); |
+ |
+ /* Reset the stream pointer. */ |
+ fseeko(glob->stream, pos, SEEK_SET); |
+} |
+ |
+void write_webm_seek_element(struct EbmlGlobal *ebml, |
+ unsigned int id, |
+ off_t pos) { |
+ uint64_t offset = pos - ebml->position_reference; |
+ EbmlLoc start; |
+ Ebml_StartSubElement(ebml, &start, Seek); |
+ Ebml_SerializeBinary(ebml, SeekID, id); |
+ Ebml_SerializeUnsigned64(ebml, SeekPosition, offset); |
+ Ebml_EndSubElement(ebml, &start); |
+} |
+ |
+void write_webm_seek_info(struct EbmlGlobal *ebml) { |
+ off_t pos; |
+ EbmlLoc start; |
+ EbmlLoc startInfo; |
+ uint64_t frame_time; |
+ char version_string[64]; |
+ |
+ /* Save the current stream pointer. */ |
+ pos = ftello(ebml->stream); |
+ |
+ if (ebml->seek_info_pos) |
+ fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET); |
+ else |
+ ebml->seek_info_pos = pos; |
+ |
+ Ebml_StartSubElement(ebml, &start, SeekHead); |
+ write_webm_seek_element(ebml, Tracks, ebml->track_pos); |
+ write_webm_seek_element(ebml, Cues, ebml->cue_pos); |
+ write_webm_seek_element(ebml, Info, ebml->segment_info_pos); |
+ Ebml_EndSubElement(ebml, &start); |
+ |
+ /* Create and write the Segment Info. */ |
+ if (ebml->debug) { |
+ strcpy(version_string, "vpxenc"); |
+ } else { |
+ strcpy(version_string, "vpxenc "); |
+ strncat(version_string, |
+ vpx_codec_version_str(), |
+ sizeof(version_string) - 1 - strlen(version_string)); |
+ } |
+ |
+ frame_time = (uint64_t)1000 * ebml->framerate.den |
+ / ebml->framerate.num; |
+ ebml->segment_info_pos = ftello(ebml->stream); |
+ Ebml_StartSubElement(ebml, &startInfo, Info); |
+ Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000); |
+ Ebml_SerializeFloat(ebml, Segment_Duration, |
+ (double)(ebml->last_pts_ms + frame_time)); |
+ Ebml_SerializeString(ebml, 0x4D80, version_string); |
+ Ebml_SerializeString(ebml, 0x5741, version_string); |
+ Ebml_EndSubElement(ebml, &startInfo); |
+} |
+ |
+void write_webm_file_header(struct EbmlGlobal *glob, |
+ const vpx_codec_enc_cfg_t *cfg, |
+ const struct vpx_rational *fps, |
+ stereo_format_t stereo_fmt, |
+ unsigned int fourcc) { |
+ EbmlLoc start; |
+ EbmlLoc trackStart; |
+ EbmlLoc videoStart; |
+ unsigned int trackNumber = 1; |
+ uint64_t trackID = 0; |
+ unsigned int pixelWidth = cfg->g_w; |
+ unsigned int pixelHeight = cfg->g_h; |
+ |
+ /* Write the EBML header. */ |
+ Ebml_StartSubElement(glob, &start, EBML); |
+ Ebml_SerializeUnsigned(glob, EBMLVersion, 1); |
+ Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1); |
+ Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4); |
+ Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8); |
+ Ebml_SerializeString(glob, DocType, "webm"); |
+ Ebml_SerializeUnsigned(glob, DocTypeVersion, 2); |
+ Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2); |
+ Ebml_EndSubElement(glob, &start); |
+ |
+ /* Open and begin writing the segment element. */ |
+ Ebml_StartSubElement(glob, &glob->startSegment, Segment); |
+ glob->position_reference = ftello(glob->stream); |
+ glob->framerate = *fps; |
+ write_webm_seek_info(glob); |
+ |
+ /* Open and write the Tracks element. */ |
+ glob->track_pos = ftello(glob->stream); |
+ Ebml_StartSubElement(glob, &trackStart, Tracks); |
+ |
+ /* Open and write the Track entry. */ |
+ Ebml_StartSubElement(glob, &start, TrackEntry); |
+ Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); |
+ glob->track_id_pos = ftello(glob->stream); |
+ Ebml_SerializeUnsigned32(glob, TrackUID, trackID); |
+ Ebml_SerializeUnsigned(glob, TrackType, 1); |
+ Ebml_SerializeString(glob, CodecID, |
+ fourcc == VP8_FOURCC ? "V_VP8" : "V_VP9"); |
+ Ebml_StartSubElement(glob, &videoStart, Video); |
+ Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth); |
+ Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight); |
+ Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt); |
+ Ebml_EndSubElement(glob, &videoStart); |
+ |
+ /* Close Track entry. */ |
+ Ebml_EndSubElement(glob, &start); |
+ |
+ /* Close Tracks element. */ |
+ Ebml_EndSubElement(glob, &trackStart); |
+ |
+ /* Segment element remains open. */ |
+} |
+ |
+void write_webm_block(struct EbmlGlobal *glob, |
+ const vpx_codec_enc_cfg_t *cfg, |
+ const vpx_codec_cx_pkt_t *pkt) { |
+ unsigned int block_length; |
+ unsigned char track_number; |
+ uint16_t block_timecode = 0; |
+ unsigned char flags; |
+ int64_t pts_ms; |
+ int start_cluster = 0, is_keyframe; |
+ |
+ /* Calculate the PTS of this frame in milliseconds. */ |
+ pts_ms = pkt->data.frame.pts * 1000 |
+ * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den; |
+ |
+ if (pts_ms <= glob->last_pts_ms) |
+ pts_ms = glob->last_pts_ms + 1; |
+ |
+ glob->last_pts_ms = pts_ms; |
+ |
+ /* Calculate the relative time of this block. */ |
+ if (pts_ms - glob->cluster_timecode > SHRT_MAX) |
+ start_cluster = 1; |
+ else |
+ block_timecode = (uint16_t)pts_ms - glob->cluster_timecode; |
+ |
+ is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY); |
+ if (start_cluster || is_keyframe) { |
+ if (glob->cluster_open) |
+ Ebml_EndSubElement(glob, &glob->startCluster); |
+ |
+ /* Open the new cluster. */ |
+ block_timecode = 0; |
+ glob->cluster_open = 1; |
+ glob->cluster_timecode = (uint32_t)pts_ms; |
+ glob->cluster_pos = ftello(glob->stream); |
+ Ebml_StartSubElement(glob, &glob->startCluster, Cluster); |
+ Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode); |
+ |
+ /* Save a cue point if this is a keyframe. */ |
+ if (is_keyframe) { |
+ struct cue_entry *cue, *new_cue_list; |
+ |
+ new_cue_list = realloc(glob->cue_list, |
+ (glob->cues + 1) * sizeof(struct cue_entry)); |
+ if (new_cue_list) |
+ glob->cue_list = new_cue_list; |
+ else |
+ fatal("Failed to realloc cue list."); |
+ |
+ cue = &glob->cue_list[glob->cues]; |
+ cue->time = glob->cluster_timecode; |
+ cue->loc = glob->cluster_pos; |
+ glob->cues++; |
+ } |
+ } |
+ |
+ /* Write the Simple Block. */ |
+ Ebml_WriteID(glob, SimpleBlock); |
+ |
+ block_length = (unsigned int)pkt->data.frame.sz + 4; |
+ block_length |= 0x10000000; |
+ Ebml_Serialize(glob, &block_length, sizeof(block_length), 4); |
+ |
+ track_number = 1; |
+ track_number |= 0x80; |
+ Ebml_Write(glob, &track_number, 1); |
+ |
+ Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2); |
+ |
+ flags = 0; |
+ if (is_keyframe) |
+ flags |= 0x80; |
+ if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) |
+ flags |= 0x08; |
+ Ebml_Write(glob, &flags, 1); |
+ |
+ Ebml_Write(glob, pkt->data.frame.buf, (unsigned int)pkt->data.frame.sz); |
+} |
+ |
+void write_webm_file_footer(struct EbmlGlobal *glob, int hash) { |
+ EbmlLoc start_cues; |
+ EbmlLoc start_cue_point; |
+ EbmlLoc start_cue_tracks; |
+ unsigned int i; |
+ |
+ if (glob->cluster_open) |
+ Ebml_EndSubElement(glob, &glob->startCluster); |
+ |
+ glob->cue_pos = ftello(glob->stream); |
+ Ebml_StartSubElement(glob, &start_cues, Cues); |
+ |
+ for (i = 0; i < glob->cues; i++) { |
+ struct cue_entry *cue = &glob->cue_list[i]; |
+ Ebml_StartSubElement(glob, &start_cue_point, CuePoint); |
+ Ebml_SerializeUnsigned(glob, CueTime, cue->time); |
+ |
+ Ebml_StartSubElement(glob, &start_cue_tracks, CueTrackPositions); |
+ Ebml_SerializeUnsigned(glob, CueTrack, 1); |
+ Ebml_SerializeUnsigned64(glob, CueClusterPosition, |
+ cue->loc - glob->position_reference); |
+ Ebml_EndSubElement(glob, &start_cue_tracks); |
+ |
+ Ebml_EndSubElement(glob, &start_cue_point); |
+ } |
+ |
+ Ebml_EndSubElement(glob, &start_cues); |
+ |
+ /* Close the Segment. */ |
+ Ebml_EndSubElement(glob, &glob->startSegment); |
+ |
+ /* Patch up the seek info block. */ |
+ write_webm_seek_info(glob); |
+ |
+ /* Patch up the track id. */ |
+ fseeko(glob->stream, glob->track_id_pos, SEEK_SET); |
+ Ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash); |
+ |
+ fseeko(glob->stream, 0, SEEK_END); |
+} |