root/media/formats/webm/tracks_builder.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetUIntMkvSize
  2. GetUIntSize
  3. MasterElementSize
  4. IntElementSize
  5. DoubleElementSize
  6. StringElementSize
  7. SerializeInt
  8. SerializeDouble
  9. WriteElementId
  10. WriteUInt
  11. WriteMasterElement
  12. WriteIntElement
  13. WriteDoubleElement
  14. WriteStringElement
  15. AddVideoTrack
  16. AddAudioTrack
  17. AddTextTrack
  18. Finish
  19. AddTrackInternal
  20. GetTracksSize
  21. GetTracksPayloadSize
  22. WriteTracks
  23. audio_sampling_frequency_
  24. GetSize
  25. GetVideoPayloadSize
  26. GetAudioPayloadSize
  27. GetPayloadSize
  28. Write

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/formats/webm/tracks_builder.h"

#include "base/logging.h"
#include "media/formats/webm/webm_constants.h"

namespace media {

// Returns size of an integer, formatted using Matroska serialization.
static int GetUIntMkvSize(uint64 value) {
  if (value < 0x07FULL)
    return 1;
  if (value < 0x03FFFULL)
    return 2;
  if (value < 0x01FFFFFULL)
    return 3;
  if (value < 0x0FFFFFFFULL)
    return 4;
  if (value < 0x07FFFFFFFFULL)
    return 5;
  if (value < 0x03FFFFFFFFFFULL)
    return 6;
  if (value < 0x01FFFFFFFFFFFFULL)
    return 7;
  return 8;
}

// Returns the minimium size required to serialize an integer value.
static int GetUIntSize(uint64 value) {
  if (value < 0x0100ULL)
    return 1;
  if (value < 0x010000ULL)
    return 2;
  if (value < 0x01000000ULL)
    return 3;
  if (value < 0x0100000000ULL)
    return 4;
  if (value < 0x010000000000ULL)
    return 5;
  if (value < 0x01000000000000ULL)
    return 6;
  if (value < 0x0100000000000000ULL)
    return 7;
  return 8;
}

static int MasterElementSize(int element_id, int payload_size) {
  return GetUIntSize(element_id) + GetUIntMkvSize(payload_size) + payload_size;
}

static int IntElementSize(int element_id, int value) {
  return GetUIntSize(element_id) + 1 + GetUIntSize(value);
}

static int DoubleElementSize(int element_id) {
  return GetUIntSize(element_id) + 1 + 8;
}

static int StringElementSize(int element_id, const std::string& value) {
 return GetUIntSize(element_id) +
        GetUIntMkvSize(value.length()) +
        value.length();
}

static void SerializeInt(uint8** buf_ptr, int* buf_size_ptr,
                         int64 value, int size) {
  uint8*& buf = *buf_ptr;
  int& buf_size = *buf_size_ptr;

  for (int idx = 1; idx <= size; ++idx) {
    *buf++ = static_cast<uint8>(value >> ((size - idx) * 8));
    --buf_size;
  }
}

static void SerializeDouble(uint8** buf_ptr, int* buf_size_ptr,
                            double value) {
  // Use a union to convert |value| to native endian integer bit pattern.
  union {
    double src;
    int64 dst;
  } tmp;
  tmp.src = value;

  // Write the bytes from native endian |tmp.dst| to big-endian form in |buf|.
  SerializeInt(buf_ptr, buf_size_ptr, tmp.dst, 8);
}

static void WriteElementId(uint8** buf, int* buf_size, int element_id) {
  SerializeInt(buf, buf_size, element_id, GetUIntSize(element_id));
}

static void WriteUInt(uint8** buf, int* buf_size, uint64 value) {
  const int size = GetUIntMkvSize(value);
  value |= (1ULL << (size * 7));  // Matroska formatting
  SerializeInt(buf, buf_size, value, size);
}

static void WriteMasterElement(uint8** buf, int* buf_size,
                               int element_id, int payload_size) {
  WriteElementId(buf, buf_size, element_id);
  WriteUInt(buf, buf_size, payload_size);
}

static void WriteIntElement(uint8** buf, int* buf_size,
                            int element_id, int value) {
  WriteElementId(buf, buf_size, element_id);

  const int size = GetUIntSize(value);
  WriteUInt(buf, buf_size, size);

  SerializeInt(buf, buf_size, value, size);
}

static void WriteDoubleElement(uint8** buf, int* buf_size,
                               int element_id, double value) {
  WriteElementId(buf, buf_size, element_id);
  WriteUInt(buf, buf_size, 8);
  SerializeDouble(buf, buf_size, value);
}

static void WriteStringElement(uint8** buf_ptr, int* buf_size_ptr,
                               int element_id, const std::string& value) {
  uint8*& buf = *buf_ptr;
  int& buf_size = *buf_size_ptr;

  WriteElementId(&buf, &buf_size, element_id);

  const uint64 size = value.length();
  WriteUInt(&buf, &buf_size, size);

  memcpy(buf, value.data(), size);
  buf += size;
  buf_size -= size;
}

TracksBuilder::TracksBuilder(bool allow_invalid_values)
    : allow_invalid_values_(allow_invalid_values) {}
TracksBuilder::TracksBuilder()
    : allow_invalid_values_(false) {}
TracksBuilder::~TracksBuilder() {}

void TracksBuilder::AddVideoTrack(
    int track_num,
    int track_uid,
    const std::string& codec_id,
    const std::string& name,
    const std::string& language,
    int default_duration,
    int video_pixel_width,
    int video_pixel_height) {
  AddTrackInternal(track_num, kWebMTrackTypeVideo, track_uid, codec_id, name,
                   language, default_duration, video_pixel_width,
                   video_pixel_height, -1, -1);
}

void TracksBuilder::AddAudioTrack(
    int track_num,
    int track_uid,
    const std::string& codec_id,
    const std::string& name,
    const std::string& language,
    int default_duration,
    int audio_channels,
    double audio_sampling_frequency) {
  AddTrackInternal(track_num, kWebMTrackTypeAudio, track_uid, codec_id, name,
                   language, default_duration, -1, -1, audio_channels,
                   audio_sampling_frequency);
}

void TracksBuilder::AddTextTrack(
    int track_num,
    int track_uid,
    const std::string& codec_id,
    const std::string& name,
    const std::string& language) {
  AddTrackInternal(track_num, kWebMTrackTypeSubtitlesOrCaptions, track_uid,
                   codec_id, name, language, -1, -1, -1, -1, -1);
}

std::vector<uint8> TracksBuilder::Finish() {
  // Allocate the storage
  std::vector<uint8> buffer;
  buffer.resize(GetTracksSize());

  // Populate the storage with a tracks header
  WriteTracks(&buffer[0], buffer.size());

  return buffer;
}

void TracksBuilder::AddTrackInternal(
    int track_num,
    int track_type,
    int track_uid,
    const std::string& codec_id,
    const std::string& name,
    const std::string& language,
    int default_duration,
    int video_pixel_width,
    int video_pixel_height,
    int audio_channels,
    double audio_sampling_frequency) {
  tracks_.push_back(Track(track_num, track_type, track_uid, codec_id, name,
                          language, default_duration, video_pixel_width,
                          video_pixel_height, audio_channels,
                          audio_sampling_frequency, allow_invalid_values_));
}

int TracksBuilder::GetTracksSize() const {
  return MasterElementSize(kWebMIdTracks, GetTracksPayloadSize());
}

int TracksBuilder::GetTracksPayloadSize() const {
  int payload_size = 0;

  for (TrackList::const_iterator itr = tracks_.begin();
       itr != tracks_.end(); ++itr) {
    payload_size += itr->GetSize();
  }

  return payload_size;
}

void TracksBuilder::WriteTracks(uint8* buf, int buf_size) const {
  WriteMasterElement(&buf, &buf_size, kWebMIdTracks, GetTracksPayloadSize());

  for (TrackList::const_iterator itr = tracks_.begin();
       itr != tracks_.end(); ++itr) {
    itr->Write(&buf, &buf_size);
  }
}

TracksBuilder::Track::Track(int track_num, int track_type, int track_uid,
                            const std::string& codec_id,
                            const std::string& name,
                            const std::string& language,
                            int default_duration,
                            int video_pixel_width, int video_pixel_height,
                            int audio_channels, double audio_sampling_frequency,
                            bool allow_invalid_values)
    : track_num_(track_num),
      track_type_(track_type),
      track_uid_(track_uid),
      codec_id_(codec_id),
      name_(name),
      language_(language),
      default_duration_(default_duration),
      video_pixel_width_(video_pixel_width),
      video_pixel_height_(video_pixel_height),
      audio_channels_(audio_channels),
      audio_sampling_frequency_(audio_sampling_frequency) {
  if (!allow_invalid_values) {
    CHECK_GT(track_num_, 0);
    CHECK_GT(track_type_, 0);
    CHECK_LT(track_type_, 255);
    CHECK_GT(track_uid_, 0);
    if (track_type != kWebMTrackTypeVideo &&
        track_type != kWebMTrackTypeAudio) {
      CHECK_EQ(default_duration_, -1);
    } else {
      CHECK(default_duration_ == -1 || default_duration_ > 0);
    }

    if (track_type == kWebMTrackTypeVideo) {
      CHECK_GT(video_pixel_width_, 0);
      CHECK_GT(video_pixel_height_, 0);
    } else {
      CHECK_EQ(video_pixel_width_, -1);
      CHECK_EQ(video_pixel_height_, -1);
    }

    if (track_type == kWebMTrackTypeAudio) {
      CHECK_GT(audio_channels_, 0);
      CHECK_GT(audio_sampling_frequency_, 0.0);
    } else {
      CHECK_EQ(audio_channels_, -1);
      CHECK_EQ(audio_sampling_frequency_, -1.0);
    }
  }
}

int TracksBuilder::Track::GetSize() const {
  return MasterElementSize(kWebMIdTrackEntry, GetPayloadSize());
}

int TracksBuilder::Track::GetVideoPayloadSize() const {
  int payload_size = 0;

  if (video_pixel_width_ >= 0)
    payload_size += IntElementSize(kWebMIdPixelWidth, video_pixel_width_);
  if (video_pixel_height_ >= 0)
    payload_size += IntElementSize(kWebMIdPixelHeight, video_pixel_height_);

  return payload_size;
}

int TracksBuilder::Track::GetAudioPayloadSize() const {
  int payload_size = 0;

  if (audio_channels_ >= 0)
    payload_size += IntElementSize(kWebMIdChannels, audio_channels_);
  if (audio_sampling_frequency_ >= 0)
    payload_size += DoubleElementSize(kWebMIdSamplingFrequency);

  return payload_size;
}

int TracksBuilder::Track::GetPayloadSize() const {
  int size = 0;

  size += IntElementSize(kWebMIdTrackNumber, track_num_);
  size += IntElementSize(kWebMIdTrackType, track_type_);
  size += IntElementSize(kWebMIdTrackUID, track_uid_);

  if (default_duration_ >= 0)
    size += IntElementSize(kWebMIdDefaultDuration, default_duration_);

  if (!codec_id_.empty())
    size += StringElementSize(kWebMIdCodecID, codec_id_);

  if (!name_.empty())
    size += StringElementSize(kWebMIdName, name_);

  if (!language_.empty())
    size += StringElementSize(kWebMIdLanguage, language_);

  if (GetVideoPayloadSize() > 0) {
    size += MasterElementSize(kWebMIdVideo, GetVideoPayloadSize());
  }

  if (GetAudioPayloadSize() > 0) {
    size += MasterElementSize(kWebMIdAudio, GetAudioPayloadSize());
  }

  return size;
}

void TracksBuilder::Track::Write(uint8** buf, int* buf_size) const {
  WriteMasterElement(buf, buf_size, kWebMIdTrackEntry, GetPayloadSize());

  WriteIntElement(buf, buf_size, kWebMIdTrackNumber, track_num_);
  WriteIntElement(buf, buf_size, kWebMIdTrackType, track_type_);
  WriteIntElement(buf, buf_size, kWebMIdTrackUID, track_uid_);

  if (default_duration_ >= 0)
    WriteIntElement(buf, buf_size, kWebMIdDefaultDuration, default_duration_);

  if (!codec_id_.empty())
    WriteStringElement(buf, buf_size, kWebMIdCodecID, codec_id_);

  if (!name_.empty())
    WriteStringElement(buf, buf_size, kWebMIdName, name_);

  if (!language_.empty())
    WriteStringElement(buf, buf_size, kWebMIdLanguage, language_);

  if (GetVideoPayloadSize() > 0) {
    WriteMasterElement(buf, buf_size, kWebMIdVideo, GetVideoPayloadSize());

    if (video_pixel_width_ >= 0)
      WriteIntElement(buf, buf_size, kWebMIdPixelWidth, video_pixel_width_);

    if (video_pixel_height_ >= 0)
      WriteIntElement(buf, buf_size, kWebMIdPixelHeight, video_pixel_height_);
  }

  if (GetAudioPayloadSize() > 0) {
    WriteMasterElement(buf, buf_size, kWebMIdAudio, GetAudioPayloadSize());

    if (audio_channels_ >= 0)
      WriteIntElement(buf, buf_size, kWebMIdChannels, audio_channels_);

    if (audio_sampling_frequency_ >= 0) {
      WriteDoubleElement(buf, buf_size, kWebMIdSamplingFrequency,
          audio_sampling_frequency_);
    }
  }
}

}  // namespace media

/* [<][>][^][v][top][bottom][index][help] */