mirror of
https://github.com/axiomatic-systems/Bento4.git
synced 2026-01-19 00:05:12 +08:00
343 lines
12 KiB
C++
343 lines
12 KiB
C++
/*****************************************************************
|
|
|
|
|
| AP4 - Hint Track Reader
|
|
|
|
|
| Copyright 2002-2008 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 <stdlib.h>
|
|
#include <time.h>
|
|
#include "Ap4HintTrackReader.h"
|
|
#include "Ap4DataBuffer.h"
|
|
#include "Ap4Track.h"
|
|
#include "Ap4Movie.h"
|
|
#include "Ap4SdpAtom.h"
|
|
#include "Ap4RtpHint.h"
|
|
#include "Ap4TrakAtom.h"
|
|
#include "Ap4TrefTypeAtom.h"
|
|
#include "Ap4TimsAtom.h"
|
|
#include "Ap4Utils.h"
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::AP4_HintTrackReader
|
|
+---------------------------------------------------------------------*/
|
|
AP4_HintTrackReader::AP4_HintTrackReader(AP4_Track& hint_track,
|
|
AP4_Movie& movie,
|
|
AP4_UI32 ssrc) :
|
|
m_HintTrack(hint_track),
|
|
m_MediaTrack(NULL),
|
|
m_MediaTimeScale(0),
|
|
m_RtpSampleData(NULL),
|
|
m_Ssrc(ssrc),
|
|
m_SampleIndex(0),
|
|
m_PacketIndex(0),
|
|
m_RtpSequenceStart(0),
|
|
m_RtpTimeStampStart(0),
|
|
m_RtpTimeScale(0)
|
|
{
|
|
// get the media track
|
|
AP4_TrakAtom* hint_trak_atom = hint_track.UseTrakAtom();
|
|
AP4_Atom* atom = hint_trak_atom->FindChild("tref/hint");
|
|
if (atom != NULL) {
|
|
AP4_UI32 media_track_id = AP4_DYNAMIC_CAST(AP4_TrefTypeAtom, atom)->GetTrackIds()[0];
|
|
m_MediaTrack = movie.GetTrack(media_track_id);
|
|
|
|
// get the media time scale
|
|
m_MediaTimeScale = m_MediaTrack->GetMediaTimeScale();
|
|
}
|
|
|
|
// initiate random generator
|
|
srand((int)time(NULL));
|
|
|
|
// rtp sequence start init TODO!!
|
|
m_RtpSequenceStart = (AP4_UI16)(rand()&0xFFFF);
|
|
|
|
// rtp timestamp start init TODO!!
|
|
m_RtpTimeStampStart = rand();
|
|
|
|
// rtp time scale
|
|
atom = hint_trak_atom->FindChild("mdia/minf/stbl/rtp /tims");
|
|
if (atom) {
|
|
AP4_TimsAtom* tims = AP4_DYNAMIC_CAST(AP4_TimsAtom, atom);
|
|
m_RtpTimeScale = tims->GetTimeScale();
|
|
}
|
|
|
|
// generate a random ssrc if = 0
|
|
if (m_Ssrc == 0) {
|
|
m_Ssrc = rand();
|
|
}
|
|
|
|
// get the first sample
|
|
GetRtpSample(0);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::Create
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_HintTrackReader::Create(AP4_Track& hint_track,
|
|
AP4_Movie& movie,
|
|
AP4_UI32 ssrc,
|
|
AP4_HintTrackReader*& reader)
|
|
{
|
|
// default value
|
|
reader = NULL;
|
|
|
|
// check the type
|
|
if (hint_track.GetType() != AP4_Track::TYPE_HINT) {
|
|
return AP4_ERROR_INVALID_TRACK_TYPE;
|
|
}
|
|
|
|
// create a new object
|
|
reader = new AP4_HintTrackReader(hint_track, movie, ssrc);
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::~AP4_HintTrackReader
|
|
+---------------------------------------------------------------------*/
|
|
AP4_HintTrackReader::~AP4_HintTrackReader()
|
|
{
|
|
delete m_RtpSampleData;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::GetRtpSample
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_HintTrackReader::GetRtpSample(AP4_Ordinal index)
|
|
{
|
|
// get the sample
|
|
AP4_Result result = m_HintTrack.GetSample(index, m_CurrentHintSample);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// renew the sample data
|
|
delete m_RtpSampleData;
|
|
AP4_ByteStream& rtp_data_stream = *m_CurrentHintSample.GetDataStream();
|
|
rtp_data_stream.Seek(m_CurrentHintSample.GetOffset());
|
|
m_RtpSampleData = new AP4_RtpSampleData(rtp_data_stream,
|
|
m_CurrentHintSample.GetSize());
|
|
|
|
// reinit the packet index
|
|
m_PacketIndex = 0;
|
|
|
|
// release the stream
|
|
rtp_data_stream.Release();
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::GetCurrentTimeStampMs
|
|
+---------------------------------------------------------------------*/
|
|
AP4_UI32
|
|
AP4_HintTrackReader::GetCurrentTimeStampMs()
|
|
{
|
|
return (AP4_UI32)AP4_ConvertTime(m_CurrentHintSample.GetCts(),
|
|
m_HintTrack.GetMediaTimeScale(),
|
|
1000);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::Rewind
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_HintTrackReader::Rewind()
|
|
{
|
|
m_SampleIndex = 0;
|
|
return GetRtpSample(m_SampleIndex);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::GetSdpText
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_HintTrackReader::GetSdpText(AP4_String& sdp_text)
|
|
{
|
|
AP4_Atom* sdp_atom = m_HintTrack.UseTrakAtom()->FindChild("udta/hnti/sdp ");
|
|
if (sdp_atom == NULL) return AP4_FAILURE;
|
|
|
|
// C cast is OK because we know the type of the atom
|
|
sdp_text = AP4_DYNAMIC_CAST(AP4_SdpAtom, sdp_atom)->GetSdpText();
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::SeekToTimeStampMs
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_HintTrackReader::SeekToTimeStampMs(AP4_UI32 desired_ts_ms,
|
|
AP4_UI32& actual_ts_ms)
|
|
{
|
|
// get the sample index
|
|
AP4_Cardinal index;
|
|
AP4_Result result = m_HintTrack.GetSampleIndexForTimeStampMs(desired_ts_ms, index);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// get the current sample based on the index and renew the sample data
|
|
result = GetRtpSample(index);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// set the actual ts
|
|
actual_ts_ms = GetCurrentTimeStampMs();
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::GetNextPacket
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_HintTrackReader::GetNextPacket(AP4_DataBuffer& packet_data,
|
|
AP4_UI32& ts_ms)
|
|
{
|
|
AP4_Result result = AP4_SUCCESS;
|
|
|
|
// get the next rtp sample if needed
|
|
AP4_List<AP4_RtpPacket>* packets = &m_RtpSampleData->GetPackets();
|
|
while (m_PacketIndex == packets->ItemCount()) { // while: handle the 0 packet case
|
|
result = GetRtpSample(++m_SampleIndex);
|
|
if (AP4_FAILED(result)) return result;
|
|
packets = &m_RtpSampleData->GetPackets();
|
|
}
|
|
|
|
// get the packet
|
|
AP4_RtpPacket* packet;
|
|
result = packets->Get(m_PacketIndex++, packet);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// build it
|
|
result = BuildRtpPacket(packet, packet_data);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// set the time stamp
|
|
ts_ms = GetCurrentTimeStampMs();
|
|
|
|
return result;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::BuildRtpPacket
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_HintTrackReader::BuildRtpPacket(AP4_RtpPacket* packet,
|
|
AP4_DataBuffer& packet_data)
|
|
{
|
|
// set the data size
|
|
AP4_Result result = packet_data.SetDataSize(packet->GetConstructedDataSize());
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// now write
|
|
AP4_ByteStream* stream = new AP4_MemoryByteStream(packet_data);
|
|
|
|
// header + ssrc
|
|
stream->WriteUI08(0x80 | (packet->GetPBit() << 5) | (packet->GetXBit() << 4));
|
|
stream->WriteUI08((packet->GetMBit() << 7) | packet->GetPayloadType());
|
|
stream->WriteUI16(m_RtpSequenceStart + packet->GetSequenceSeed());
|
|
stream->WriteUI32(m_RtpTimeStampStart + (AP4_UI32)m_CurrentHintSample.GetCts() + packet->GetTimeStampOffset());
|
|
stream->WriteUI32(m_Ssrc);
|
|
|
|
AP4_List<AP4_RtpConstructor>::Item* constructors_it
|
|
= packet->GetConstructors().FirstItem();
|
|
while (constructors_it != NULL) {
|
|
AP4_RtpConstructor* constructor = constructors_it->GetData();
|
|
|
|
// add data to the packet according to the constructor
|
|
switch (constructor->GetType()) {
|
|
case AP4_RTP_CONSTRUCTOR_TYPE_NOOP:
|
|
// nothing to do here
|
|
break;
|
|
case AP4_RTP_CONSTRUCTOR_TYPE_IMMEDIATE:
|
|
result = WriteImmediateRtpData(
|
|
static_cast<AP4_ImmediateRtpConstructor*>(constructor), stream);
|
|
if (AP4_FAILED(result)) return result;
|
|
break;
|
|
case AP4_RTP_CONSTRUCTOR_TYPE_SAMPLE:
|
|
result = WriteSampleRtpData(
|
|
static_cast<AP4_SampleRtpConstructor*>(constructor), stream);
|
|
if (AP4_FAILED(result)) return result;
|
|
break;
|
|
case AP4_RTP_CONSTRUCTOR_TYPE_SAMPLE_DESC:
|
|
return AP4_ERROR_NOT_SUPPORTED;
|
|
default:
|
|
// unknown constructor type
|
|
return AP4_FAILURE;
|
|
}
|
|
|
|
// iterate
|
|
constructors_it = constructors_it->GetNext();
|
|
}
|
|
|
|
// release the stream
|
|
stream->Release();
|
|
|
|
return result;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::WriteImmediateRtpData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_HintTrackReader::WriteImmediateRtpData(AP4_ImmediateRtpConstructor* constructor,
|
|
AP4_ByteStream* data_stream)
|
|
{
|
|
const AP4_DataBuffer& data_buffer = constructor->GetData();
|
|
return data_stream->Write(data_buffer.GetData(), data_buffer.GetDataSize());
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_HintTrackReader::WriteSampleRtpData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_HintTrackReader::WriteSampleRtpData(AP4_SampleRtpConstructor* constructor,
|
|
AP4_ByteStream* data_stream)
|
|
{
|
|
AP4_Track* referenced_track = NULL;
|
|
if (constructor->GetTrackRefIndex() == 0xFF) {
|
|
// data is in the hint track
|
|
referenced_track = &m_HintTrack;
|
|
} else {
|
|
// check if we have a media track
|
|
if (m_MediaTrack == NULL) return AP4_FAILURE;
|
|
referenced_track = m_MediaTrack;
|
|
}
|
|
|
|
// write the sample data
|
|
AP4_Sample sample;
|
|
AP4_Result result = referenced_track->GetSample(constructor->GetSampleNum()-1, // adjust
|
|
sample);
|
|
if (AP4_FAILED(result)) return result;
|
|
AP4_DataBuffer buffer(constructor->GetLength());
|
|
result = sample.ReadData(
|
|
buffer, constructor->GetLength(), constructor->GetSampleOffset());
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// write the data
|
|
return data_stream->Write(buffer.GetData(), buffer.GetDataSize());
|
|
}
|
|
|