blob: 51c9f142eed77e9295df1df2bcf26118d59df26e [file] [log] [blame]
// Copyright 2017 Google Inc. 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.
#ifndef COBALT_MEDIA_BASE_VIDEO_DMP_READER_H_
#define COBALT_MEDIA_BASE_VIDEO_DMP_READER_H_
// This file is deliberately not using any Cobalt/Starboard specific API so it
// can be used in an independent application.
#include <algorithm>
#include <string>
#include <vector>
// File: <BOM> <Record>*
// BOM: 0x76543210
// Record: <4 bytes type> + <4 bytes size> + <|size| bytes binary data>
// type: 0: audio config, 1: video config, 2: eme init data,
// 3: audio access unit, 4: video access unit.
// audio/video access unit;
// <8 bytes time stamp in microseconds>
// <4 bytes size of key_id> + |size| bytes of key id
// <4 bytes size of iv> + |size| bytes of iv
// <4 bytes count> (0 for non-encrypted AU/frame)
// (subsample: 4 bytes clear size, 4 bytes encrypted size) * |count|
// <4 bytes size>
// |size| bytes encoded binary data
class VideoDmpReader {
public:
enum {
kRecordTypeAudioConfig,
kRecordTypeVideoConfig,
kRecordTypeEmeInitData,
kRecordTypeAudioAccessUnit,
kRecordTypeVideoAccessUnit,
};
enum AccessUnitType { kAccessUnitTypeAudio, kAccessUnitTypeVideo };
struct Subsample {
uint32_t clear_bytes;
uint32_t encrypted_bytes;
};
typedef std::vector<Subsample> Subsamples;
typedef std::vector<uint8_t> EmeInitData;
class AccessUnit {
public:
AccessUnit(AccessUnitType access_unit_type, int64_t timestamp,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& iv, const Subsamples& subsamples,
std::vector<uint8_t> data)
: access_unit_type_(access_unit_type),
timestamp_(timestamp),
key_id_(key_id),
iv_(iv),
subsamples_(subsamples),
data_(std::move(data)) {}
AccessUnitType access_unit_type() const { return access_unit_type_; }
int64_t timestamp() const { return timestamp_; }
// Returns empty vector when the AU is not encrypted.
const std::vector<uint8_t>& key_id() const { return key_id_; }
const std::vector<uint8_t>& iv() const { return iv_; }
const Subsamples& subsamples() const { return subsamples_; }
const std::vector<uint8_t> data() const { return data_; }
private:
AccessUnitType access_unit_type_;
int64_t timestamp_;
std::vector<uint8_t> key_id_;
std::vector<uint8_t> iv_;
Subsamples subsamples_;
std::vector<uint8_t> data_;
};
static const uint32_t kBOM = 0x76543210;
VideoDmpReader() : reverse_byte_order_(false) {}
// Abstract function implemented by the derived class to read data. When
// the return value is less than |bytes_to_read|, the stream reaches the end.
virtual size_t Read(void* buffer, size_t bytes_to_read) = 0;
virtual void ReportFatalError() = 0;
const std::vector<EmeInitData> eme_init_datas() const {
return eme_init_datas_;
}
const std::vector<AccessUnit>& access_units() const { return access_units_; }
void Parse() {
uint32_t bom;
ReadChecked(&bom);
if (bom != kBOM) {
std::reverse(reinterpret_cast<uint8_t*>(&bom),
reinterpret_cast<uint8_t*>(&bom + 1));
if (bom != kBOM) {
ReportFatalError();
return;
}
reverse_byte_order_ = true;
}
uint32_t type;
EmeInitData eme_init_data;
while (ReadUnchecked(&type)) {
uint32_t size;
switch (type) {
case kRecordTypeAudioConfig:
ReadChecked(&size);
if (size != 0) {
ReportFatalError();
}
break;
case kRecordTypeVideoConfig:
ReadChecked(&size);
if (size != 0) {
ReportFatalError();
}
break;
case kRecordTypeEmeInitData:
ReadChecked(&eme_init_data);
eme_init_datas_.push_back(eme_init_data);
break;
case kRecordTypeAudioAccessUnit:
ReadChecked(&size);
ReadAndAppendAccessUnitChecked(kAccessUnitTypeAudio);
break;
case kRecordTypeVideoAccessUnit:
ReadChecked(&size);
ReadAndAppendAccessUnitChecked(kAccessUnitTypeVideo);
break;
}
}
}
private:
void ReadAndAppendAccessUnitChecked(AccessUnitType access_unit_type) {
int64_t timestamp;
ReadChecked(&timestamp);
std::vector<uint8_t> key_id, iv;
ReadChecked(&key_id);
ReadChecked(&iv);
uint32_t subsample_count;
ReadChecked(&subsample_count);
Subsamples subsamples(subsample_count);
for (auto& subsample : subsamples) {
ReadChecked(&subsample.clear_bytes);
ReadChecked(&subsample.encrypted_bytes);
}
std::vector<uint8_t> data;
ReadChecked(&data);
access_units_.emplace_back(access_unit_type, timestamp, key_id, iv,
subsamples, std::move(data));
}
template <typename T>
bool ReadUnchecked(T* value) {
if (!value) {
ReportFatalError();
return false;
}
int bytes_to_read = static_cast<int>(sizeof(*value));
int bytes_read = Read(value, bytes_to_read);
if (reverse_byte_order_) {
std::reverse(reinterpret_cast<uint8_t*>(value),
reinterpret_cast<uint8_t*>(value + 1));
}
return bytes_to_read == bytes_read;
}
template <typename T>
void ReadChecked(T* value) {
if (!ReadUnchecked(value)) {
ReportFatalError();
}
}
void ReadChecked(std::vector<uint8_t>* value) {
if (!value) {
ReportFatalError();
}
uint32_t size;
ReadChecked(&size);
value->resize(size);
if (size == 0) {
return;
}
int bytes_read = Read(value->data(), size);
if (bytes_read != size) {
ReportFatalError();
}
}
bool reverse_byte_order_;
std::vector<EmeInitData> eme_init_datas_;
std::vector<AccessUnit> access_units_;
};
#endif // COBALT_MEDIA_BASE_VIDEO_DMP_READER_H_