static ComponentResult __PacketizeSampleData( RTPMPComponentVideoInstanceData ** inGlobals, const RTPMPSampleDataParams * inSampleData ) { ComponentResult theError; RTPPacketGroupRef thePacketGroup; UInt32 theDataOffset; RTPPacketRef thePacket; UInt32 theDataSize; ComponentVideoPayload theHeader; UInt32 theHeaderSize; /* This packetizer sends all the data for one frame in a single packet group. */ theError = RTPPBBeginPacketGroup( ( **inGlobals ).itsPacketBuilder, __kNoFlags, inSampleData->timeStamp, &thePacketGroup ); if( !theError ) { /* Most network packets for the frame will be uniformly sized, with a fixed header and a fixed amount of sample data. The first network packet will include whatever data is left over (taken from the start of the sample data, not the end), as well as a fixed header and a payload description. */ theDataOffset = 0; theDataSize = ( **inGlobals ).itsFrameDataSize % ( **inGlobals ).itsPayloadDataSize; theHeader = ( **inGlobals ).itsPayloadHeader; theHeaderSize = sizeof( theHeader ); /* Construct network packets until the sample data for this frame is exhausted. */ while( theDataOffset < ( **inGlobals ).itsFrameDataSize && !theError ) { theError = RTPPBBeginPacket( ( **inGlobals ).itsPacketBuilder, __kNoFlags, thePacketGroup, ( **inGlobals ).itsPacketSizeLimit, &thePacket ); if( !theError ) { /* The header (with optional payload description) is added to the network packet as literal data. The data is written directly to the network packet. If the RTPPacketBuilder is storing network packet data to disk, it must store a copy of literal data. */ theError = RTPPBAddPacketLiteralData( ( **inGlobals ).itsPacketBuilder, __kNoFlags, thePacketGroup, thePacket, REINTERPRET_CAST( UInt8 * )( &theHeader ), theHeaderSize, NULL ); if( !theError ) { /* The RTPPacketBuilder provides a routine specifically for adding sample data. For stored movies, the RTPPacketBuilder need not store a copy of sample data, since the data is already stored in the movie. */ theError = RTPPBAddPacketSampleData( ( **inGlobals ).itsPacketBuilder, __kNoFlags, thePacketGroup, thePacket, CONST_CAST( RTPMPSampleDataParams * )( inSampleData ), theDataOffset, theDataSize, NULL ); if( !theError ) { /* The packetizer sets the RTP/AVP marker bit in the last network packet of a frame. The QuickTime Streaming base RTPReassembler can better assist in reassembling the payload data if this bit is used to mark the end of a packet group. Most payloads have no duration, except for the last payload, which absorbs the duration for the entire frame. */ if( theDataOffset + theDataSize < ( **inGlobals ).itsFrameDataSize ) { theError = RTPPBEndPacket( ( **inGlobals ).itsPacketBuilder, __kNoFlags, thePacketGroup, thePacket, 0 /* inTimeOffset */, 0 /* inDuration */ ); } else { theError = RTPPBEndPacket( ( **inGlobals ).itsPacketBuilder, kRTPPBSetMarkerFlag, thePacketGroup, thePacket, 0 /* inTimeOffset */, inSampleData->duration ); } } } } /* For this packetizer, packets after the first network packet of a frame are uniformly sized and have no payload description. */ if( theDataOffset ) { theDataOffset += theDataSize; } else { theDataOffset += theDataSize; theDataSize = ( **inGlobals ).itsPayloadDataSize; theHeaderSize = sizeof( theHeader.itsFixedHeader ); ComponentVideoPayloadSetDescription( &theHeader, 0, 0 ); } /* Update the Offset field of the header to indicate the next block of sample data. */ ComponentVideoPayloadSetOffset( &theHeader, theDataOffset ); } if( theError ) { RTPPBEndPacketGroup( ( **inGlobals ).itsPacketBuilder, kRTPPBDontSendFlag, thePacketGroup ); } else { /* For this packetizer, every group contains only sync samples. That means the sample data for the group can be decoded independently of any previous sample data. When randomly accessing stored movies, a streaming server can look for sync samples. */ theError = RTPPBEndPacketGroup( ( **inGlobals ).itsPacketBuilder, kRTPPBSyncSampleFlag, thePacketGroup ); } } return( theError ); }
ComponentResult XMRTPH264Packetizer_SetSampleData(XMRTPH264PacketizerGlobals globals, const RTPMPSampleDataParams *sampleData, SInt32 *outFlags) { UInt32 maxPacketLength = globals->maxPacketSize_Hard; if (globals->useNonInterleavedMode == true) { maxPacketLength = globals->maxPacketSize_Soft; } const UInt8 *data = sampleData->data; UInt32 dataLength = sampleData->dataLength; UInt32 index = 0; ComponentInstance packetBuilder = globals->packetBuilder; RTPPacketGroupRef packetGroupRef; RTPPBBeginPacketGroup(packetBuilder, 0, sampleData->timeStamp, &packetGroupRef); // Check whether SPS and PPS units should be sent if ((sampleData->flags & 1) != 0) { RTPPacketRef packetRef; RTPPBBeginPacket(packetBuilder, 0, packetGroupRef, globals->spsAtomLength, &packetRef); RTPPBAddPacketLiteralData(packetBuilder, 0, packetGroupRef, packetRef, globals->spsAtomData, globals->spsAtomLength, NULL); RTPPBEndPacket(packetBuilder, 0, packetGroupRef, packetRef, 0, 0); RTPPBBeginPacket(packetBuilder, 0, packetGroupRef, globals->ppsAtomLength, &packetRef); RTPPBAddPacketLiteralData(packetBuilder, 0, packetGroupRef, packetRef, globals->ppsAtomData, globals->ppsAtomLength, NULL); RTPPBEndPacket(packetBuilder, 0, packetGroupRef, packetRef, 0, 0); } do { // Getting the size of this NAL unit UInt32 *lengthPtr = (UInt32 *)&(data[index]); index += 4; UInt32 nalLength = ntohl(lengthPtr[0]); // If the NAL does not fit within one single packet, // we have to use FU-A packets, which are only available // in the non-interleaved mode if (nalLength > maxPacketLength && globals->useNonInterleavedMode == true) { // Send some FU-A packets UInt8 nri = (data[index] >> 5) & 0x03; UInt8 type = data[index] & 0x1f; UInt32 remainingLength = nalLength; index += 1; remainingLength -= 1; while (remainingLength > 0) { UInt8 s = 0; UInt8 e = 0; UInt32 sendDataLength = maxPacketLength-2; if (remainingLength == nalLength-1) { s = 1; } else if (remainingLength <= (maxPacketLength - 2)) { e = 1; sendDataLength = remainingLength; } UInt8 header[2]; header[0] = 0; header[0] |= (nri << 5); header[0] |= 28; header[1] = 0; header[1] = (s << 7); header[1] |= (e << 6); header[1] |= type; RTPPacketRef packetRef; RTPPBBeginPacket(packetBuilder, 0, packetGroupRef, sendDataLength+2, &packetRef); RTPPBAddPacketLiteralData(packetBuilder, 0, packetGroupRef, packetRef, header, 2, NULL); RTPPBAddPacketSampleData(packetBuilder, 0, packetGroupRef, packetRef, (RTPMPSampleDataParams *)sampleData, index, sendDataLength, NULL); index += sendDataLength; remainingLength -= sendDataLength; SInt32 flags = 0; if (index >= dataLength) { flags = 1; } RTPPBEndPacket(packetBuilder, flags, packetGroupRef, packetRef, 0, 0); } } else if (nalLength > maxPacketLength) { // Generating a packet of short length indicates to the subsystem // to drop this packet and increment the RTP Sequence Number by one RTPPacketRef packetRef; RTPPBBeginPacket(packetBuilder, 0, packetGroupRef, 0, &packetRef); UInt8 empty[2]; empty[0] = 0; empty[1] = 0; RTPPBAddPacketLiteralData(packetBuilder, 0, packetGroupRef, packetRef, empty, 2, NULL); RTPPBEndPacket(packetBuilder, 0, packetGroupRef, packetRef, 0, 0); index += nalLength; } else { RTPPacketRef packetRef; RTPPBBeginPacket(packetBuilder, 0, packetGroupRef, nalLength, &packetRef); RTPPBAddPacketSampleData(packetBuilder, 0, packetGroupRef, packetRef, (RTPMPSampleDataParams *)sampleData, index, nalLength, NULL); index += nalLength; SInt32 flags = 0; if (index >= dataLength) { flags = 1; } RTPPBEndPacket(packetBuilder, flags, packetGroupRef, packetRef, 0, 0); } } while (index < dataLength);