Files
Bento4/Source/C++/Core/Ap4LinearReader.cpp
2017-10-14 12:47:41 -07:00

646 lines
23 KiB
C++

/*****************************************************************
|
| AP4 - Linear Sample Reader
|
| Copyright 2002-2009 Axiomatic Systems, LLC
|
|
| This file is part of Bento4/AP4 (MP4 Atom Processing Library).
|
| Unless you have obtained Bento4 under a difference license,
| this version of Bento4 is Bento4|GPL.
| Bento4|GPL is free software; you can redistribute it and/or modify
| it under the terms of the GNU General Public License as published by
| the Free Software Foundation; either version 2, or (at your option)
| any later version.
|
| Bento4|GPL is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU General Public License
| along with Bento4|GPL; see the file COPYING. If not, write to the
| Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
| 02111-1307, USA.
|
****************************************************************/
/*----------------------------------------------------------------------
| includes
+---------------------------------------------------------------------*/
#include "Ap4LinearReader.h"
#include "Ap4Array.h"
#include "Ap4SampleTable.h"
#include "Ap4MovieFragment.h"
#include "Ap4FragmentSampleTable.h"
#include "Ap4AtomFactory.h"
#include "Ap4TfraAtom.h"
/*----------------------------------------------------------------------
| AP4_LinearReader::AP4_LinearReader
+---------------------------------------------------------------------*/
AP4_LinearReader::AP4_LinearReader(AP4_Movie& movie,
AP4_ByteStream* fragment_stream,
AP4_Size max_buffer) :
m_Movie(movie),
m_Fragment(NULL),
m_FragmentStream(fragment_stream),
m_CurrentFragmentPosition(0),
m_NextFragmentPosition(0),
m_BufferFullness(0),
m_BufferFullnessPeak(0),
m_MaxBufferFullness(max_buffer),
m_Mfra(NULL)
{
m_HasFragments = movie.HasFragments();
if (fragment_stream) {
fragment_stream->AddReference();
fragment_stream->Tell(m_CurrentFragmentPosition);
m_NextFragmentPosition = m_CurrentFragmentPosition;
}
}
/*----------------------------------------------------------------------
| AP4_LinearReader::~AP4_LinearReader
+---------------------------------------------------------------------*/
AP4_LinearReader::~AP4_LinearReader()
{
for (unsigned int i=0; i<m_Trackers.ItemCount(); i++) {
delete m_Trackers[i];
}
delete m_Fragment;
delete m_Mfra;
if (m_FragmentStream) m_FragmentStream->Release();
}
/*----------------------------------------------------------------------
| AP4_LinearReader::EnableTrack
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::EnableTrack(AP4_UI32 track_id)
{
// check if we don't already have this
if (FindTracker(track_id)) return AP4_SUCCESS;
// find the track in the movie
AP4_Track* track = m_Movie.GetTrack(track_id);
if (track == NULL) return AP4_ERROR_NO_SUCH_ITEM;
// process this track
return ProcessTrack(track);
}
/*----------------------------------------------------------------------
| AP4_LinearReader::FlushQueue
+---------------------------------------------------------------------*/
void
AP4_LinearReader::FlushQueue(Tracker* tracker)
{
// empty any queued samples
for (AP4_List<SampleBuffer>::Item* item = tracker->m_Samples.FirstItem();
item;
item = item->GetNext()) {
SampleBuffer* buffer = item->GetData();
m_BufferFullness -= buffer->m_Data.GetDataSize();
delete buffer;
}
tracker->m_Samples.Clear();
}
/*----------------------------------------------------------------------
| AP4_LinearReader::FlushQueues
+---------------------------------------------------------------------*/
void
AP4_LinearReader::FlushQueues()
{
for (unsigned int i=0; i<m_Trackers.ItemCount(); i++) {
FlushQueue(m_Trackers[i]);
}
}
/*----------------------------------------------------------------------
| AP4_LinearReader::SetSampleIndex
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::SetSampleIndex(AP4_UI32 track_id, AP4_UI32 sample_index)
{
Tracker* tracker = FindTracker(track_id);
if (tracker == NULL) return AP4_ERROR_INVALID_PARAMETERS;
assert(tracker->m_SampleTable);
delete tracker->m_NextSample;
tracker->m_NextSample = NULL;
if (sample_index >= tracker->m_SampleTable->GetSampleCount()) {
return AP4_ERROR_OUT_OF_RANGE;
}
tracker->m_Eos = false;
tracker->m_NextSampleIndex = sample_index;
// empty any queued samples
for (AP4_List<SampleBuffer>::Item* item = tracker->m_Samples.FirstItem();
item;
item = item->GetNext()) {
SampleBuffer* buffer = item->GetData();
m_BufferFullness -= buffer->m_Data.GetDataSize();
delete buffer;
}
tracker->m_Samples.Clear();
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_LinearReader::SeekTo
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::SeekTo(AP4_UI32 time_ms, AP4_UI32* actual_time_ms)
{
if (actual_time_ms) *actual_time_ms = time_ms; // default
// we only support fragmented sources for now
if (!m_HasFragments) return AP4_ERROR_NOT_SUPPORTED;
// look for a fragment index
if (m_Mfra == NULL) {
if (m_FragmentStream) {
// get the size of the stream (needed)
AP4_LargeSize stream_size = 0;
m_FragmentStream->GetSize(stream_size);
if (stream_size > 12) {
// remember where we are
AP4_Position here;
m_FragmentStream->Tell(here);
// read the last 12 bytes
unsigned char mfro[12];
AP4_Result result = m_FragmentStream->Seek(stream_size-12);
if (AP4_SUCCEEDED(result)) {
result = m_FragmentStream->Read(mfro, 12);
}
if (AP4_SUCCEEDED(result) && mfro[0] == 'm' && mfro[1] == 'f' && mfro[2] == 'r' && mfro[3] == 'o') {
AP4_UI32 mfra_size = AP4_BytesToUInt32BE(&mfro[8]);
if ((AP4_LargeSize)mfra_size < stream_size) {
result = m_FragmentStream->Seek(stream_size-mfra_size);
if (AP4_SUCCEEDED(result)) {
AP4_Atom* mfra = NULL;
AP4_LargeSize available = mfra_size;
AP4_DefaultAtomFactory atom_factory;
atom_factory.CreateAtomFromStream(*m_FragmentStream, available, mfra);
m_Mfra = AP4_DYNAMIC_CAST(AP4_ContainerAtom, mfra);
}
}
}
if (AP4_SUCCEEDED(result)) {
result = m_FragmentStream->Seek(here);
}
}
}
}
// return now if we have not found an index
if (m_Mfra == NULL) {
return AP4_ERROR_NOT_SUPPORTED;
}
// look for the earliest fragment referenced by an entry with the largest timestamp that's
// before or equal to the requested time
int best_entry = -1;
for (unsigned t=0; t<m_Trackers.ItemCount(); t++) {
// find the tfra index for this track
AP4_TfraAtom* tfra = NULL;
for (AP4_List<AP4_Atom>::Item* item = m_Mfra->GetChildren().FirstItem();
item;
item = item->GetNext()) {
if (item->GetData()->GetType() == AP4_ATOM_TYPE_TFRA) {
AP4_TfraAtom* tfra_ = (AP4_TfraAtom*)item->GetData();
if (tfra_->GetTrackId() == m_Trackers[t]->m_Track->GetId()) {
tfra = tfra_;
break;
}
}
}
if (tfra == NULL) {
return AP4_ERROR_NOT_SUPPORTED;
}
AP4_Array<AP4_TfraAtom::Entry>& entries = tfra->GetEntries();
AP4_UI64 media_time = AP4_ConvertTime(time_ms, 1000, m_Trackers[t]->m_Track->GetMediaTimeScale());
int entry = -1;
for (int i=0; i<(int)entries.ItemCount(); i++) {
if (entries[i].m_Time > media_time) break;
entry = i;
}
if (entry >= 0) {
if (best_entry == -1) {
best_entry = entry;
} else if (entries[entry].m_MoofOffset < entries[best_entry].m_MoofOffset) {
best_entry = entry;
}
// update our position
if (best_entry >= 0) {
if (actual_time_ms) {
// report the actual time we found (in milliseconds)
*actual_time_ms = (AP4_UI32)AP4_ConvertTime(entries[best_entry].m_Time, m_Trackers[t]->m_Track->GetMediaTimeScale(), 1000);
}
m_NextFragmentPosition = entries[best_entry].m_MoofOffset;
}
}
}
// check that we found something
if (best_entry == -1) {
return AP4_FAILURE;
}
// flush any queued samples
FlushQueues();
// reset tracker states
for (unsigned int i=0; i<m_Trackers.ItemCount(); i++) {
if (m_Trackers[i]->m_SampleTableIsOwned) {
delete m_Trackers[i]->m_SampleTable;
}
delete m_Trackers[i]->m_NextSample;
m_Trackers[i]->m_SampleTable = NULL;
m_Trackers[i]->m_NextSample = NULL;
m_Trackers[i]->m_NextSampleIndex = 0;
m_Trackers[i]->m_Eos = false;
}
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_LinearReader::ProcessTrack
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::ProcessTrack(AP4_Track* track)
{
// create a new entry for the track
Tracker* tracker = new Tracker(track);
tracker->m_SampleTable = track->GetSampleTable();
return m_Trackers.Append(tracker);
}
/*----------------------------------------------------------------------
| AP4_LinearReader::ProcessMoof
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::ProcessMoof(AP4_ContainerAtom* moof,
AP4_Position moof_offset,
AP4_Position mdat_payload_offset)
{
AP4_Result result;
// create a new fragment
delete m_Fragment;
m_Fragment = new AP4_MovieFragment(moof);
// update the trackers
AP4_Array<AP4_UI32> ids;
m_Fragment->GetTrackIds(ids);
for (unsigned int i=0; i<m_Trackers.ItemCount(); i++) {
Tracker* tracker = m_Trackers[i];
if (tracker->m_SampleTableIsOwned) {
delete tracker->m_SampleTable;
}
tracker->m_SampleTable = NULL;
tracker->m_NextSampleIndex = 0;
for (unsigned int j=0; j<ids.ItemCount(); j++) {
if (ids[j] == tracker->m_Track->GetId()) {
AP4_FragmentSampleTable* sample_table = NULL;
result = m_Fragment->CreateSampleTable(&m_Movie,
ids[j],
m_FragmentStream,
moof_offset,
mdat_payload_offset,
tracker->m_NextDts,
sample_table);
if (AP4_FAILED(result)) return result;
tracker->m_SampleTable = sample_table;
tracker->m_SampleTableIsOwned = true;
tracker->m_Eos = false;
break;
}
}
}
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_LinearReader::AdvanceFragment
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::AdvanceFragment()
{
AP4_Result result;
// go the the start of the next fragment
result = m_FragmentStream->Seek(m_NextFragmentPosition);
if (AP4_FAILED(result)) return result;
m_CurrentFragmentPosition = m_NextFragmentPosition;
// read atoms until we find a moof
assert(m_HasFragments);
if (!m_FragmentStream) return AP4_ERROR_INVALID_STATE;
AP4_DefaultAtomFactory atom_factory;
do {
AP4_Atom* atom = NULL;
AP4_Position last_position = 0;
m_FragmentStream->Tell(last_position);
result = atom_factory.CreateAtomFromStream(*m_FragmentStream, atom);
if (AP4_SUCCEEDED(result)) {
if (atom->GetType() == AP4_ATOM_TYPE_MOOF) {
AP4_ContainerAtom* moof = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
if (moof) {
// remember where the moof started
m_CurrentFragmentPosition = last_position;
// remember where we are in the stream
AP4_Position position = 0;
m_FragmentStream->Tell(position);
// process the movie fragment
result = ProcessMoof(moof, position-atom->GetSize(), position+8);
if (AP4_FAILED(result)) return result;
// compute where the next fragment will be
AP4_UI32 size;
AP4_UI32 type;
m_FragmentStream->Tell(position);
result = m_FragmentStream->ReadUI32(size);
if (AP4_FAILED(result)) return AP4_SUCCESS; // can't read more
result = m_FragmentStream->ReadUI32(type);
if (AP4_FAILED(result)) return AP4_SUCCESS; // can't read more
if (size == 0) {
m_NextFragmentPosition = 0;
} else if (size == 1) {
AP4_UI64 size_64 = 0;
result = m_FragmentStream->ReadUI64(size_64);
if (AP4_FAILED(result)) return AP4_SUCCESS; // can't read more
m_NextFragmentPosition = position+size_64;
} else {
m_NextFragmentPosition = position+size;
}
return AP4_SUCCESS;
} else {
delete atom;
}
} else {
delete atom;
}
}
} while (AP4_SUCCEEDED(result));
return AP4_ERROR_EOS;
}
/*----------------------------------------------------------------------
| AP4_LinearReader::Advance
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::Advance(bool read_data)
{
// first, check if we have space to advance
if (m_BufferFullness >= m_MaxBufferFullness) {
return AP4_ERROR_NOT_ENOUGH_SPACE;
}
AP4_UI64 min_offset = (AP4_UI64)(-1);
Tracker* next_tracker = NULL;
for (;;) {
for (unsigned int i=0; i<m_Trackers.ItemCount(); i++) {
Tracker* tracker = m_Trackers[i];
if (tracker->m_Eos) continue;
if (tracker->m_SampleTable == NULL) continue;
// get the next sample unless we have it already
if (tracker->m_NextSample == NULL) {
if (tracker->m_NextSampleIndex >= tracker->m_SampleTable->GetSampleCount()) {
if (!m_HasFragments) tracker->m_Eos = true;
if (tracker->m_SampleTableIsOwned) {
delete tracker->m_SampleTable;
tracker->m_SampleTable = NULL;
}
continue;
}
tracker->m_NextSample = new AP4_Sample();
AP4_Result result = tracker->m_SampleTable->GetSample(tracker->m_NextSampleIndex, *tracker->m_NextSample);
if (AP4_FAILED(result)) {
tracker->m_Eos = true;
delete tracker->m_NextSample;
tracker->m_NextSample = NULL;
continue;
}
tracker->m_NextDts += tracker->m_NextSample->GetDuration();
}
assert(tracker->m_NextSample);
AP4_UI64 offset = tracker->m_NextSample->GetOffset();
if (offset < min_offset) {
min_offset = offset;
next_tracker = tracker;
}
}
if (next_tracker) break;
if (m_HasFragments) {
AP4_Result result = AdvanceFragment();
if (AP4_FAILED(result)) return result;
} else {
break;
}
}
if (next_tracker) {
// read the sample into a buffer
assert(next_tracker->m_NextSample);
SampleBuffer* buffer = new SampleBuffer(next_tracker->m_NextSample);
AP4_Result result;
if (read_data) {
if (next_tracker->m_Reader) {
result = next_tracker->m_Reader->ReadSampleData(*buffer->m_Sample, buffer->m_Data);
} else {
result = buffer->m_Sample->ReadData(buffer->m_Data);
}
if (AP4_FAILED(result)) {
delete buffer;
return result;
}
// detach the sample from its source now that we've read its data
buffer->m_Sample->Detach();
}
// add the buffer to the queue
next_tracker->m_Samples.Add(buffer);
m_BufferFullness += buffer->m_Data.GetDataSize();
if (m_BufferFullness > m_BufferFullnessPeak) {
m_BufferFullnessPeak = m_BufferFullness;
}
next_tracker->m_NextSample = NULL;
next_tracker->m_NextSampleIndex++;
return AP4_SUCCESS;
}
return AP4_ERROR_EOS;
}
/*----------------------------------------------------------------------
| AP4_LinearReader::PopSample
+---------------------------------------------------------------------*/
bool
AP4_LinearReader::PopSample(Tracker* tracker,
AP4_Sample& sample,
AP4_DataBuffer* sample_data)
{
SampleBuffer* head = NULL;
if (AP4_SUCCEEDED(tracker->m_Samples.PopHead(head)) && head) {
assert(head->m_Sample);
sample = *head->m_Sample;
if (sample_data) {
sample_data->SetData(head->m_Data.GetData(), head->m_Data.GetDataSize());
}
assert(m_BufferFullness >= head->m_Data.GetDataSize());
m_BufferFullness -= head->m_Data.GetDataSize();
delete head;
return true;
}
return false;
}
/*----------------------------------------------------------------------
| AP4_LinearReader::ReadNextSample
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::ReadNextSample(AP4_UI32 track_id,
AP4_Sample& sample,
AP4_DataBuffer& sample_data)
{
if (m_Trackers.ItemCount() == 0) {
return AP4_ERROR_NO_SUCH_ITEM;
}
// look for a sample from a specific track
Tracker* tracker = FindTracker(track_id);
if (tracker == NULL) return AP4_ERROR_INVALID_PARAMETERS;
for(;;) {
// pop a sample if we can
if (PopSample(tracker, sample, &sample_data)) return AP4_SUCCESS;
// don't continue if we've reached the end of that tracker
if (tracker->m_Eos) return AP4_ERROR_EOS;
AP4_Result result = Advance();
if (AP4_FAILED(result)) return result;
}
// unreachable - return AP4_ERROR_EOS;
}
/*----------------------------------------------------------------------
| AP4_LinearReader::ReadNextSample
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::ReadNextSample(AP4_Sample& sample,
AP4_DataBuffer* sample_data,
AP4_UI32& track_id)
{
if (m_Trackers.ItemCount() == 0) {
track_id = 0;
return AP4_ERROR_NO_SUCH_ITEM;
}
// return the oldest buffered sample, if any
AP4_UI64 min_offset = (AP4_UI64)(-1);
Tracker* next_tracker = NULL;
for (;;) {
for (unsigned int i=0; i<m_Trackers.ItemCount(); i++) {
Tracker* tracker = m_Trackers[i];
if (tracker->m_Eos) continue;
AP4_List<SampleBuffer>::Item* item = tracker->m_Samples.FirstItem();
if (item) {
AP4_UI64 offset = item->GetData()->m_Sample->GetOffset();
if (offset < min_offset) {
min_offset = offset;
next_tracker = tracker;
}
}
}
// return the sample if we have found a tracker
if (next_tracker) {
PopSample(next_tracker, sample, sample_data);
track_id = next_tracker->m_Track->GetId();
return AP4_SUCCESS;
}
// nothing found, read one more sample
AP4_Result result = Advance(sample_data != NULL);
if (AP4_FAILED(result)) return result;
}
// unreachable - return AP4_ERROR_EOS;
}
/*----------------------------------------------------------------------
| AP4_LinearReader::ReadNextSample
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::ReadNextSample(AP4_Sample& sample,
AP4_DataBuffer& sample_data,
AP4_UI32& track_id)
{
return ReadNextSample(sample, &sample_data, track_id);
}
/*----------------------------------------------------------------------
| AP4_LinearReader::GetNextSample
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::GetNextSample(AP4_Sample& sample, AP4_UI32& track_id)
{
return ReadNextSample(sample, NULL, track_id);
}
/*----------------------------------------------------------------------
| AP4_LinearReader::FindTracker
+---------------------------------------------------------------------*/
AP4_LinearReader::Tracker*
AP4_LinearReader::FindTracker(AP4_UI32 track_id)
{
for (unsigned int i=0; i<m_Trackers.ItemCount(); i++) {
if (m_Trackers[i]->m_Track->GetId() == track_id) return m_Trackers[i];
}
// not found
return NULL;
}
/*----------------------------------------------------------------------
| AP4_LinearReader::Tracker::~Tracker
+---------------------------------------------------------------------*/
AP4_LinearReader::Tracker::~Tracker()
{
if (m_SampleTableIsOwned) delete m_SampleTable;
delete m_Reader;
}
/*----------------------------------------------------------------------
| AP4_DecryptingSampleReader::ReadSampleData
+---------------------------------------------------------------------*/
AP4_Result
AP4_DecryptingSampleReader::ReadSampleData(AP4_Sample& sample,
AP4_DataBuffer& sample_data)
{
AP4_Result result = sample.ReadData(m_DataBuffer);
if (AP4_FAILED(result)) return result;
return m_Decrypter->DecryptSampleData(m_DataBuffer, sample_data);
}