diff options
Diffstat (limited to 'debian/mp4v2/mp4v2-2.0.0~dfsg0/src/rtphint.cpp')
-rw-r--r-- | debian/mp4v2/mp4v2-2.0.0~dfsg0/src/rtphint.cpp | 1358 |
1 files changed, 1358 insertions, 0 deletions
diff --git a/debian/mp4v2/mp4v2-2.0.0~dfsg0/src/rtphint.cpp b/debian/mp4v2/mp4v2-2.0.0~dfsg0/src/rtphint.cpp new file mode 100644 index 00000000..e07309d6 --- /dev/null +++ b/debian/mp4v2/mp4v2-2.0.0~dfsg0/src/rtphint.cpp @@ -0,0 +1,1358 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is MPEG4IP. + * + * The Initial Developer of the Original Code is Cisco Systems Inc. + * Portions created by Cisco Systems Inc. are + * Copyright (C) Cisco Systems Inc. 2001. All Rights Reserved. + * + * Contributor(s): + * Dave Mackie [email protected] + */ + +#include "src/impl.h" + +namespace mp4v2 { +namespace impl { + +/////////////////////////////////////////////////////////////////////////////// + +/* rtp hint track operations */ + +MP4RtpHintTrack::MP4RtpHintTrack(MP4File& file, MP4Atom& trakAtom) + : MP4Track(file, trakAtom) +{ + m_pRefTrack = NULL; + + m_pRtpMapProperty = NULL; + m_pPayloadNumberProperty = NULL; + m_pMaxPacketSizeProperty = NULL; + m_pSnroProperty = NULL; + m_pTsroProperty = NULL; + + m_pReadHint = NULL; + m_pReadHintSample = NULL; + m_readHintSampleSize = 0; + + m_pWriteHint = NULL; + m_writeHintId = MP4_INVALID_SAMPLE_ID; + m_writePacketId = 0; + + m_pTrpy = NULL; + m_pNump = NULL; + m_pTpyl = NULL; + m_pMaxr = NULL; + m_pDmed = NULL; + m_pDimm = NULL; + m_pPmax = NULL; + m_pDmax = NULL; + + m_pMaxPdu = NULL; + m_pAvgPdu = NULL; + m_pMaxBitRate = NULL; + m_pAvgBitRate = NULL; + + m_thisSec = 0; + m_bytesThisSec = 0; + m_bytesThisHint = 0; + m_bytesThisPacket = 0; +} + +MP4RtpHintTrack::~MP4RtpHintTrack() +{ + delete m_pReadHint; m_pReadHint = NULL; + MP4Free( m_pReadHintSample ); m_pReadHintSample = NULL; + delete m_pWriteHint; m_pWriteHint = NULL; +} + +void MP4RtpHintTrack::InitRefTrack() +{ + if (m_pRefTrack == NULL) { + MP4Integer32Property* pRefTrackIdProperty = NULL; + (void)m_trakAtom.FindProperty( + "trak.tref.hint.entries[0].trackId", + (MP4Property**)&pRefTrackIdProperty); + ASSERT(pRefTrackIdProperty); + + m_pRefTrack = m_File.GetTrack(pRefTrackIdProperty->GetValue()); + } +} + +void MP4RtpHintTrack::InitRtpStart() +{ + number::srandom( time::getLocalTimeMilliseconds() ); + + (void)m_trakAtom.FindProperty( + "trak.udta.hnti.rtp .snro.offset", + (MP4Property**)&m_pSnroProperty); + + if (m_pSnroProperty) { + m_rtpSequenceStart = m_pSnroProperty->GetValue(); + } else { + m_rtpSequenceStart = number::random32(); + } + + (void)m_trakAtom.FindProperty( + "trak.udta.hnti.rtp .tsro.offset", + (MP4Property**)&m_pTsroProperty); + + if (m_pTsroProperty) { + m_rtpTimestampStart = m_pTsroProperty->GetValue(); + } else { + m_rtpTimestampStart = number::random32(); + } +} + +void MP4RtpHintTrack::ReadHint( + MP4SampleId hintSampleId, + uint16_t* pNumPackets) +{ + if (m_pRefTrack == NULL) { + InitRefTrack(); + InitRtpStart(); + } + + // dispose of any old hint + delete m_pReadHint; m_pReadHint = NULL; + MP4Free( m_pReadHintSample ); m_pReadHintSample = NULL; + m_readHintSampleSize = 0; + + // read the desired hint sample into memory + ReadSample( + hintSampleId, + &m_pReadHintSample, + &m_readHintSampleSize, + &m_readHintTimestamp); + + m_File.EnableMemoryBuffer(m_pReadHintSample, m_readHintSampleSize); + + m_pReadHint = new MP4RtpHint(*this); + m_pReadHint->Read(m_File); + + m_File.DisableMemoryBuffer(); + + if (pNumPackets) { + *pNumPackets = GetHintNumberOfPackets(); + } +} + +uint16_t MP4RtpHintTrack::GetHintNumberOfPackets() +{ + if (m_pReadHint == NULL) { + throw new Exception("no hint has been read", + __FILE__, __LINE__, __FUNCTION__); + } + return m_pReadHint->GetNumberOfPackets(); +} + +bool MP4RtpHintTrack::GetPacketBFrame(uint16_t packetIndex) +{ + if (m_pReadHint == NULL) { + throw new Exception("no hint has been read", + __FILE__, __LINE__, __FUNCTION__); + } + MP4RtpPacket* pPacket = + m_pReadHint->GetPacket(packetIndex); + + return pPacket->IsBFrame(); +} + +uint16_t MP4RtpHintTrack::GetPacketTransmitOffset(uint16_t packetIndex) +{ + if (m_pReadHint == NULL) { + throw new Exception("no hint has been read", + __FILE__, __LINE__, __FUNCTION__); + } + + MP4RtpPacket* pPacket = + m_pReadHint->GetPacket(packetIndex); + + return pPacket->GetTransmitOffset(); +} + +void MP4RtpHintTrack::ReadPacket( + uint16_t packetIndex, + uint8_t** ppBytes, + uint32_t* pNumBytes, + uint32_t ssrc, + bool addHeader, + bool addPayload) +{ + if (m_pReadHint == NULL) { + throw new Exception("no hint has been read", + __FILE__, __LINE__, __FUNCTION__); + } + if (!addHeader && !addPayload) { + throw new Exception("no data requested", + __FILE__, __LINE__, __FUNCTION__); + } + + MP4RtpPacket* pPacket = + m_pReadHint->GetPacket(packetIndex); + + *pNumBytes = 0; + if (addHeader) { + *pNumBytes += 12; + } + if (addPayload) { + *pNumBytes += pPacket->GetDataSize(); + } + + // if needed, allocate the packet memory + bool buffer_malloc = false; + + if (*ppBytes == NULL) { + *ppBytes = (uint8_t*)MP4Malloc(*pNumBytes); + buffer_malloc = true; + } + + try { + uint8_t* pDest = *ppBytes; + + if (addHeader) { + *pDest++ = + 0x80 | (pPacket->GetPBit() << 5) | (pPacket->GetXBit() << 4); + + *pDest++ = + (pPacket->GetMBit() << 7) | pPacket->GetPayload(); + + *((uint16_t*)pDest) = + MP4V2_HTONS(m_rtpSequenceStart + pPacket->GetSequenceNumber()); + pDest += 2; + + *((uint32_t*)pDest) = + MP4V2_HTONL(m_rtpTimestampStart + (uint32_t)m_readHintTimestamp); + pDest += 4; + + *((uint32_t*)pDest) = + MP4V2_HTONL(ssrc); + pDest += 4; + } + + if (addPayload) { + pPacket->GetData(pDest); + } + } + catch (Exception* x) { + if (buffer_malloc) { + MP4Free(*ppBytes); + *ppBytes = NULL; + } + throw x; + } + + log.hexDump(0, MP4_LOG_VERBOSE1, *ppBytes, *pNumBytes, + "\"%s\": %u ", GetFile().GetFilename().c_str(), + packetIndex); +} + +MP4Timestamp MP4RtpHintTrack::GetRtpTimestampStart() +{ + if (m_pRefTrack == NULL) { + InitRefTrack(); + InitRtpStart(); + } + + return m_rtpTimestampStart; +} + +void MP4RtpHintTrack::SetRtpTimestampStart(MP4Timestamp start) +{ + if (!m_pTsroProperty) { + MP4Atom* pTsroAtom = + m_File.AddDescendantAtoms(&m_trakAtom, "udta.hnti.rtp .tsro"); + + ASSERT(pTsroAtom); + + (void)pTsroAtom->FindProperty("offset", + (MP4Property**)&m_pTsroProperty); + + ASSERT(m_pTsroProperty); + } + + m_pTsroProperty->SetValue(start); + m_rtpTimestampStart = start; +} + +void MP4RtpHintTrack::InitPayload() +{ + if (m_pRtpMapProperty == NULL) { + (void)m_trakAtom.FindProperty( + "trak.udta.hinf.payt.rtpMap", + (MP4Property**)&m_pRtpMapProperty); + } + + if (m_pPayloadNumberProperty == NULL) { + (void)m_trakAtom.FindProperty( + "trak.udta.hinf.payt.payloadNumber", + (MP4Property**)&m_pPayloadNumberProperty); + } + + if (m_pMaxPacketSizeProperty == NULL) { + (void)m_trakAtom.FindProperty( + "trak.mdia.minf.stbl.stsd.rtp .maxPacketSize", + (MP4Property**)&m_pMaxPacketSizeProperty); + } +} + +void MP4RtpHintTrack::GetPayload( + char** ppPayloadName, + uint8_t* pPayloadNumber, + uint16_t* pMaxPayloadSize, + char **ppEncodingParams) +{ + const char* pRtpMap; + const char* pSlash; + uint32_t length; + InitPayload(); + + if (ppPayloadName || ppEncodingParams) { + if (ppPayloadName) + *ppPayloadName = NULL; + if (ppEncodingParams) + *ppEncodingParams = NULL; + if (m_pRtpMapProperty) { + pRtpMap = m_pRtpMapProperty->GetValue(); + pSlash = strchr(pRtpMap, '/'); + + if (pSlash) { + length = pSlash - pRtpMap; + } else { + length = (uint32_t)strlen(pRtpMap); + } + + if (ppPayloadName) { + *ppPayloadName = (char*)MP4Calloc(length + 1); + strncpy(*ppPayloadName, pRtpMap, length); + } + if (pSlash && ppEncodingParams) { + pSlash++; + pSlash = strchr(pSlash, '/'); + if (pSlash != NULL) { + pSlash++; + if (pSlash != '\0') { + length = (uint32_t)strlen(pRtpMap) - (pSlash - pRtpMap); + *ppEncodingParams = (char *)MP4Calloc(length + 1); + strncpy(*ppEncodingParams, pSlash, length); + } + } + } + } + } + + if (pPayloadNumber) { + if (m_pPayloadNumberProperty) { + *pPayloadNumber = m_pPayloadNumberProperty->GetValue(); + } else { + *pPayloadNumber = 0; + } + } + + if (pMaxPayloadSize) { + if (m_pMaxPacketSizeProperty) { + *pMaxPayloadSize = m_pMaxPacketSizeProperty->GetValue(); + } else { + *pMaxPayloadSize = 0; + } + } +} + +void MP4RtpHintTrack::SetPayload( + const char* payloadName, + uint8_t payloadNumber, + uint16_t maxPayloadSize, + const char *encoding_parms, + bool include_rtp_map, + bool include_mpeg4_esid) +{ + InitRefTrack(); + InitPayload(); + + ASSERT(m_pRtpMapProperty); + ASSERT(m_pPayloadNumberProperty); + ASSERT(m_pMaxPacketSizeProperty); + + size_t len = strlen(payloadName) + 16; + if (encoding_parms != NULL) { + size_t temp = strlen(encoding_parms); + if (temp == 0) { + encoding_parms = NULL; + } else { + len += temp; + } + } + + char* rtpMapBuf = (char*)MP4Malloc(len); + snprintf(rtpMapBuf, len, "%s/%u%c%s", + payloadName, + GetTimeScale(), + encoding_parms != NULL ? '/' : '\0', + encoding_parms == NULL ? "" : encoding_parms); + m_pRtpMapProperty->SetValue(rtpMapBuf); + + m_pPayloadNumberProperty->SetValue(payloadNumber); + + if (maxPayloadSize == 0) { + maxPayloadSize = 1460; + } + m_pMaxPacketSizeProperty->SetValue(maxPayloadSize); + + // set sdp media type + const char* sdpMediaType; + if (!strcmp(m_pRefTrack->GetType(), MP4_AUDIO_TRACK_TYPE)) { + sdpMediaType = "audio"; + } else if (!strcmp(m_pRefTrack->GetType(), MP4_VIDEO_TRACK_TYPE)) { + sdpMediaType = "video"; + } else if (!strcmp(m_pRefTrack->GetType(), MP4_CNTL_TRACK_TYPE)) { + sdpMediaType = "control"; + } else { + sdpMediaType = "application"; + } + + uint32_t maxlen = + (uint32_t)strlen(sdpMediaType) + (uint32_t)strlen(rtpMapBuf) + 256; + char* sdpBuf = (char*)MP4Malloc(maxlen); + uint32_t buflen; + buflen = snprintf(sdpBuf, maxlen, + "m=%s 0 RTP/AVP %u\015\012" + "a=control:trackID=%u\015\012", + sdpMediaType, payloadNumber, + m_trackId); + if (include_rtp_map) { + buflen += snprintf(sdpBuf + buflen, maxlen - buflen, + "a=rtpmap:%u %s\015\012", + payloadNumber, rtpMapBuf); + } + if (include_mpeg4_esid) { + snprintf(sdpBuf + buflen, maxlen - buflen, + "a=mpeg4-esid:%u\015\012", + m_pRefTrack->GetId()); + } + + MP4StringProperty* pSdpProperty = NULL; + (void)m_trakAtom.FindProperty("trak.udta.hnti.sdp .sdpText", + (MP4Property**)&pSdpProperty); + ASSERT(pSdpProperty); + pSdpProperty->SetValue(sdpBuf); + + // cleanup + MP4Free(rtpMapBuf); + MP4Free(sdpBuf); +} + +void MP4RtpHintTrack::AddHint(bool isBFrame, uint32_t timestampOffset) +{ + // on first hint, need to lookup the reference track + if (m_writeHintId == MP4_INVALID_SAMPLE_ID) { + InitRefTrack(); + InitStats(); + } + + if (m_pWriteHint) { + throw new Exception("unwritten hint is still pending", __FILE__, __LINE__, __FUNCTION__); + } + + m_pWriteHint = new MP4RtpHint(*this); + m_pWriteHint->SetBFrame(isBFrame); + m_pWriteHint->SetTimestampOffset(timestampOffset); + + m_bytesThisHint = 0; + m_writeHintId++; +} + +void MP4RtpHintTrack::AddPacket(bool setMbit, int32_t transmitOffset) +{ + if (m_pWriteHint == NULL) { + throw new Exception("no hint pending", __FILE__, __LINE__, __FUNCTION__); + } + + MP4RtpPacket* pPacket = m_pWriteHint->AddPacket(); + + ASSERT(m_pPayloadNumberProperty); + + pPacket->Set( + m_pPayloadNumberProperty->GetValue(), + m_writePacketId++, + setMbit); + pPacket->SetTransmitOffset(transmitOffset); + + m_bytesThisHint += 12; + if (m_bytesThisPacket > m_pPmax->GetValue()) { + m_pPmax->SetValue(m_bytesThisPacket); + } + m_bytesThisPacket = 12; + m_pNump->IncrementValue(); + m_pTrpy->IncrementValue(12); // RTP packet header size +} + +void MP4RtpHintTrack::AddImmediateData( + const uint8_t* pBytes, + uint32_t numBytes) +{ + if (m_pWriteHint == NULL) { + throw new Exception("no hint pending", __FILE__, __LINE__, __FUNCTION__ ); + } + + MP4RtpPacket* pPacket = m_pWriteHint->GetCurrentPacket(); + if (pPacket == NULL) { + throw new Exception("no packet pending", __FILE__, __LINE__, __FUNCTION__); + } + + if (pBytes == NULL || numBytes == 0) { + throw new Exception("no data", + __FILE__, __LINE__, __FUNCTION__ ); + } + if (numBytes > 14) { + throw new Exception("data size is larger than 14 bytes", + __FILE__, __LINE__, __FUNCTION__ ); + } + + MP4RtpImmediateData* pData = new MP4RtpImmediateData(*pPacket); + pData->Set(pBytes, numBytes); + + pPacket->AddData(pData); + + m_bytesThisHint += numBytes; + m_bytesThisPacket += numBytes; + m_pDimm->IncrementValue(numBytes); + m_pTpyl->IncrementValue(numBytes); + m_pTrpy->IncrementValue(numBytes); +} + +void MP4RtpHintTrack::AddSampleData( + MP4SampleId sampleId, + uint32_t dataOffset, + uint32_t dataLength) +{ + if (m_pWriteHint == NULL) { + throw new Exception("no hint pending", __FILE__, __LINE__, __FUNCTION__ ); + } + + MP4RtpPacket* pPacket = m_pWriteHint->GetCurrentPacket(); + if (pPacket == NULL) { + throw new Exception("no packet pending", __FILE__, __LINE__, __FUNCTION__ ); + } + + MP4RtpSampleData* pData = new MP4RtpSampleData(*pPacket); + + pData->SetReferenceSample(sampleId, dataOffset, dataLength); + + pPacket->AddData(pData); + + m_bytesThisHint += dataLength; + m_bytesThisPacket += dataLength; + m_pDmed->IncrementValue(dataLength); + m_pTpyl->IncrementValue(dataLength); + m_pTrpy->IncrementValue(dataLength); +} + +void MP4RtpHintTrack::AddESConfigurationPacket() +{ + if (m_pWriteHint == NULL) { + throw new Exception("no hint pending", __FILE__, __LINE__, __FUNCTION__ ); + } + + uint8_t* pConfig = NULL; + uint32_t configSize = 0; + + m_File.GetTrackESConfiguration(m_pRefTrack->GetId(), + &pConfig, &configSize); + + if (pConfig == NULL) { + return; + } + + ASSERT(m_pMaxPacketSizeProperty); + + if (configSize > m_pMaxPacketSizeProperty->GetValue()) { + throw new Exception("ES configuration is too large for RTP payload", __FILE__, __LINE__, __FUNCTION__ ); + } + + AddPacket(false); + + MP4RtpPacket* pPacket = m_pWriteHint->GetCurrentPacket(); + ASSERT(pPacket); + + // This is ugly! + // To get the ES configuration data somewhere known + // we create a sample data reference that points to + // this hint track (not the media track) + // and this sample of the hint track + // the offset into this sample is filled in during the write process + MP4RtpSampleData* pData = new MP4RtpSampleData(*pPacket); + + pData->SetEmbeddedImmediate(m_writeSampleId, pConfig, configSize); + + pPacket->AddData(pData); + + m_bytesThisHint += configSize; + m_bytesThisPacket += configSize; + m_pTpyl->IncrementValue(configSize); + m_pTrpy->IncrementValue(configSize); +} + +void MP4RtpHintTrack::WriteHint(MP4Duration duration, bool isSyncSample) +{ + if (m_pWriteHint == NULL) { + throw new Exception("no hint pending", __FILE__, __LINE__, __FUNCTION__ ); + } + + uint8_t* pBytes; + uint64_t numBytes; + + m_File.EnableMemoryBuffer(); + + m_pWriteHint->Write(m_File); + + m_File.DisableMemoryBuffer(&pBytes, &numBytes); + + WriteSample(pBytes, numBytes, duration, 0, isSyncSample); + + MP4Free(pBytes); + + // update statistics + if (m_bytesThisPacket > m_pPmax->GetValue()) { + m_pPmax->SetValue(m_bytesThisPacket); + } + + if (duration > m_pDmax->GetValue()) { + m_pDmax->SetValue(duration); + } + + MP4Timestamp startTime; + + GetSampleTimes(m_writeHintId, &startTime, NULL); + + if (startTime < m_thisSec + GetTimeScale()) { + m_bytesThisSec += m_bytesThisHint; + } else { + if (m_bytesThisSec > m_pMaxr->GetValue()) { + m_pMaxr->SetValue(m_bytesThisSec); + } + m_thisSec = startTime - (startTime % GetTimeScale()); + m_bytesThisSec = m_bytesThisHint; + } + + // cleanup + delete m_pWriteHint; + m_pWriteHint = NULL; +} + +void MP4RtpHintTrack::FinishWrite(uint32_t option) +{ + if (m_writeHintId != MP4_INVALID_SAMPLE_ID) { + m_pMaxPdu->SetValue(m_pPmax->GetValue()); + if (m_pNump->GetValue()) { + m_pAvgPdu->SetValue(m_pTrpy->GetValue() / m_pNump->GetValue()); + } + + m_pMaxBitRate->SetValue(m_pMaxr->GetValue() * 8); + if (GetDuration()) { + m_pAvgBitRate->SetValue( + m_pTrpy->GetValue() * 8 * GetTimeScale() / GetDuration()); + } + } + + MP4Track::FinishWrite(); +} + +void MP4RtpHintTrack::InitStats() +{ + MP4Atom* pHinfAtom = m_trakAtom.FindAtom("trak.udta.hinf"); + + ASSERT(pHinfAtom); + + (void)pHinfAtom->FindProperty("hinf.trpy.bytes", (MP4Property**)&m_pTrpy); + (void)pHinfAtom->FindProperty("hinf.nump.packets", (MP4Property**)&m_pNump); + (void)pHinfAtom->FindProperty("hinf.tpyl.bytes", (MP4Property**)&m_pTpyl); + (void)pHinfAtom->FindProperty("hinf.maxr.bytes", (MP4Property**)&m_pMaxr); + (void)pHinfAtom->FindProperty("hinf.dmed.bytes", (MP4Property**)&m_pDmed); + (void)pHinfAtom->FindProperty("hinf.dimm.bytes", (MP4Property**)&m_pDimm); + (void)pHinfAtom->FindProperty("hinf.pmax.bytes", (MP4Property**)&m_pPmax); + (void)pHinfAtom->FindProperty("hinf.dmax.milliSecs", (MP4Property**)&m_pDmax); + + MP4Atom* pHmhdAtom = m_trakAtom.FindAtom("trak.mdia.minf.hmhd"); + + ASSERT(pHmhdAtom); + + (void)pHmhdAtom->FindProperty("hmhd.maxPduSize", (MP4Property**)&m_pMaxPdu); + (void)pHmhdAtom->FindProperty("hmhd.avgPduSize", (MP4Property**)&m_pAvgPdu); + (void)pHmhdAtom->FindProperty("hmhd.maxBitRate", (MP4Property**)&m_pMaxBitRate); + (void)pHmhdAtom->FindProperty("hmhd.avgBitRate", (MP4Property**)&m_pAvgBitRate); + + MP4Integer32Property* pMaxrPeriod = NULL; + (void)pHinfAtom->FindProperty("hinf.maxr.granularity", + (MP4Property**)&pMaxrPeriod); + if (pMaxrPeriod) { + pMaxrPeriod->SetValue(1000); // 1 second + } +} + + +MP4RtpHint::MP4RtpHint(MP4RtpHintTrack& track) + : m_track(track) +{ + AddProperty( /* 0 */ + new MP4Integer16Property(this->GetTrack().GetTrakAtom(), "packetCount")); + AddProperty( /* 1 */ + new MP4Integer16Property(this->GetTrack().GetTrakAtom(), "reserved")); +} + +MP4RtpHint::~MP4RtpHint() +{ + for (uint32_t i = 0; i < m_rtpPackets.Size(); i++) { + delete m_rtpPackets[i]; + } +} + +MP4RtpPacket* MP4RtpHint::AddPacket() +{ + MP4RtpPacket* pPacket = new MP4RtpPacket(*this); + m_rtpPackets.Add(pPacket); + + // packetCount property + ((MP4Integer16Property*)m_pProperties[0])->IncrementValue(); + + pPacket->SetBFrame(m_isBFrame); + pPacket->SetTimestampOffset(m_timestampOffset); + + return pPacket; +} + +void MP4RtpHint::Read(MP4File& file) +{ + // call base class Read for required properties + MP4Container::Read(file); + + uint16_t numPackets = + ((MP4Integer16Property*)m_pProperties[0])->GetValue(); + + for (uint16_t i = 0; i < numPackets; i++) { + MP4RtpPacket* pPacket = new MP4RtpPacket(*this); + + m_rtpPackets.Add(pPacket); + + pPacket->Read(file); + } + + if (log.verbosity >= MP4_LOG_VERBOSE1) { + log.verbose1f("\"%s\": ReadHint:", GetTrack().GetFile().GetFilename().c_str()); + Dump(10, false); + } +} + +void MP4RtpHint::Write(MP4File& file) +{ + uint64_t hintStartPos = file.GetPosition(); + + MP4Container::Write(file); + + uint64_t packetStartPos = file.GetPosition(); + + uint32_t i; + + // first write out packet (and data) entries + for (i = 0; i < m_rtpPackets.Size(); i++) { + m_rtpPackets[i]->Write(file); + } + + // now let packets write their extra data into the hint sample + for (i = 0; i < m_rtpPackets.Size(); i++) { + m_rtpPackets[i]->WriteEmbeddedData(file, hintStartPos); + } + + uint64_t endPos = file.GetPosition(); + + file.SetPosition(packetStartPos); + + // finally rewrite the packet and data entries + // which now contain the correct offsets for the embedded data + for (i = 0; i < m_rtpPackets.Size(); i++) { + m_rtpPackets[i]->Write(file); + } + + file.SetPosition(endPos); + + if (log.verbosity >= MP4_LOG_VERBOSE1) { + log.verbose1f("\"%s\": WriteRtpHint:", GetTrack().GetFile().GetFilename().c_str()); + Dump(14, false); + } +} + +void MP4RtpHint::Dump(uint8_t indent, bool dumpImplicits) +{ + MP4Container::Dump(indent, dumpImplicits); + + for (uint32_t i = 0; i < m_rtpPackets.Size(); i++) { + log.dump(indent, MP4_LOG_VERBOSE1,"\"%s\": RtpPacket: %u", + GetTrack().GetFile().GetFilename().c_str(), i); + m_rtpPackets[i]->Dump(indent + 1, dumpImplicits); + } +} + +MP4RtpPacket::MP4RtpPacket(MP4RtpHint& hint) + : m_hint(hint) +{ + AddProperty( /* 0 */ + new MP4Integer32Property(this->GetHint().GetTrack().GetTrakAtom(), "relativeXmitTime")); + AddProperty( /* 1 */ + new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "reserved1", 2)); + AddProperty( /* 2 */ + new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "Pbit", 1)); + AddProperty( /* 3 */ + new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "Xbit", 1)); + AddProperty( /* 4 */ + new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "reserved2", 4)); + AddProperty( /* 5 */ + new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "Mbit", 1)); + AddProperty( /* 6 */ + new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "payloadType", 7)); + AddProperty( /* 7 */ + new MP4Integer16Property(this->GetHint().GetTrack().GetTrakAtom(), "sequenceNumber")); + AddProperty( /* 8 */ + new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "reserved3", 13)); + AddProperty( /* 9 */ + new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "extraFlag", 1)); + AddProperty( /* 10 */ + new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "bFrameFlag", 1)); + AddProperty( /* 11 */ + new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "repeatFlag", 1)); + AddProperty( /* 12 */ + new MP4Integer16Property(this->GetHint().GetTrack().GetTrakAtom(), "entryCount")); +} + +MP4RtpPacket::~MP4RtpPacket() +{ + for (uint32_t i = 0; i < m_rtpData.Size(); i++) { + delete m_rtpData[i]; + } +} + +void MP4RtpPacket::AddExtraProperties() +{ + AddProperty( /* 13 */ + new MP4Integer32Property(this->GetHint().GetTrack().GetTrakAtom(), "extraInformationLength")); + + // This is a bit of a hack, since the tlv entries are really defined + // as atoms but there is only one type defined now, rtpo, and getting + // our atom code hooked up here would be a major pain with little gain + + AddProperty( /* 14 */ + new MP4Integer32Property(this->GetHint().GetTrack().GetTrakAtom(), "tlvLength")); + AddProperty( /* 15 */ + new MP4StringProperty(this->GetHint().GetTrack().GetTrakAtom(), "tlvType")); + AddProperty( /* 16 */ + new MP4Integer32Property(this->GetHint().GetTrack().GetTrakAtom(), "timestampOffset")); + + ((MP4Integer32Property*)m_pProperties[13])->SetValue(16); + ((MP4Integer32Property*)m_pProperties[14])->SetValue(12); + ((MP4StringProperty*)m_pProperties[15])->SetFixedLength(4); + ((MP4StringProperty*)m_pProperties[15])->SetValue("rtpo"); +} + +void MP4RtpPacket::Read(MP4File& file) +{ + // call base class Read for required properties + MP4Container::Read(file); + + // read extra info if present + // we only support the rtpo field! + if (((MP4BitfieldProperty*)m_pProperties[9])->GetValue() == 1) { + ReadExtra(file); + } + + uint16_t numDataEntries = + ((MP4Integer16Property*)m_pProperties[12])->GetValue(); + + // read data entries + for (uint16_t i = 0; i < numDataEntries; i++) { + uint8_t dataType; + file.PeekBytes(&dataType, 1); + + MP4RtpData* pData; + + switch (dataType) { + case 0: + pData = new MP4RtpNullData(*this); + break; + case 1: + pData = new MP4RtpImmediateData(*this); + break; + case 2: + pData = new MP4RtpSampleData(*this); + break; + case 3: + pData = new MP4RtpSampleDescriptionData(*this); + break; + default: + throw new Exception("unknown packet data entry type", __FILE__, __LINE__, __FUNCTION__ ); + } + + m_rtpData.Add(pData); + + // read data entry's properties + pData->Read(file); + } +} + +void MP4RtpPacket::ReadExtra(MP4File& file) +{ + AddExtraProperties(); + + int32_t extraLength = (int32_t)file.ReadUInt32(); + + if (extraLength < 4) { + throw new Exception("bad packet extra info length", __FILE__, __LINE__, __FUNCTION__ ); + } + extraLength -= 4; + + while (extraLength > 0) { + uint32_t entryLength = file.ReadUInt32(); + uint32_t entryTag = file.ReadUInt32(); + + if (entryLength < 8) { + throw new Exception("bad packet extra info entry length", __FILE__, __LINE__, __FUNCTION__ ); + } + + if (entryTag == STRTOINT32("rtpo") && entryLength == 12) { + // read the rtp timestamp offset + m_pProperties[16]->Read(file); + } else { + // ignore it, LATER carry it along + file.SetPosition(file.GetPosition() + entryLength - 8); + } + + extraLength -= entryLength; + } + + if (extraLength < 0) { + throw new Exception("invalid packet extra info length", __FILE__, __LINE__, __FUNCTION__ ); + } +} + +void MP4RtpPacket::Set(uint8_t payloadNumber, + uint32_t packetId, bool setMbit) +{ + ((MP4BitfieldProperty*)m_pProperties[5])->SetValue(setMbit); + ((MP4BitfieldProperty*)m_pProperties[6])->SetValue(payloadNumber); + ((MP4Integer16Property*)m_pProperties[7])->SetValue(packetId); +} + +int32_t MP4RtpPacket::GetTransmitOffset() +{ + return ((MP4Integer32Property*)m_pProperties[0])->GetValue(); +} + +void MP4RtpPacket::SetTransmitOffset(int32_t transmitOffset) +{ + ((MP4Integer32Property*)m_pProperties[0])->SetValue(transmitOffset); +} + +bool MP4RtpPacket::GetPBit() +{ + return ((MP4BitfieldProperty*)m_pProperties[2])->GetValue(); +} + +bool MP4RtpPacket::GetXBit() +{ + return ((MP4BitfieldProperty*)m_pProperties[3])->GetValue(); +} + +bool MP4RtpPacket::GetMBit() +{ + return ((MP4BitfieldProperty*)m_pProperties[5])->GetValue(); +} + +uint8_t MP4RtpPacket::GetPayload() +{ + return ((MP4BitfieldProperty*)m_pProperties[6])->GetValue(); +} + +uint16_t MP4RtpPacket::GetSequenceNumber() +{ + return ((MP4Integer16Property*)m_pProperties[7])->GetValue(); +} + +bool MP4RtpPacket::IsBFrame() +{ + return ((MP4BitfieldProperty*)m_pProperties[10])->GetValue(); +} + +void MP4RtpPacket::SetBFrame(bool isBFrame) +{ + ((MP4BitfieldProperty*)m_pProperties[10])->SetValue(isBFrame); +} + +void MP4RtpPacket::SetTimestampOffset(uint32_t timestampOffset) +{ + if (timestampOffset == 0) { + return; + } + + ASSERT(((MP4BitfieldProperty*)m_pProperties[9])->GetValue() == 0); + + // set X bit + ((MP4BitfieldProperty*)m_pProperties[9])->SetValue(1); + + AddExtraProperties(); + + ((MP4Integer32Property*)m_pProperties[16])->SetValue(timestampOffset); +} + +void MP4RtpPacket::AddData(MP4RtpData* pData) +{ + m_rtpData.Add(pData); + + // increment entry count property + ((MP4Integer16Property*)m_pProperties[12])->IncrementValue(); +} + +uint32_t MP4RtpPacket::GetDataSize() +{ + uint32_t totalDataSize = 0; + + for (uint32_t i = 0; i < m_rtpData.Size(); i++) { + totalDataSize += m_rtpData[i]->GetDataSize(); + } + + return totalDataSize; +} + +void MP4RtpPacket::GetData(uint8_t* pDest) +{ + for (uint32_t i = 0; i < m_rtpData.Size(); i++) { + m_rtpData[i]->GetData(pDest); + pDest += m_rtpData[i]->GetDataSize(); + } +} + +void MP4RtpPacket::Write(MP4File& file) +{ + MP4Container::Write(file); + + for (uint32_t i = 0; i < m_rtpData.Size(); i++) { + m_rtpData[i]->Write(file); + } +} + +void MP4RtpPacket::WriteEmbeddedData(MP4File& file, uint64_t startPos) +{ + for (uint32_t i = 0; i < m_rtpData.Size(); i++) { + m_rtpData[i]->WriteEmbeddedData(file, startPos); + } +} + +void MP4RtpPacket::Dump(uint8_t indent, bool dumpImplicits) +{ + MP4Container::Dump(indent, dumpImplicits); + + for (uint32_t i = 0; i < m_rtpData.Size(); i++) { + log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": RtpData: %u", + GetHint().GetTrack().GetFile().GetFilename().c_str(), i); + m_rtpData[i]->Dump(indent + 1, dumpImplicits); + } +} + +MP4RtpData::MP4RtpData(MP4RtpPacket& packet) + : m_packet(packet) +{ + AddProperty( /* 0 */ + new MP4Integer8Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "type")); +} + +MP4Track* MP4RtpData::FindTrackFromRefIndex(uint8_t refIndex) +{ + MP4Track* pTrack; + + if (refIndex == (uint8_t)-1) { + // ourselves + pTrack = &GetPacket().GetHint().GetTrack(); + } else if (refIndex == 0) { + // our reference track + pTrack = GetPacket().GetHint().GetTrack().GetRefTrack(); + } else { + // some other track + MP4RtpHintTrack* pHintTrack = + &GetPacket().GetHint().GetTrack(); + + MP4Atom& trakAtom = pHintTrack->GetTrakAtom(); + + MP4Integer32Property* pTrackIdProperty = NULL; + (void)trakAtom.FindProperty( + "trak.tref.hint.entries", + (MP4Property**)&pTrackIdProperty); + ASSERT(pTrackIdProperty); + + uint32_t refTrackId = + pTrackIdProperty->GetValue(refIndex - 1); + + pTrack = pHintTrack->GetFile().GetTrack(refTrackId); + } + + return pTrack; +} + +MP4RtpNullData::MP4RtpNullData(MP4RtpPacket& packet) + : MP4RtpData(packet) +{ + ((MP4Integer8Property*)m_pProperties[0])->SetValue(0); + + AddProperty( /* 1 */ + new MP4BytesProperty(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "pad", 15)); + + ((MP4BytesProperty*)m_pProperties[1])->SetFixedSize(15); +} + +MP4RtpImmediateData::MP4RtpImmediateData(MP4RtpPacket& packet) + : MP4RtpData(packet) +{ + ((MP4Integer8Property*)m_pProperties[0])->SetValue(1); + + AddProperty( /* 1 */ + new MP4Integer8Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "count")); + AddProperty( /* 2 */ + new MP4BytesProperty(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "data", 14)); + + ((MP4BytesProperty*)m_pProperties[2])->SetFixedSize(14); +} + +void MP4RtpImmediateData::Set(const uint8_t* pBytes, uint8_t numBytes) +{ + ((MP4Integer8Property*)m_pProperties[1])->SetValue(numBytes); + ((MP4BytesProperty*)m_pProperties[2])->SetValue(pBytes, numBytes); +} + +uint16_t MP4RtpImmediateData::GetDataSize() +{ + return ((MP4Integer8Property*)m_pProperties[1])->GetValue(); +} + +void MP4RtpImmediateData::GetData(uint8_t* pDest) +{ + uint8_t* pValue; + uint32_t valueSize; + ((MP4BytesProperty*)m_pProperties[2])->GetValue(&pValue, &valueSize); + + memcpy(pDest, pValue, GetDataSize()); + MP4Free(pValue); +} + +MP4RtpSampleData::MP4RtpSampleData(MP4RtpPacket& packet) + : MP4RtpData(packet) +{ + ((MP4Integer8Property*)m_pProperties[0])->SetValue(2); + + AddProperty( /* 1 */ + new MP4Integer8Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "trackRefIndex")); + AddProperty( /* 2 */ + new MP4Integer16Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "length")); + AddProperty( /* 3 */ + new MP4Integer32Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "sampleNumber")); + AddProperty( /* 4 */ + new MP4Integer32Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "sampleOffset")); + AddProperty( /* 5 */ + new MP4Integer16Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "bytesPerBlock")); + AddProperty( /* 6 */ + new MP4Integer16Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "samplesPerBlock")); + + ((MP4Integer16Property*)m_pProperties[5])->SetValue(1); + ((MP4Integer16Property*)m_pProperties[6])->SetValue(1); + + m_pRefData = NULL; + m_pRefTrack = NULL; + m_refSampleId = MP4_INVALID_SAMPLE_ID; + m_refSampleOffset = 0; +} + +void MP4RtpSampleData::SetEmbeddedImmediate(MP4SampleId sampleId, + uint8_t* pData, uint16_t dataLength) +{ + ((MP4Integer8Property*)m_pProperties[1])->SetValue((uint8_t)-1); + ((MP4Integer16Property*)m_pProperties[2])->SetValue(dataLength); + ((MP4Integer32Property*)m_pProperties[3])->SetValue(sampleId); + ((MP4Integer32Property*)m_pProperties[4])->SetValue(0); + CHECK_AND_FREE(m_pRefData); + m_pRefData = pData; +} + +void MP4RtpSampleData::SetReferenceSample( + MP4SampleId refSampleId, uint32_t refSampleOffset, + uint16_t sampleLength) +{ + ((MP4Integer8Property*)m_pProperties[1])->SetValue(0); + ((MP4Integer16Property*)m_pProperties[2])->SetValue(sampleLength); + ((MP4Integer32Property*)m_pProperties[3])->SetValue(refSampleId); + ((MP4Integer32Property*)m_pProperties[4])->SetValue(refSampleOffset); +} + +void MP4RtpSampleData::SetEmbeddedSample( + MP4SampleId sampleId, MP4Track* pRefTrack, + MP4SampleId refSampleId, uint32_t refSampleOffset, + uint16_t sampleLength) +{ + ((MP4Integer8Property*)m_pProperties[1])->SetValue((uint8_t)-1); + ((MP4Integer16Property*)m_pProperties[2])->SetValue(sampleLength); + ((MP4Integer32Property*)m_pProperties[3])->SetValue(sampleId); + ((MP4Integer32Property*)m_pProperties[4])->SetValue(0); + m_pRefTrack = pRefTrack; + m_refSampleId = refSampleId; + m_refSampleOffset = refSampleOffset; +} + +uint16_t MP4RtpSampleData::GetDataSize() +{ + return ((MP4Integer16Property*)m_pProperties[2])->GetValue(); +} + +void MP4RtpSampleData::GetData(uint8_t* pDest) +{ + uint8_t trackRefIndex = + ((MP4Integer8Property*)m_pProperties[1])->GetValue(); + + MP4Track* pSampleTrack = + FindTrackFromRefIndex(trackRefIndex); + + pSampleTrack->ReadSampleFragment( + ((MP4Integer32Property*)m_pProperties[3])->GetValue(), // sampleId + ((MP4Integer32Property*)m_pProperties[4])->GetValue(), // sampleOffset + ((MP4Integer16Property*)m_pProperties[2])->GetValue(), // sampleLength + pDest); +} + +void MP4RtpSampleData::WriteEmbeddedData(MP4File& file, uint64_t startPos) +{ + // if not using embedded data, nothing to do + if (((MP4Integer8Property*)m_pProperties[1])->GetValue() != (uint8_t)-1) { + return; + } + + // figure out the offset within this hint sample for this embedded data + uint64_t offset = file.GetPosition() - startPos; + ASSERT(offset <= 0xFFFFFFFF); + ((MP4Integer32Property*)m_pProperties[4])->SetValue((uint32_t)offset); + + uint16_t length = ((MP4Integer16Property*)m_pProperties[2])->GetValue(); + + if (m_pRefData) { + file.WriteBytes(m_pRefData, length); + return; + } + + if (m_refSampleId != MP4_INVALID_SAMPLE_ID) { + uint8_t* pSample = NULL; + uint32_t sampleSize = 0; + + ASSERT(m_pRefTrack); + m_pRefTrack->ReadSample(m_refSampleId, &pSample, &sampleSize); + + ASSERT(m_refSampleOffset + length <= sampleSize); + + file.WriteBytes(&pSample[m_refSampleOffset], length); + + MP4Free(pSample); + return; + } +} + +MP4RtpSampleDescriptionData::MP4RtpSampleDescriptionData(MP4RtpPacket& packet) + : MP4RtpData(packet) +{ + ((MP4Integer8Property*)m_pProperties[0])->SetValue(3); + + AddProperty( /* 1 */ + new MP4Integer8Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "trackRefIndex")); + AddProperty( /* 2 */ + new MP4Integer16Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "length")); + AddProperty( /* 3 */ + new MP4Integer32Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "sampleDescriptionIndex")); + AddProperty( /* 4 */ + new MP4Integer32Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "sampleDescriptionOffset")); + AddProperty( /* 5 */ + new MP4Integer32Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "reserved")); +} + +void MP4RtpSampleDescriptionData::Set(uint32_t sampleDescrIndex, + uint32_t offset, uint16_t length) +{ + ((MP4Integer16Property*)m_pProperties[2])->SetValue(length); + ((MP4Integer32Property*)m_pProperties[3])->SetValue(sampleDescrIndex); + ((MP4Integer32Property*)m_pProperties[4])->SetValue(offset); +} + +uint16_t MP4RtpSampleDescriptionData::GetDataSize() +{ + return ((MP4Integer16Property*)m_pProperties[2])->GetValue(); +} + +void MP4RtpSampleDescriptionData::GetData(uint8_t* pDest) +{ + // we start with the index into our track references + uint8_t trackRefIndex = + ((MP4Integer8Property*)m_pProperties[1])->GetValue(); + + // from which we can find the track structure + MP4Track* pSampleTrack = + FindTrackFromRefIndex(trackRefIndex); + + // next find the desired atom in the track's sample description table + uint32_t sampleDescrIndex = + ((MP4Integer32Property*)m_pProperties[3])->GetValue(); + + MP4Atom& trakAtom = + pSampleTrack->GetTrakAtom(); + + char sdName[64]; + snprintf(sdName, 64, "trak.mdia.minf.stbl.stsd.*[%u]", sampleDescrIndex); + + MP4Atom* pSdAtom = + trakAtom.FindAtom(sdName); + + // bad reference + if (pSdAtom == NULL) { + throw new Exception("invalid sample description index", __FILE__, __LINE__, __FUNCTION__ ); + } + + // check validity of the upcoming copy + uint16_t length = + ((MP4Integer16Property*)m_pProperties[2])->GetValue(); + uint32_t offset = + ((MP4Integer32Property*)m_pProperties[4])->GetValue(); + + if (offset + length > pSdAtom->GetSize()) { + throw new Exception("offset and/or length are too large", + __FILE__, __LINE__, __FUNCTION__); + } + + // now we use the raw file to get the desired bytes + + MP4File& file = GetPacket().GetHint().GetTrack().GetFile(); + + uint64_t orgPos = file.GetPosition(); + + // It's not entirely clear from the spec whether the offset is from + // the start of the sample descirption atom, or the start of the atom's + // data. I believe it is the former, but the commented out code will + // realize the latter interpretation if I turn out to be wrong. + uint64_t dataPos = pSdAtom->GetStart(); + //uint64_t dataPos = pSdAtom->GetEnd() - pSdAtom->GetSize(); + + file.SetPosition(dataPos + offset); + + file.ReadBytes(pDest, length); + + file.SetPosition(orgPos); +} + +/////////////////////////////////////////////////////////////////////////////// + +} +} // namespace mp4v2::impl |