Add TextSample to MediaParser.

In addition to the MediaSample handling of the MediaParser, this now
adds callbacks for TextSample.  This allows reading text streams from
the media files.

Change-Id: I6c00e286e98bc9aafe05b99cf2f7ce6f89d167a9
This commit is contained in:
Jacob Trimble 2020-07-01 15:43:44 -07:00
parent 748e7e0056
commit ba33a63693
17 changed files with 157 additions and 80 deletions

View File

@ -20,6 +20,7 @@ namespace media {
class KeySource;
class MediaSample;
class StreamInfo;
class TextSample;
class MediaParser {
public:
@ -39,18 +40,31 @@ class MediaParser {
/// @return true if the sample is accepted, false if something was wrong
/// with the sample and a parsing error should be signaled.
typedef base::Callback<bool(uint32_t track_id,
const std::shared_ptr<MediaSample>& media_sample)>
NewSampleCB;
std::shared_ptr<MediaSample> media_sample)>
NewMediaSampleCB;
/// Called when a new text sample has been parsed.
/// @param track_id is the track id of the new sample.
/// @param text_sample is the new text sample.
/// @return true if the sample is accepted, false if something was wrong
/// with the sample and a parsing error should be signaled.
typedef base::Callback<bool(uint32_t track_id,
std::shared_ptr<TextSample> text_sample)>
NewTextSampleCB;
/// Initialize the parser with necessary callbacks. Must be called before any
/// data is passed to Parse().
/// @param init_cb will be called once enough data has been parsed to
/// determine the initial stream configurations.
/// @param new_sample_cb will be called each time a new media sample is
/// available from the parser. May be NULL, and caller retains
/// ownership.
/// @param new_media_sample_cb will be called each time a new media sample is
/// available from the parser.
/// @param new_text_sample_cb will be called each time a new text sample is
/// available from the parser.
/// @param decryption_key_source the key source to decrypt the frames. May be
/// NULL, and caller retains ownership.
virtual void Init(const InitCB& init_cb,
const NewSampleCB& new_sample_cb,
const NewMediaSampleCB& new_media_sample_cb,
const NewTextSampleCB& new_text_sample_cb,
KeySource* decryption_key_source) = 0;
/// Flush data currently in the parser and put the parser in a state where it

View File

@ -147,12 +147,6 @@ void Demuxer::SetLanguageOverride(const std::string& stream_label,
language_overrides_[stream_index] = language_override;
}
Demuxer::QueuedSample::QueuedSample(uint32_t local_track_id,
std::shared_ptr<MediaSample> local_sample)
: track_id(local_track_id), sample(local_sample) {}
Demuxer::QueuedSample::~QueuedSample() {}
Status Demuxer::InitializeParser() {
DCHECK(!media_file_);
DCHECK(!all_streams_ready_);
@ -213,8 +207,10 @@ Status Demuxer::InitializeParser() {
return Status(error::UNIMPLEMENTED, "Container not supported.");
}
parser_->Init(base::Bind(&Demuxer::ParserInitEvent, base::Unretained(this)),
base::Bind(&Demuxer::NewSampleEvent, base::Unretained(this)),
parser_->Init(
base::Bind(&Demuxer::ParserInitEvent, base::Unretained(this)),
base::Bind(&Demuxer::NewMediaSampleEvent, base::Unretained(this)),
base::Bind(&Demuxer::NewTextSampleEvent, base::Unretained(this)),
key_source_.get());
// Handle trailing 'moov'.
@ -294,31 +290,56 @@ void Demuxer::ParserInitEvent(
all_streams_ready_ = true;
}
bool Demuxer::NewSampleEvent(uint32_t track_id,
const std::shared_ptr<MediaSample>& sample) {
bool Demuxer::NewMediaSampleEvent(uint32_t track_id,
std::shared_ptr<MediaSample> sample) {
if (!all_streams_ready_) {
if (queued_samples_.size() >= kQueuedSamplesLimit) {
if (queued_media_samples_.size() >= kQueuedSamplesLimit) {
LOG(ERROR) << "Queued samples limit reached: " << kQueuedSamplesLimit;
return false;
}
queued_samples_.push_back(QueuedSample(track_id, sample));
queued_media_samples_.emplace_back(track_id, sample);
return true;
}
if (!init_event_status_.ok()) {
return false;
}
while (!queued_samples_.empty()) {
if (!PushSample(queued_samples_.front().track_id,
queued_samples_.front().sample)) {
while (!queued_media_samples_.empty()) {
if (!PushMediaSample(queued_media_samples_.front().track_id,
queued_media_samples_.front().sample)) {
return false;
}
queued_samples_.pop_front();
queued_media_samples_.pop_front();
}
return PushSample(track_id, sample);
return PushMediaSample(track_id, sample);
}
bool Demuxer::PushSample(uint32_t track_id,
const std::shared_ptr<MediaSample>& sample) {
bool Demuxer::NewTextSampleEvent(uint32_t track_id,
std::shared_ptr<TextSample> sample) {
if (!all_streams_ready_) {
if (queued_text_samples_.size() >= kQueuedSamplesLimit) {
LOG(ERROR) << "Queued samples limit reached: " << kQueuedSamplesLimit;
return false;
}
queued_text_samples_.emplace_back(track_id, sample);
return true;
}
if (!init_event_status_.ok()) {
return false;
}
while (!queued_text_samples_.empty()) {
if (!PushTextSample(queued_text_samples_.front().track_id,
queued_text_samples_.front().sample)) {
return false;
}
queued_text_samples_.pop_front();
}
return PushTextSample(track_id, sample);
}
bool Demuxer::PushMediaSample(uint32_t track_id,
std::shared_ptr<MediaSample> sample) {
auto stream_index_iter = track_id_to_stream_index_map_.find(track_id);
if (stream_index_iter == track_id_to_stream_index_map_.end()) {
LOG(ERROR) << "Track " << track_id << " not found.";
@ -330,8 +351,27 @@ bool Demuxer::PushSample(uint32_t track_id,
if (!status.ok()) {
LOG(ERROR) << "Failed to process sample " << stream_index_iter->second
<< " " << status;
return false;
}
return status.ok();
return true;
}
bool Demuxer::PushTextSample(uint32_t track_id,
std::shared_ptr<TextSample> sample) {
auto stream_index_iter = track_id_to_stream_index_map_.find(track_id);
if (stream_index_iter == track_id_to_stream_index_map_.end()) {
LOG(ERROR) << "Track " << track_id << " not found.";
return false;
}
if (stream_index_iter->second == kInvalidStreamIndex)
return true;
Status status = DispatchTextSample(stream_index_iter->second, sample);
if (!status.ok()) {
LOG(ERROR) << "Failed to process sample " << stream_index_iter->second
<< " " << status;
return false;
}
return true;
}
Status Demuxer::Parse() {

View File

@ -94,12 +94,15 @@ class Demuxer : public OriginHandler {
Demuxer(const Demuxer&) = delete;
Demuxer& operator=(const Demuxer&) = delete;
template <typename T>
struct QueuedSample {
QueuedSample(uint32_t track_id, std::shared_ptr<MediaSample> sample);
~QueuedSample();
QueuedSample(uint32_t track_id, std::shared_ptr<T> sample)
: track_id(track_id), sample(sample) {}
~QueuedSample() {}
uint32_t track_id;
std::shared_ptr<MediaSample> sample;
std::shared_ptr<T> sample;
};
// Initialize the parser. This method primes the demuxer by parsing portions
@ -112,11 +115,13 @@ class Demuxer : public OriginHandler {
// Parser new sample event handler. Queues the samples if init event has not
// been received, otherwise calls PushSample() to push the sample to
// corresponding stream.
bool NewSampleEvent(uint32_t track_id,
const std::shared_ptr<MediaSample>& sample);
bool NewMediaSampleEvent(uint32_t track_id,
std::shared_ptr<MediaSample> sample);
bool NewTextSampleEvent(uint32_t track_id,
std::shared_ptr<TextSample> sample);
// Helper function to push the sample to corresponding stream.
bool PushSample(uint32_t track_id,
const std::shared_ptr<MediaSample>& sample);
bool PushMediaSample(uint32_t track_id, std::shared_ptr<MediaSample> sample);
bool PushTextSample(uint32_t track_id, std::shared_ptr<TextSample> sample);
// Read from the source and send it to the parser.
Status Parse();
@ -126,7 +131,8 @@ class Demuxer : public OriginHandler {
// A stream is considered ready after receiving the stream info.
bool all_streams_ready_ = false;
// Queued samples received in NewSampleEvent() before ParserInitEvent().
std::deque<QueuedSample> queued_samples_;
std::deque<QueuedSample<MediaSample>> queued_media_samples_;
std::deque<QueuedSample<TextSample>> queued_text_samples_;
std::unique_ptr<MediaParser> parser_;
// TrackId -> StreamIndex map.
std::map<uint32_t, size_t> track_id_to_stream_index_map_;

View File

@ -149,17 +149,17 @@ Mp2tMediaParser::Mp2tMediaParser()
Mp2tMediaParser::~Mp2tMediaParser() {}
void Mp2tMediaParser::Init(
const InitCB& init_cb,
const NewSampleCB& new_sample_cb,
void Mp2tMediaParser::Init(const InitCB& init_cb,
const NewMediaSampleCB& new_media_sample_cb,
const NewTextSampleCB& new_text_sample_cb,
KeySource* decryption_key_source) {
DCHECK(!is_initialized_);
DCHECK(init_cb_.is_null());
DCHECK(!init_cb.is_null());
DCHECK(!new_sample_cb.is_null());
DCHECK(!new_media_sample_cb.is_null());
init_cb_ = init_cb;
new_sample_cb_ = new_sample_cb;
new_sample_cb_ = new_media_sample_cb;
}
bool Mp2tMediaParser::Flush() {

View File

@ -34,7 +34,8 @@ class Mp2tMediaParser : public MediaParser {
/// @name MediaParser implementation overrides.
/// @{
void Init(const InitCB& init_cb,
const NewSampleCB& new_sample_cb,
const NewMediaSampleCB& new_media_sample_cb,
const NewTextSampleCB& new_text_sample_cb,
KeySource* decryption_key_source) override;
bool Flush() override WARN_UNUSED_RESULT;
bool Parse(const uint8_t* buf, int size) override WARN_UNUSED_RESULT;
@ -74,7 +75,7 @@ class Mp2tMediaParser : public MediaParser {
// List of callbacks.
InitCB init_cb_;
NewSampleCB new_sample_cb_;
NewMediaSampleCB new_sample_cb_;
bool sbr_in_mimetype_;

View File

@ -69,8 +69,7 @@ class Mp2tMediaParserTest : public testing::Test {
}
}
bool OnNewSample(uint32_t track_id,
const std::shared_ptr<MediaSample>& sample) {
bool OnNewSample(uint32_t track_id, std::shared_ptr<MediaSample> sample) {
StreamMap::const_iterator stream = stream_map_.find(track_id);
EXPECT_NE(stream_map_.end(), stream);
if (stream != stream_map_.end()) {
@ -97,11 +96,15 @@ class Mp2tMediaParserTest : public testing::Test {
return true;
}
bool OnNewTextSample(uint32_t track_id, std::shared_ptr<TextSample> sample) {
return false;
}
void InitializeParser() {
parser_->Init(
base::Bind(&Mp2tMediaParserTest::OnInit,
base::Unretained(this)),
base::Bind(&Mp2tMediaParserTest::OnNewSample,
base::Bind(&Mp2tMediaParserTest::OnInit, base::Unretained(this)),
base::Bind(&Mp2tMediaParserTest::OnNewSample, base::Unretained(this)),
base::Bind(&Mp2tMediaParserTest::OnNewTextSample,
base::Unretained(this)),
NULL);
}

View File

@ -179,16 +179,17 @@ MP4MediaParser::MP4MediaParser()
MP4MediaParser::~MP4MediaParser() {}
void MP4MediaParser::Init(const InitCB& init_cb,
const NewSampleCB& new_sample_cb,
const NewMediaSampleCB& new_media_sample_cb,
const NewTextSampleCB& new_text_sample_cb,
KeySource* decryption_key_source) {
DCHECK_EQ(state_, kWaitingForInit);
DCHECK(init_cb_.is_null());
DCHECK(!init_cb.is_null());
DCHECK(!new_sample_cb.is_null());
DCHECK(!new_media_sample_cb.is_null());
ChangeState(kParsingBoxes);
init_cb_ = init_cb;
new_sample_cb_ = new_sample_cb;
new_sample_cb_ = new_media_sample_cb;
decryption_key_source_ = decryption_key_source;
if (decryption_key_source)
decryptor_source_.reset(new DecryptorSource(decryption_key_source));

View File

@ -35,7 +35,8 @@ class MP4MediaParser : public MediaParser {
/// @name MediaParser implementation overrides.
/// @{
void Init(const InitCB& init_cb,
const NewSampleCB& new_sample_cb,
const NewMediaSampleCB& new_media_sample_cb,
const NewTextSampleCB& new_text_sample_cb,
KeySource* decryption_key_source) override;
bool Flush() override WARN_UNUSED_RESULT;
bool Parse(const uint8_t* buf, int size) override WARN_UNUSED_RESULT;
@ -82,7 +83,7 @@ class MP4MediaParser : public MediaParser {
State state_;
InitCB init_cb_;
NewSampleCB new_sample_cb_;
NewMediaSampleCB new_sample_cb_;
KeySource* decryption_key_source_;
std::unique_ptr<DecryptorSource> decryptor_source_;

View File

@ -82,18 +82,22 @@ class MP4MediaParserTest : public testing::Test {
num_samples_ = 0;
}
bool NewSampleF(uint32_t track_id,
const std::shared_ptr<MediaSample>& sample) {
bool NewSampleF(uint32_t track_id, std::shared_ptr<MediaSample> sample) {
DVLOG(2) << "Track Id: " << track_id << " "
<< sample->ToString();
++num_samples_;
return true;
}
bool NewTextSampleF(uint32_t track_id, std::shared_ptr<TextSample> sample) {
return false;
}
void InitializeParser(KeySource* decryption_key_source) {
parser_->Init(
base::Bind(&MP4MediaParserTest::InitF, base::Unretained(this)),
base::Bind(&MP4MediaParserTest::NewSampleF, base::Unretained(this)),
base::Bind(&MP4MediaParserTest::NewTextSampleF, base::Unretained(this)),
decryption_key_source);
}

View File

@ -37,7 +37,7 @@ WebMClusterParser::WebMClusterParser(
const std::set<int64_t>& ignored_tracks,
const std::string& audio_encryption_key_id,
const std::string& video_encryption_key_id,
const MediaParser::NewSampleCB& new_sample_cb,
const MediaParser::NewMediaSampleCB& new_sample_cb,
const MediaParser::InitCB& init_cb,
KeySource* decryption_key_source)
: timecode_multiplier_(timecode_scale /
@ -482,10 +482,11 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
return track->EmitBuffer(buffer);
}
WebMClusterParser::Track::Track(int track_num,
WebMClusterParser::Track::Track(
int track_num,
bool is_video,
int64_t default_duration,
const MediaParser::NewSampleCB& new_sample_cb)
const MediaParser::NewMediaSampleCB& new_sample_cb)
: track_num_(track_num),
is_video_(is_video),
default_duration_(default_duration),

View File

@ -41,7 +41,7 @@ class WebMClusterParser : public WebMParserClient {
Track(int track_num,
bool is_video,
int64_t default_duration,
const MediaParser::NewSampleCB& new_sample_cb);
const MediaParser::NewMediaSampleCB& new_sample_cb);
~Track();
int track_num() const { return track_num_; }
@ -89,7 +89,7 @@ class WebMClusterParser : public WebMParserClient {
// only if |default_duration_| is kNoTimestamp.
int64_t estimated_next_frame_duration_;
MediaParser::NewSampleCB new_sample_cb_;
MediaParser::NewMediaSampleCB new_sample_cb_;
};
typedef std::map<int, Track> TextTrackMap;
@ -129,7 +129,7 @@ class WebMClusterParser : public WebMParserClient {
const std::set<int64_t>& ignored_tracks,
const std::string& audio_encryption_key_id,
const std::string& video_encryption_key_id,
const MediaParser::NewSampleCB& new_sample_cb,
const MediaParser::NewMediaSampleCB& new_sample_cb,
const MediaParser::InitCB& init_cb,
KeySource* decryption_key_source);
~WebMClusterParser() override;

View File

@ -377,8 +377,7 @@ class WebMClusterParserTest : public testing::Test {
streams_from_init_event_ = stream_info;
}
bool NewSampleEvent(uint32_t track_id,
const std::shared_ptr<MediaSample>& sample) {
bool NewSampleEvent(uint32_t track_id, std::shared_ptr<MediaSample> sample) {
switch (track_id) {
case kAudioTrackNum:
audio_buffers_.push_back(sample);

View File

@ -26,16 +26,17 @@ WebMMediaParser::WebMMediaParser()
WebMMediaParser::~WebMMediaParser() {}
void WebMMediaParser::Init(const InitCB& init_cb,
const NewSampleCB& new_sample_cb,
const NewMediaSampleCB& new_media_sample_cb,
const NewTextSampleCB& new_text_sample_cb,
KeySource* decryption_key_source) {
DCHECK_EQ(state_, kWaitingForInit);
DCHECK(init_cb_.is_null());
DCHECK(!init_cb.is_null());
DCHECK(!new_sample_cb.is_null());
DCHECK(!new_media_sample_cb.is_null());
ChangeState(kParsingHeaders);
init_cb_ = init_cb;
new_sample_cb_ = new_sample_cb;
new_sample_cb_ = new_media_sample_cb;
decryption_key_source_ = decryption_key_source;
ignore_text_tracks_ = true;
}

View File

@ -23,7 +23,8 @@ class WebMMediaParser : public MediaParser {
/// @name MediaParser implementation overrides.
/// @{
void Init(const InitCB& init_cb,
const NewSampleCB& new_sample_cb,
const NewMediaSampleCB& new_media_sample_cb,
const NewTextSampleCB& new_text_sample_cb,
KeySource* decryption_key_source) override;
bool Flush() override WARN_UNUSED_RESULT;
bool Parse(const uint8_t* buf, int size) override WARN_UNUSED_RESULT;
@ -64,7 +65,7 @@ class WebMMediaParser : public MediaParser {
State state_;
InitCB init_cb_;
NewSampleCB new_sample_cb_;
NewMediaSampleCB new_sample_cb_;
KeySource* decryption_key_source_;
bool ignore_text_tracks_;

View File

@ -112,14 +112,15 @@ WvmMediaParser::WvmMediaParser()
WvmMediaParser::~WvmMediaParser() {}
void WvmMediaParser::Init(const InitCB& init_cb,
const NewSampleCB& new_sample_cb,
const NewMediaSampleCB& new_media_sample_cb,
const NewTextSampleCB& new_text_sample_cb,
KeySource* decryption_key_source) {
DCHECK(!is_initialized_);
DCHECK(!init_cb.is_null());
DCHECK(!new_sample_cb.is_null());
DCHECK(!new_media_sample_cb.is_null());
decryption_key_source_ = decryption_key_source;
init_cb_ = init_cb;
new_sample_cb_ = new_sample_cb;
new_sample_cb_ = new_media_sample_cb;
}
bool WvmMediaParser::Parse(const uint8_t* buf, int size) {

View File

@ -56,7 +56,8 @@ class WvmMediaParser : public MediaParser {
/// @name MediaParser implementation overrides.
/// @{
void Init(const InitCB& init_cb,
const NewSampleCB& new_sample_cb,
const NewMediaSampleCB& new_media_sample_cb,
const NewTextSampleCB& new_text_sample_cb,
KeySource* decryption_key_source) override;
bool Flush() override WARN_UNUSED_RESULT;
bool Parse(const uint8_t* buf, int size) override WARN_UNUSED_RESULT;
@ -216,7 +217,7 @@ class WvmMediaParser : public MediaParser {
// List of callbacks.t
InitCB init_cb_;
NewSampleCB new_sample_cb_;
NewMediaSampleCB new_sample_cb_;
// Whether |init_cb_| has been invoked.
bool is_initialized_;

View File

@ -99,8 +99,7 @@ class WvmMediaParserTest : public testing::Test {
}
}
bool OnNewSample(uint32_t track_id,
const std::shared_ptr<MediaSample>& sample) {
bool OnNewSample(uint32_t track_id, std::shared_ptr<MediaSample> sample) {
std::string stream_type;
if (static_cast<int32_t>(track_id) != current_track_id_) {
// onto next track.
@ -138,11 +137,15 @@ class WvmMediaParserTest : public testing::Test {
return true;
}
bool OnNewTextSample(uint32_t track_id, std::shared_ptr<TextSample> sample) {
return false;
}
void InitializeParser() {
parser_->Init(
base::Bind(&WvmMediaParserTest::OnInit,
base::Unretained(this)),
base::Bind(&WvmMediaParserTest::OnNewSample,
base::Bind(&WvmMediaParserTest::OnInit, base::Unretained(this)),
base::Bind(&WvmMediaParserTest::OnNewSample, base::Unretained(this)),
base::Bind(&WvmMediaParserTest::OnNewTextSample,
base::Unretained(this)),
key_source_.get());
}