/***************************************************************** | | 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 #include #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* 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::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(constructor), stream); if (AP4_FAILED(result)) return result; break; case AP4_RTP_CONSTRUCTOR_TYPE_SAMPLE: result = WriteSampleRtpData( static_cast(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()); }