Ejemplo n.º 1
0
void writeVideoTrack(EbmlGlobal *glob, unsigned int trackNumber, int flagLacing,
                     const char *codecId, unsigned int pixelWidth, unsigned int pixelHeight,
                     unsigned int displayWidth, unsigned int displayHeight,
                     double frameRate) {
  EbmlLoc start;
  UInt64 trackID;
  Ebml_StartSubElement(glob, &start, TrackEntry);
  Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber);
  trackID = generateTrackID(trackNumber);
  Ebml_SerializeUnsigned(glob, TrackUID, trackID);
  Ebml_SerializeString(glob, CodecName, "VP8");  // TODO shouldn't be fixed

  Ebml_SerializeUnsigned(glob, TrackType, 1); // video is always 1
  Ebml_SerializeString(glob, CodecID, codecId);
  {
    EbmlLoc videoStart;
    Ebml_StartSubElement(glob, &videoStart, Video);
    Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth);
    Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight);
    if (pixelWidth != displayWidth) {
      Ebml_SerializeUnsigned(glob, DisplayWidth, displayWidth);
    }
    if (pixelHeight != displayHeight) {
      Ebml_SerializeUnsigned(glob, DisplayHeight, displayHeight);
    }
    Ebml_SerializeFloat(glob, FrameRate, frameRate);
    Ebml_EndSubElement(glob, &videoStart); // Video
  }
  Ebml_EndSubElement(glob, &start); // Track Entry
}
Ejemplo n.º 2
0
void EbmlComposer::GenerateHeader()
{
  // Write the EBML header.
  EbmlGlobal ebml;
  // The WEbM header default size usually smaller than 1k.
  auto buffer = MakeUnique<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,
                            mDisplayWidth, mDisplayHeight, mFrameRate);
          }
          // Audio
          if (mCodecPrivateData.Length() > 0) {
            // Extract the pre-skip from mCodecPrivateData
            // then convert it to nanoseconds.
            // Details in OpusTrackEncoder.cpp.
            mCodecDelay =
              (uint64_t)LittleEndian::readUint16(mCodecPrivateData.Elements() + 10)
              * PR_NSEC_PER_SEC / 48000;
            // Fixed 80ms, convert into nanoseconds.
            uint64_t seekPreRoll = 80 * PR_NSEC_PER_MSEC;
            writeAudioTrack(&ebml, 0x2, 0x0, "A_OPUS", mSampleFreq,
                            mChannels, mCodecDelay, seekPreRoll,
                            mCodecPrivateData.Elements(),
                            mCodecPrivateData.Length());
          }
        }
        Ebml_EndSubElement(&ebml, &trackLoc);
      }
    }
    // The Recording length is unknown and
    // ignore write the whole Segment element size
  }
  MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(),
             "write more data > EBML_BUFFER_SIZE");
  auto block = mClusterBuffs.AppendElement();
  block->SetLength(ebml.offset);
  memcpy(block->Elements(), ebml.buf, ebml.offset);
  mFlushState |= FLUSH_METADATA;
}
Ejemplo n.º 3
0
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_file_header(EbmlGlobal *global, const vpx_codec_enc_cfg_t *cfg, const struct vpx_rational *fps)
{
	off_t start;
	off_t trackStart;
	off_t 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(global, &start, EBML);
	Ebml_SerializeUnsigned(global, EBMLVersion, 1);
	Ebml_SerializeUnsigned(global, EBMLReadVersion, 1);
	Ebml_SerializeUnsigned(global, EBMLMaxIDLength, 4);
	Ebml_SerializeUnsigned(global, EBMLMaxSizeLength, 8);
	Ebml_SerializeString(global, DocType, "webm");
	Ebml_SerializeUnsigned(global, DocTypeVersion, 2);
	Ebml_SerializeUnsigned(global, DocTypeReadVersion, 2);
	Ebml_EndSubElement(global, &start);

	/* Open and begin writing the segment element. */
	Ebml_StartSubElement(global, &global->startSegment, Segment);
	global->position_reference = ftello(global->stream);
	global->framerate = *fps;
	write_webm_seek_info(global);

	/* Open and write the Tracks element. */
	global->track_pos = ftello(global->stream);
	Ebml_StartSubElement(global, &trackStart, Tracks);

	/* Open and write the Track entry. */
	Ebml_StartSubElement(global, &start, TrackEntry);
	Ebml_SerializeUnsigned(global, TrackNumber, trackNumber);
	global->track_id_pos = ftello(global->stream);
	Ebml_SerializeUnsigned32(global, TrackUID, trackID);
	Ebml_SerializeUnsigned(global, TrackType, 1);
	Ebml_SerializeString(global, CodecID, "V_VP8");
	Ebml_StartSubElement(global, &videoStart, Video);
	Ebml_SerializeUnsigned(global, PixelWidth, pixelWidth);
	Ebml_SerializeUnsigned(global, PixelHeight, pixelHeight);
	Ebml_SerializeUnsigned(global, StereoMode, STEREO_FORMAT_MONO);
	Ebml_EndSubElement(global, &videoStart);

	/* Close Track entry. */
	Ebml_EndSubElement(global, &start);

	/* Close Tracks element. */
	Ebml_EndSubElement(global, &trackStart);

	/* Segment element remains open. */
}
Ejemplo n.º 5
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,
                            mDisplayWidth, mDisplayHeight, 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 unknown and
    // ignore write the whole Segment element size
  }
  MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(),
             "write more data > EBML_BUFFER_SIZE");
  auto block = mClusterBuffs.AppendElement();
  block->SetLength(ebml.offset);
  memcpy(block->Elements(), ebml.buf, ebml.offset);
  mFlushState |= FLUSH_METADATA;
}
Ejemplo n.º 6
0
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 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);
}
Ejemplo n.º 8
0
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);
}
Ejemplo n.º 10
0
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);
}
Ejemplo n.º 11
0
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);
}
Ejemplo n.º 13
0
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); 
}
Ejemplo n.º 14
0
void
EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
{
  EbmlGlobal ebml;
  ebml.offset = 0;

  if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
    FinishCluster();
  }

  auto block = mClusterBuffs.AppendElement();
  block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
  ebml.buf = block->Elements();

  if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
    EbmlLoc ebmlLoc;
    Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
    MOZ_ASSERT(mClusterBuffs.Length() > 0);
    // current cluster header array index
    mClusterHeaderIndex = mClusterBuffs.Length() - 1;
    mClusterLengthLoc = ebmlLoc.offset;
    mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
    Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode);
    mFlushState |= FLUSH_CLUSTER;
  }

  if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) {
    short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC
                     - mClusterTimecode;
    writeSimpleBlock(&ebml, 0x1, timeCode, aFrame->GetFrameType() ==
                     EncodedFrame::FrameType::VP8_I_FRAME,
                     0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
                     aFrame->GetFrameData().Length());
  } else {
    writeSimpleBlock(&ebml, 0x2, 0, false,
                     0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
                     aFrame->GetFrameData().Length());
  }
  MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE +
             aFrame->GetFrameData().Length(),
             "write more data > EBML_BUFFER_SIZE");
  block->SetLength(ebml.offset);
}
Ejemplo n.º 15
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);
}