blob: cf5b54ee8cbd435ff5f3ae97f1d0d424081e5470 [file] [log] [blame]
// Copyright 2018 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/shared/starboard/player/video_dmp_reader.h"
#include <algorithm>
#include <functional>
namespace starboard {
namespace shared {
namespace starboard {
namespace player {
namespace video_dmp {
namespace {
template <typename AccessUnit>
int64_t CalculateAverageBitrate(const std::vector<AccessUnit>& access_units) {
if (access_units.empty()) {
return 0;
}
if (access_units.size() == 1) {
// A guestimated bitrate of 1k.
return 1024;
}
SbTime duration =
access_units.back().timestamp() - access_units.front().timestamp();
SB_DCHECK(duration > 0);
int64_t total_bitrate = 0;
for (auto& au : access_units) {
total_bitrate += au.data().size();
}
return total_bitrate * 8 * kSbTimeSecond / duration;
}
static void DeallocateSampleFunc(SbPlayer player,
void* context,
const void* sample_buffer) {
}
SbPlayerSampleInfo ConvertToPlayerSampleInfo(
const VideoDmpReader::AudioAccessUnit& audio_unit) {
SbPlayerSampleInfo sample_info = {};
sample_info.buffer = audio_unit.data().data();
sample_info.buffer_size = static_cast<int>(audio_unit.data().size());
sample_info.timestamp = audio_unit.timestamp();
sample_info.drm_info = audio_unit.drm_sample_info();
#if SB_API_VERSION >= 11
sample_info.type = kSbMediaTypeAudio;
sample_info.audio_sample_info = audio_unit.audio_sample_info();
#else // SB_API_VERSION >= 11
sample_info.video_sample_info = NULL;
#endif // SB_API_VERSION >= 11
return sample_info;
}
SbPlayerSampleInfo ConvertToPlayerSampleInfo(
const VideoDmpReader::VideoAccessUnit& video_unit) {
SbPlayerSampleInfo sample_info = {};
sample_info.buffer = video_unit.data().data();
sample_info.buffer_size = static_cast<int>(video_unit.data().size());
sample_info.timestamp = video_unit.timestamp();
sample_info.drm_info = video_unit.drm_sample_info();
#if SB_API_VERSION >= 11
sample_info.type = kSbMediaTypeVideo;
sample_info.video_sample_info = video_unit.video_sample_info();
#else // SB_API_VERSION >= 11
sample_info.video_sample_info = &video_unit.video_sample_info();
#endif // SB_API_VERSION >= 11
return sample_info;
}
} // namespace
using std::placeholders::_1;
using std::placeholders::_2;
bool VideoDmpReader::Registry::GetDmpInfo(const char* filename,
DmpInfo* dmp_info) const {
SB_DCHECK(filename);
SB_DCHECK(dmp_info);
ScopedLock scoped_lock(mutex_);
auto iter = dmp_infos_.find(filename);
if (iter == dmp_infos_.end()) {
return false;
}
*dmp_info = iter->second;
return true;
}
void VideoDmpReader::Registry::Register(const char* filename,
const DmpInfo& dmp_info) {
SB_DCHECK(filename);
ScopedLock scoped_lock(mutex_);
SB_DCHECK(dmp_infos_.find(filename) == dmp_infos_.end());
dmp_infos_[filename] = dmp_info;
}
VideoDmpReader::VideoDmpReader(
const char* filename,
ReadOnDemandOptions read_on_demand_options /*= kDisableReadOnDemand*/)
: file_reader_(filename, 1024 * 1024),
read_cb_(std::bind(&FileCacheReader::Read, &file_reader_, _1, _2)),
allow_read_on_demand_(read_on_demand_options == kEnableReadOnDemand) {
bool already_cached = GetRegistry()->GetDmpInfo(filename, &dmp_info_);
if (already_cached && allow_read_on_demand_) {
// This is necessary as the current implementation assumes that the address
// of any access units are never changed during the life time of the object,
// and keep using it without explicit reference.
audio_access_units_.reserve(dmp_info_.audio_access_units_size);
video_access_units_.reserve(dmp_info_.video_access_units_size);
return;
}
Parse();
if (!already_cached) {
GetRegistry()->Register(filename, dmp_info_);
}
}
VideoDmpReader::~VideoDmpReader() {}
SbPlayerSampleInfo VideoDmpReader::GetPlayerSampleInfo(SbMediaType type,
size_t index) {
EnsureSampleLoaded(type, index);
switch (type) {
case kSbMediaTypeAudio: {
SB_DCHECK(index < audio_access_units_.size());
const AudioAccessUnit& audio_au = audio_access_units_[index];
return ConvertToPlayerSampleInfo(audio_au);
}
case kSbMediaTypeVideo: {
SB_DCHECK(index < video_access_units_.size());
const VideoAccessUnit& video_au = video_access_units_[index];
return ConvertToPlayerSampleInfo(video_au);
}
}
SB_NOTREACHED() << "Unhandled SbMediaType";
return SbPlayerSampleInfo();
}
const SbMediaAudioSampleInfo& VideoDmpReader::GetAudioSampleInfo(size_t index) {
EnsureSampleLoaded(kSbMediaTypeAudio, index);
SB_DCHECK(index < audio_access_units_.size());
const AudioAccessUnit& au = audio_access_units_[index];
return au.audio_sample_info();
}
void VideoDmpReader::ParseHeader(uint32_t* dmp_writer_version) {
SB_DCHECK(dmp_writer_version);
SB_DCHECK(!reverse_byte_order_.has_engaged());
int64_t file_size = file_reader_.GetSize();
SB_CHECK(file_size >= 0);
reverse_byte_order_ = false;
uint32_t byte_order_mark;
Read(read_cb_, reverse_byte_order_.value(), &byte_order_mark);
if (byte_order_mark != kByteOrderMark) {
std::reverse(reinterpret_cast<uint8_t*>(&byte_order_mark),
reinterpret_cast<uint8_t*>(&byte_order_mark + 1));
SB_CHECK(byte_order_mark == kByteOrderMark);
reverse_byte_order_ = true;
}
Read(read_cb_, reverse_byte_order_.value(), dmp_writer_version);
}
bool VideoDmpReader::ParseOneRecord() {
uint32_t type;
int bytes_read = read_cb_(&type, sizeof(type));
if (bytes_read != sizeof(type)) {
// Read an invalid number of bytes (corrupt file), or we read zero bytes
// (end of file). Return false to signal the end of reading.
return false;
}
if (reverse_byte_order_.value()) {
std::reverse(reinterpret_cast<uint8_t*>(&type),
reinterpret_cast<uint8_t*>(&type + 1));
}
switch (type) {
case kRecordTypeAudioConfig:
Read(read_cb_, reverse_byte_order_.value(), &dmp_info_.audio_codec);
if (dmp_info_.audio_codec != kSbMediaAudioCodecNone) {
Read(read_cb_, reverse_byte_order_.value(),
&dmp_info_.audio_sample_info);
}
break;
case kRecordTypeVideoConfig:
Read(read_cb_, reverse_byte_order_.value(), &dmp_info_.video_codec);
break;
case kRecordTypeAudioAccessUnit:
audio_access_units_.push_back(ReadAudioAccessUnit());
break;
case kRecordTypeVideoAccessUnit:
video_access_units_.push_back(ReadVideoAccessUnit());
break;
default:
SB_NOTREACHED() << type;
return false;
}
return true;
}
void VideoDmpReader::Parse() {
SB_DCHECK(!reverse_byte_order_.has_engaged());
uint32_t dmp_writer_version = 0;
ParseHeader(&dmp_writer_version);
if (dmp_writer_version != kSupportedWriterVersion) {
SB_LOG(ERROR) << "Unsupported input dmp file(" << dmp_writer_version
<< "). Please regenerate dmp files with"
<< " right dmp writer. Currently support version "
<< kSupportedWriterVersion << ".";
return;
}
while (ParseOneRecord()) {
}
dmp_info_.audio_access_units_size = audio_access_units_.size();
dmp_info_.audio_bitrate = CalculateAverageBitrate(audio_access_units_);
dmp_info_.video_access_units_size = video_access_units_.size();
dmp_info_.video_bitrate = CalculateAverageBitrate(video_access_units_);
// Guestimate the video fps.
if (video_access_units_.size() > 1) {
SbTime first_timestamp = video_access_units_.front().timestamp();
SbTime second_timestamp = video_access_units_.back().timestamp();
for (const auto& au : video_access_units_) {
if (au.timestamp() != first_timestamp &&
au.timestamp() < second_timestamp) {
second_timestamp = au.timestamp();
}
}
SB_DCHECK(first_timestamp < second_timestamp);
dmp_info_.video_fps = kSbTimeSecond / (second_timestamp - first_timestamp);
}
}
void VideoDmpReader::EnsureSampleLoaded(SbMediaType type, size_t index) {
if (!reverse_byte_order_.has_engaged()) {
uint32_t dmp_writer_version = 0;
ParseHeader(&dmp_writer_version);
SB_DCHECK(dmp_writer_version == kSupportedWriterVersion);
}
if (type == kSbMediaTypeAudio) {
while (index >= audio_access_units_.size() && ParseOneRecord()) {
}
SB_CHECK(index < audio_access_units_.size());
} else {
SB_DCHECK(type == kSbMediaTypeVideo);
while (index >= video_access_units_.size() && ParseOneRecord()) {
}
SB_CHECK(index < video_access_units_.size());
}
}
VideoDmpReader::AudioAccessUnit VideoDmpReader::ReadAudioAccessUnit() {
SbTime timestamp;
Read(read_cb_, reverse_byte_order_.value(), &timestamp);
bool drm_sample_info_present;
Read(read_cb_, reverse_byte_order_.value(), &drm_sample_info_present);
SbDrmSampleInfoWithSubSampleMapping drm_sample_info;
if (drm_sample_info_present) {
Read(read_cb_, reverse_byte_order_.value(), &drm_sample_info);
}
uint32_t size;
Read(read_cb_, reverse_byte_order_.value(), &size);
std::vector<uint8_t> data(size);
Read(read_cb_, data.data(), size);
SbMediaAudioSampleInfoWithConfig audio_sample_info;
Read(read_cb_, reverse_byte_order_.value(), &audio_sample_info);
return AudioAccessUnit(timestamp,
drm_sample_info_present ? &drm_sample_info : NULL,
std::move(data), audio_sample_info);
}
VideoDmpReader::VideoAccessUnit VideoDmpReader::ReadVideoAccessUnit() {
SbTime timestamp;
Read(read_cb_, reverse_byte_order_.value(), &timestamp);
bool drm_sample_info_present;
Read(read_cb_, reverse_byte_order_.value(), &drm_sample_info_present);
SbDrmSampleInfoWithSubSampleMapping drm_sample_info;
if (drm_sample_info_present) {
Read(read_cb_, reverse_byte_order_.value(), &drm_sample_info);
}
uint32_t size;
Read(read_cb_, reverse_byte_order_.value(), &size);
std::vector<uint8_t> data(size);
Read(read_cb_, data.data(), size);
SbMediaVideoSampleInfoWithOptionalColorMetadata video_sample_info;
Read(read_cb_, reverse_byte_order_.value(), &video_sample_info);
return VideoAccessUnit(timestamp,
drm_sample_info_present ? &drm_sample_info : NULL,
std::move(data), video_sample_info);
}
// static
VideoDmpReader::Registry* VideoDmpReader::GetRegistry() {
static Registry s_registry;
return &s_registry;
}
} // namespace video_dmp
} // namespace player
} // namespace starboard
} // namespace shared
} // namespace starboard