/***************************************************************** | | AP4 - RTP Hint Objects | | 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 "Ap4RtpHint.h" #include "Ap4ByteStream.h" #include "Ap4Atom.h" /*---------------------------------------------------------------------- | AP4_RtpSampleData::~AP4_RtpSampleData +---------------------------------------------------------------------*/ AP4_RtpSampleData::~AP4_RtpSampleData() { AP4_List::Item* it = m_Packets.FirstItem(); while (it != NULL) { it->GetData()->Release(); it = it->GetNext(); } } /*---------------------------------------------------------------------- | AP4_RtpSampleData::AP4_RtpSampleData +---------------------------------------------------------------------*/ AP4_RtpSampleData::AP4_RtpSampleData(AP4_ByteStream& stream, AP4_UI32 size) { // save the start position AP4_Position start, extra_data_start; stream.Tell(start); AP4_UI16 packet_count; stream.ReadUI16(packet_count); AP4_UI16 reserved; stream.ReadUI16(reserved); // later, check that reserved is 0 // packets for (AP4_UI16 i=0; i::Item* it = m_Packets.FirstItem(); while (it != NULL) { result = it->GetData()->GetSize(); it = it->GetNext(); } // extra data result += m_ExtraData.GetDataSize(); return result; } /*---------------------------------------------------------------------- | AP4_RtpSampleData::ToByteStream +---------------------------------------------------------------------*/ AP4_ByteStream* AP4_RtpSampleData::ToByteStream() { // refresh the size AP4_Size size = GetSize(); // create a memory stream AP4_MemoryByteStream* stream = new AP4_MemoryByteStream(size); // write in it AP4_Result result = stream->WriteUI16(static_cast(m_Packets.ItemCount())); if (AP4_FAILED(result)) goto bail; result = stream->WriteUI16(0); // reserved if (AP4_FAILED(result)) goto bail; { AP4_List::Item* it = m_Packets.FirstItem(); while (it != NULL) { result = it->GetData()->Write(*stream); if (AP4_FAILED(result)) goto bail; it = it->GetNext(); } } result = stream->Write(m_ExtraData.GetData(), m_ExtraData.GetDataSize()); if (AP4_FAILED(result)) goto bail; // return return stream; bail: stream->Release(); return NULL; } /*---------------------------------------------------------------------- | AP4_RtpSampleData::AddPacket +---------------------------------------------------------------------*/ AP4_Result AP4_RtpSampleData::AddPacket(AP4_RtpPacket* packet) { packet->AddReference(); return m_Packets.Add(packet); } /*---------------------------------------------------------------------- | AP4_RtpPacket::AP4_RtpPacket +---------------------------------------------------------------------*/ AP4_RtpPacket::AP4_RtpPacket(int relative_time, bool p_bit, bool x_bit, bool m_bit, AP4_UI08 payload_type, AP4_UI16 sequence_seed, int time_stamp_offset /* = 0 */, bool bframe_flag /* = false */, bool repeat_flag /* = false */) : m_ReferenceCount(1), m_RelativeTime(relative_time), m_PBit(p_bit), m_XBit(x_bit), m_MBit(m_bit), m_PayloadType(payload_type), m_SequenceSeed(sequence_seed), m_TimeStampOffset(time_stamp_offset), m_BFrameFlag(bframe_flag), m_RepeatFlag(repeat_flag) {} /*---------------------------------------------------------------------- | AP4_RtpPacket::AP4_RtpPacket +---------------------------------------------------------------------*/ AP4_RtpPacket::AP4_RtpPacket(AP4_ByteStream& stream) : m_ReferenceCount(1), m_TimeStampOffset(0) { AP4_UI08 octet; // relative time AP4_UI32 relative_time; stream.ReadUI32(relative_time); m_RelativeTime = relative_time; // pbit and xbit stream.ReadUI08(octet); m_PBit = (octet & 0x20) != 0; m_XBit = (octet & 0x10) != 0; // mbit and payload type stream.ReadUI08(octet); m_MBit = (octet & 0x80) != 0; m_PayloadType = octet & 0x7F; // sequence seed stream.ReadUI16(m_SequenceSeed); // extra, bframe and repeat flags stream.ReadUI08(octet); stream.ReadUI08(octet); // repeat on purpose bool extra_flag = (octet & 0x04) != 0; // bframe and repeat flags m_BFrameFlag = (octet & 0x02) != 0; m_RepeatFlag = (octet & 0x01) != 0; // constructor count AP4_UI16 constructor_count; stream.ReadUI16(constructor_count); // parse the packet extra data if (extra_flag) { // read the length AP4_UI32 extra_length; stream.ReadUI32(extra_length); // check it if (extra_length < 4) return; // now read the entries extra_length -= 4; while (extra_length > 0) { AP4_UI32 entry_length; AP4_UI32 entry_tag; stream.ReadUI32(entry_length); stream.ReadUI32(entry_tag); // check the entry if (entry_length < 8) return; // parse the single entry that's currently defined in the spec if (entry_tag == AP4_ATOM_TYPE('r','t','p','o') && entry_length == 12) { AP4_UI32 time_stamp_offset; stream.ReadUI32(time_stamp_offset); m_TimeStampOffset = time_stamp_offset; } else { // ignore it AP4_Position cur_pos; stream.Tell(cur_pos); stream.Seek(cur_pos + entry_length - 8); // 8 = length + tag } extra_length -= entry_length; } } // constructors for (AP4_UI16 i=0; i::Item* it = m_Constructors.FirstItem(); while (it != NULL) { it->GetData()->Release(); it = it->GetNext(); } } /*---------------------------------------------------------------------- | AP4_RtpPacket::AddReference +---------------------------------------------------------------------*/ void AP4_RtpPacket::AddReference() { m_ReferenceCount++; } /*---------------------------------------------------------------------- | AP4_RtpPacket::Release +---------------------------------------------------------------------*/ void AP4_RtpPacket::Release() { if (--m_ReferenceCount == 0) { delete this; } } /*---------------------------------------------------------------------- | AP4_RtpPacket::GetSize +---------------------------------------------------------------------*/ AP4_Size AP4_RtpPacket::GetSize() { AP4_Size result = 12 + ((m_TimeStampOffset != 0)?16:0); result += m_Constructors.ItemCount() * AP4_RTP_CONSTRUCTOR_SIZE; return result; } /*---------------------------------------------------------------------- | AP4_RtpPacket::Write +---------------------------------------------------------------------*/ AP4_Result AP4_RtpPacket::Write(AP4_ByteStream& stream) { // check the payload type if (m_PayloadType > 128) return AP4_FAILURE; // now write AP4_Result result = stream.WriteUI32(m_RelativeTime); if (AP4_FAILED(result)) return result; result = stream.WriteUI08(0x80 | m_PBit << 5 | m_XBit << 4); if (AP4_FAILED(result)) return result; result = stream.WriteUI08(m_MBit << 7 | m_PayloadType); if (AP4_FAILED(result)) return result; result = stream.WriteUI16(m_SequenceSeed); if (AP4_FAILED(result)) return result; result = stream.WriteUI08(0); if (AP4_FAILED(result)) return result; // deal with extra flag bool extra_flag = m_TimeStampOffset != 0; result = stream.WriteUI08(0x00 | extra_flag << 2 | m_BFrameFlag << 1 | m_RepeatFlag << 0); if (AP4_FAILED(result)) return result; // constructor count result = stream.WriteUI16(static_cast(m_Constructors.ItemCount())); // write extra data if (extra_flag) { // extra_length result = stream.WriteUI32(16); // 4 (extra_length) + 12 (rtpo atom) if (AP4_FAILED(result)) return result; // rtpo atom result = stream.WriteUI32(12); // size if (AP4_FAILED(result)) return result; result = stream.WriteUI32(AP4_ATOM_TYPE('r','t','p','o')); if (AP4_FAILED(result)) return result; result = stream.WriteUI32(m_TimeStampOffset); if (AP4_FAILED(result)) return result; } // constructors AP4_List::Item* it = m_Constructors.FirstItem(); while (it != NULL) { result = it->GetData()->Write(stream); if (AP4_FAILED(result)) return result; it = it->GetNext(); } return result; } /*---------------------------------------------------------------------- | AP4_RtpPacket::AddConstructor +---------------------------------------------------------------------*/ AP4_Result AP4_RtpPacket::AddConstructor(AP4_RtpConstructor* constructor) { constructor->AddReference(); return m_Constructors.Add(constructor); } /*---------------------------------------------------------------------- | AP4_RtpConstructor::GetConstructedDataSize +---------------------------------------------------------------------*/ AP4_Size AP4_RtpPacket::GetConstructedDataSize() { // header + ssrc AP4_Size size = 12; // constructed data from constructors AP4_List::Item* constructors_it = m_Constructors.FirstItem(); while (constructors_it != NULL) { size += constructors_it->GetData()->GetConstructedDataSize(); constructors_it = constructors_it->GetNext(); } return size; } /*---------------------------------------------------------------------- | AP4_RtpConstructor::AddReference +---------------------------------------------------------------------*/ void AP4_RtpConstructor::AddReference() { m_ReferenceCount++; } /*---------------------------------------------------------------------- | AP4_RtpConstructor::Release +---------------------------------------------------------------------*/ void AP4_RtpConstructor::Release() { if (--m_ReferenceCount == 0) { delete this; } } /*---------------------------------------------------------------------- | AP4_RtpConstructor::Write +---------------------------------------------------------------------*/ AP4_Result AP4_RtpConstructor::Write(AP4_ByteStream& stream) { AP4_Result result = stream.WriteUI08(m_Type); if (AP4_FAILED(result)) return result; return DoWrite(stream); } /*---------------------------------------------------------------------- | AP4_NoopRtpConstructor::AP4_NoopRtpConstructor +---------------------------------------------------------------------*/ AP4_NoopRtpConstructor::AP4_NoopRtpConstructor(AP4_ByteStream& stream) : AP4_RtpConstructor(AP4_RTP_CONSTRUCTOR_TYPE_NOOP) { AP4_Position cur_offset; stream.Tell(cur_offset); stream.Seek(cur_offset+15); } /*---------------------------------------------------------------------- | AP4_NoopRtpConstructor::DoWrite +---------------------------------------------------------------------*/ AP4_Result AP4_NoopRtpConstructor::DoWrite(AP4_ByteStream& stream) { AP4_UI08 pad[15]; return stream.Write(pad, sizeof(pad)); } /*---------------------------------------------------------------------- | AP4_ImmediateRtpConstructor::AP4_ImmediateRtpConstructor +---------------------------------------------------------------------*/ AP4_ImmediateRtpConstructor::AP4_ImmediateRtpConstructor(const AP4_DataBuffer& data) : AP4_RtpConstructor(AP4_RTP_CONSTRUCTOR_TYPE_IMMEDIATE), m_Data(data) {} /*---------------------------------------------------------------------- | AP4_ImmediateRtpConstructor::AP4_ImmediateRtpConstructor +---------------------------------------------------------------------*/ AP4_ImmediateRtpConstructor::AP4_ImmediateRtpConstructor(AP4_ByteStream& stream) : AP4_RtpConstructor(AP4_RTP_CONSTRUCTOR_TYPE_IMMEDIATE) { AP4_Position cur_offset; stream.Tell(cur_offset); // data AP4_UI08 data_size; stream.ReadUI08(data_size); m_Data.SetDataSize(data_size); stream.Read(m_Data.UseData(), data_size); // reposition the stream stream.Seek(cur_offset+15); } /*---------------------------------------------------------------------- | AP4_ImmediateRtpConstructor::DoWrite +---------------------------------------------------------------------*/ AP4_Result AP4_ImmediateRtpConstructor::DoWrite(AP4_ByteStream& stream) { // first check that the data is not too large if (m_Data.GetDataSize() > 14) return AP4_FAILURE; // now write AP4_Result result = stream.WriteUI08(static_cast(m_Data.GetDataSize())); if (AP4_FAILED(result)) return result; result = stream.Write(m_Data.GetData(), m_Data.GetDataSize()); if (AP4_FAILED(result)) return result; // pad AP4_Byte pad[14]; return stream.Write(pad, sizeof(pad)-m_Data.GetDataSize()); } /*---------------------------------------------------------------------- | AP4_SampleRtpConstructor::AP4_SampleRtpConstructor +---------------------------------------------------------------------*/ AP4_SampleRtpConstructor::AP4_SampleRtpConstructor(AP4_UI08 track_ref_index, AP4_UI16 length, AP4_UI32 sample_num, AP4_UI32 sample_offset) : AP4_RtpConstructor(AP4_RTP_CONSTRUCTOR_TYPE_SAMPLE), m_TrackRefIndex(track_ref_index), m_Length(length), m_SampleNum(sample_num), m_SampleOffset(sample_offset) {} /*---------------------------------------------------------------------- | AP4_SampleRtpConstructor::AP4_SampleRtpConstructor +---------------------------------------------------------------------*/ AP4_SampleRtpConstructor::AP4_SampleRtpConstructor(AP4_ByteStream& stream) : AP4_RtpConstructor(AP4_RTP_CONSTRUCTOR_TYPE_SAMPLE) { // offset AP4_Position cur_offset; stream.Tell(cur_offset); // data stream.ReadUI08(m_TrackRefIndex); stream.ReadUI16(m_Length); stream.ReadUI32(m_SampleNum); stream.ReadUI32(m_SampleOffset); // reposition the stream stream.Seek(cur_offset+15); } /*---------------------------------------------------------------------- | AP4_SampleRtpConstructor::DoWrite +---------------------------------------------------------------------*/ AP4_Result AP4_SampleRtpConstructor::DoWrite(AP4_ByteStream& stream) { AP4_Result result = stream.WriteUI08(m_TrackRefIndex); if (AP4_FAILED(result)) return result; result = stream.WriteUI16(m_Length); if (AP4_FAILED(result)) return result; result = stream.WriteUI32(m_SampleNum); if (AP4_FAILED(result)) return result; result = stream.WriteUI32(m_SampleOffset); if (AP4_FAILED(result)) return result; result = stream.WriteUI16(1); // bytes per block if (AP4_FAILED(result)) return result; return stream.WriteUI16(1); // samples per block } /*---------------------------------------------------------------------- | AP4_SampleDescRtpConstructor::AP4_SampleDescRtpConstructor +---------------------------------------------------------------------*/ AP4_SampleDescRtpConstructor::AP4_SampleDescRtpConstructor(AP4_UI08 track_ref_index, AP4_UI16 length, AP4_UI32 sample_desc_index, AP4_UI32 sample_desc_offset) : AP4_RtpConstructor(AP4_RTP_CONSTRUCTOR_TYPE_SAMPLE_DESC), m_TrackRefIndex(track_ref_index), m_Length(length), m_SampleDescIndex(sample_desc_index), m_SampleDescOffset(sample_desc_offset) {} /*---------------------------------------------------------------------- | AP4_SampleDescRtpConstructor::AP4_SampleDescRtpConstructor +---------------------------------------------------------------------*/ AP4_SampleDescRtpConstructor::AP4_SampleDescRtpConstructor(AP4_ByteStream& stream) : AP4_RtpConstructor(AP4_RTP_CONSTRUCTOR_TYPE_SAMPLE_DESC) { // offset AP4_Position cur_offset; stream.Tell(cur_offset); // data stream.ReadUI08(m_TrackRefIndex); stream.ReadUI16(m_Length); stream.ReadUI32(m_SampleDescIndex); stream.ReadUI32(m_SampleDescOffset); // reposition the stream stream.Seek(cur_offset+15); } /*---------------------------------------------------------------------- | AP4_SampleDescRtpConstructor::DoWrite +---------------------------------------------------------------------*/ AP4_Result AP4_SampleDescRtpConstructor::DoWrite(AP4_ByteStream& stream) { AP4_Result result = stream.WriteUI08(m_TrackRefIndex); if (AP4_FAILED(result)) return result; result = stream.WriteUI16(m_Length); if (AP4_FAILED(result)) return result; result = stream.WriteUI32(m_SampleDescIndex); if (AP4_FAILED(result)) return result; result = stream.WriteUI32(m_SampleDescOffset); if (AP4_FAILED(result)) return result; return stream.WriteUI32(0); // reserved } /*---------------------------------------------------------------------- | AP4_RtpConstructorFactory::CreateConstructorFromStream +---------------------------------------------------------------------*/ AP4_Result AP4_RtpConstructorFactory::CreateConstructorFromStream(AP4_ByteStream& stream, AP4_RtpConstructor*& constructor) { // read the first byte (type) AP4_RtpConstructor::Type type; AP4_Result result = stream.ReadUI08(type); if (AP4_FAILED(result)) return result; // now create the right constructor switch(type) { case AP4_RTP_CONSTRUCTOR_TYPE_NOOP: constructor = new AP4_NoopRtpConstructor(stream); break; case AP4_RTP_CONSTRUCTOR_TYPE_IMMEDIATE: constructor = new AP4_ImmediateRtpConstructor(stream); break; case AP4_RTP_CONSTRUCTOR_TYPE_SAMPLE: constructor = new AP4_SampleRtpConstructor(stream); break; case AP4_RTP_CONSTRUCTOR_TYPE_SAMPLE_DESC: constructor = new AP4_SampleDescRtpConstructor(stream); break; default: return AP4_ERROR_INVALID_RTP_CONSTRUCTOR_TYPE; } return AP4_SUCCESS; }