void EbmlComposer::FinishCluster() { FinishMetadata(); if (!(mFlushState & FLUSH_CLUSTER)) { // No completed cluster available. return; } MOZ_ASSERT(mClusterLengthLoc > 0); EbmlGlobal ebml; EbmlLoc ebmlLoc; ebmlLoc.offset = mClusterLengthLoc; ebml.offset = 0; for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) { ebml.offset += mClusterBuffs[i].Length(); } ebml.buf = mClusterBuffs[mClusterHeaderIndex].Elements(); Ebml_EndSubElement(&ebml, &ebmlLoc); // Move the mClusterBuffs data from mClusterHeaderIndex that we can skip // the metadata and the rest P-frames after ContainerWriter::FLUSH_NEEDED. for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) { mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]); } mClusterHeaderIndex = 0; mClusterLengthLoc = 0; mClusterBuffs.Clear(); mFlushState &= ~FLUSH_CLUSTER; }
int main(int argc, char *argv[]) { // init the datatype we're using for ebml output unsigned char data[8192]; EbmlGlobal ebml; ebml.buf = data; ebml.offset = 0; ebml.length = 8192; writeHeader(&ebml); { EbmlLoc startSegment; Ebml_StartSubElement(&ebml, &startSegment, Segment); // segment { // segment info EbmlLoc startInfo; Ebml_StartSubElement(&ebml, &startInfo, Info); Ebml_SerializeString(&ebml, 0x4D80, "muxingAppLibMkv"); Ebml_SerializeString(&ebml, 0x5741, "writingAppLibMkv"); Ebml_EndSubElement(&ebml, &startInfo); } { EbmlLoc trackStart; Ebml_StartSubElement(&ebml, &trackStart, Tracks); writeVideoTrack(&ebml, 1, 1, "V_MS/VFW/FOURCC", 320, 240, 29.97); // writeAudioTrack(&ebml,2,1, "A_VORBIS", 32000, 1, NULL, 0); Ebml_EndSubElement(&ebml, &trackStart); } { EbmlLoc clusterStart; Ebml_StartSubElement(&ebml, &clusterStart, Cluster); // cluster Ebml_SerializeUnsigned(&ebml, Timecode, 0); unsigned char someData[4] = {1, 2, 3, 4}; writeSimpleBlock(&ebml, 1, 0, 1, 0, 0, someData, 4); Ebml_EndSubElement(&ebml, &clusterStart); } // end cluster Ebml_EndSubElement(&ebml, &startSegment); } // dump ebml stuff to the file FILE *file_out = fopen("test.mkv", "wb"); size_t bytesWritten = fwrite(data, 1, ebml.offset, file_out); fclose(file_out); return 0; }
void EbmlComposer::GenerateHeader() { // Write the EBML header. EbmlGlobal ebml; // The WEbM header default size usually smaller than 1k. nsAutoArrayPtr<uint8_t> buffer(new uint8_t[DEFAULT_HEADER_SIZE + mCodecPrivateData.Length()]); ebml.buf = buffer.get(); ebml.offset = 0; writeHeader(&ebml); { EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc; Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment); { Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead); // Todo: We don't know the exact sizes of encoded data and ignore this section. Ebml_EndSubElement(&ebml, &ebmlLocseg); writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0); { EbmlLoc trackLoc; Ebml_StartSubElement(&ebml, &trackLoc, Tracks); { // Video if (mWidth > 0 && mHeight > 0) { writeVideoTrack(&ebml, 0x1, 0, "V_VP8", mWidth, mHeight, mFrameRate); } // Audio if (mCodecPrivateData.Length() > 0) { writeAudioTrack(&ebml, 0x2, 0x0, "A_VORBIS", mSampleFreq, mChannels, mCodecPrivateData.Elements(), mCodecPrivateData.Length()); } } Ebml_EndSubElement(&ebml, &trackLoc); } } // The Recording length is unknow and ignore write the whole Segment element size } MOZ_ASSERT_IF(ebml.offset > DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(), "write more data > EBML_BUFFER_SIZE"); mClusterBuffs.AppendElement(); mClusterBuffs.LastElement().SetLength(ebml.offset); memcpy(mClusterBuffs.LastElement().Elements(), ebml.buf, ebml.offset); }
//------------------------------------------------------------------ void write_webm_seek_element(EbmlGlobal *global, unsigned int id, off_t pos) { uint64_t offset = pos - global->position_reference; off_t start; Ebml_StartSubElement(global, &start, Seek); Ebml_SerializeBinary(global, SeekID, id); Ebml_SerializeUnsigned64(global, SeekPosition, offset); Ebml_EndSubElement(global, &start); }
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_file_footer(EbmlGlobal *global, int hash) { off_t start_cues; off_t start_cue_point; off_t start_cue_tracks; unsigned int i; if (global->cluster_open) { Ebml_EndSubElement(global, &global->startCluster); } global->cue_pos = ftello(global->stream); Ebml_StartSubElement(global, &start_cues, Cues); for (i = 0; i < global->cues; i++) { struct cue_entry *cue = &global->cue_list[i]; Ebml_StartSubElement(global, &start_cue_point, CuePoint); Ebml_SerializeUnsigned(global, CueTime, cue->time); Ebml_StartSubElement(global, &start_cue_tracks, CueTrackPositions); Ebml_SerializeUnsigned(global, CueTrack, 1); Ebml_SerializeUnsigned64(global, CueClusterPosition, cue->loc - global->position_reference); Ebml_EndSubElement(global, &start_cue_tracks); Ebml_EndSubElement(global, &start_cue_point); } Ebml_EndSubElement(global, &start_cues); /* Close the Segment. */ Ebml_EndSubElement(global, &global->startSegment); /* Patch up the seek info block. */ write_webm_seek_info(global); /* Patch up the track id. */ fseeko(global->stream, global->track_id_pos, SEEK_SET); Ebml_SerializeUnsigned32(global, TrackUID, hash); fseeko(global->stream, 0, SEEK_END); }
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 writeHeader(EbmlGlobal *glob) { EbmlLoc start; 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); }
//------------------------------------------------------------------ void write_webm_seek_info(EbmlGlobal *global) { off_t pos; off_t start; off_t startInfo; uint64_t frame_time; char version_string[64]; /* Save the current stream pointer. */ pos = ftello(global->stream); if (global->seek_info_pos) { fseeko(global->stream, global->seek_info_pos, SEEK_SET); } else { global->seek_info_pos = pos; } Ebml_StartSubElement(global, &start, SeekHead); write_webm_seek_element(global, Tracks, global->track_pos); write_webm_seek_element(global, Cues, global->cue_pos); write_webm_seek_element(global, Info, global->segment_info_pos); Ebml_EndSubElement(global, &start); /* Create and write the Segment Info. */ strcpy(version_string, "DesktopCapture v1.0 - libVPX "); strncat(version_string, vpx_codec_version_str(), sizeof(version_string) - 1 - strlen(version_string)); frame_time = (uint64_t) 1000 * global->framerate.den / global->framerate.num; global->segment_info_pos = ftello(global->stream); Ebml_StartSubElement(global, &startInfo, Info); Ebml_SerializeUnsigned(global, TimecodeScale, 1000000); Ebml_SerializeFloat(global, Segment_Duration, (double) (global->last_pts_ms + frame_time)); Ebml_SerializeString(global, MuxingApp, version_string); Ebml_SerializeString(global, WritingApp, version_string); Ebml_EndSubElement(global, &startInfo); }
void writeVideoTrack(EbmlGlobal *glob, unsigned int trackNumber, int flagLacing, char *codecId, unsigned int pixelWidth, unsigned int pixelHeight, double frameRate) { EbmlLoc start; Ebml_StartSubElement(glob, &start, TrackEntry); Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); UInt64 trackID = generateTrackID(trackNumber); Ebml_SerializeUnsigned(glob, TrackUID, trackID); Ebml_SerializeString(glob, CodecName, "VP8"); Ebml_SerializeUnsigned(glob, TrackType, 1); Ebml_SerializeString(glob, CodecID, codecId); { EbmlLoc videoStart; Ebml_StartSubElement(glob, &videoStart, Video); Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth); Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight); Ebml_SerializeFloat(glob, FrameRate, frameRate); Ebml_EndSubElement(glob, &videoStart); } Ebml_EndSubElement(glob, &start); }
void EbmlComposer::FinishCluster() { MOZ_ASSERT(mClusterLengthLoc > 0 ); MOZ_ASSERT(mClusterHeaderIndex > 0); for (uint32_t i = 0; i < mClusterBuffs.Length(); i ++ ) { mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]); } mClusterBuffs.Clear(); EbmlGlobal ebml; EbmlLoc ebmlLoc; ebmlLoc.offset = mClusterLengthLoc; ebml.offset = mClusterCanFlushBuffs[mClusterHeaderIndex].Length(); ebml.buf = mClusterCanFlushBuffs[mClusterHeaderIndex].Elements(); Ebml_EndSubElement(&ebml, &ebmlLoc); mClusterHeaderIndex = 0; mClusterLengthLoc = 0; }
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); }