in FBControlCore/Utility/FBVideoStream.m [45:153]
BOOL WriteFrameToAnnexBStream(CMSampleBufferRef sampleBuffer, id<FBDataConsumer> consumer, id<FBControlCoreLogger> logger, NSError **error)
{
if (!CMSampleBufferDataIsReady(sampleBuffer)) {
return [[FBControlCoreError
describeFormat:@"Sample Buffer is not ready"]
failBool:error];
}
NSData *headerData = AnnexBNALUStartCodeData();
NSMutableData *consumableData = [NSMutableData alloc];
bool isKeyFrame = false;
CFArrayRef attachments =
CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true);
if (CFArrayGetCount(attachments)) {
CFDictionaryRef attachment = (CFDictionaryRef)CFArrayGetValueAtIndex(attachments, 0);
CFBooleanRef dependsOnOthers = (CFBooleanRef)CFDictionaryGetValue(
attachment, kCMSampleAttachmentKey_DependsOnOthers);
isKeyFrame = (dependsOnOthers == kCFBooleanFalse);
}
if (isKeyFrame) {
CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer);
size_t spsSize, spsCount;
const uint8_t *spsParameterSet;
OSStatus status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
format,
0,
&spsParameterSet,
&spsSize,
&spsCount,
0
);
if (status != noErr) {
return [[FBControlCoreError
describeFormat:@"Failed to get SPS Params %d", status]
failBool:error];
}
size_t ppsSize, ppsCount;
const uint8_t *ppsParameterSet;
status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
format,
1,
&ppsParameterSet,
&ppsSize,
&ppsCount,
0
);
if (status != noErr) {
return [[FBControlCoreError
describeFormat:@"Failed to get PPS Params %d", status]
failBool:error];
}
NSData *spsData = [NSData dataWithBytes:spsParameterSet length:spsSize];
NSData *ppsData = [NSData dataWithBytes:ppsParameterSet length:ppsSize];
[consumableData appendData:headerData];
[consumableData appendData:spsData];
[consumableData appendData:headerData];
[consumableData appendData:ppsData];
}
// Get the underlying data buffer.
CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
size_t dataLength;
char *dataPointer;
OSStatus status = CMBlockBufferGetDataPointer(
dataBuffer,
0,
NULL,
&dataLength,
&dataPointer
);
if (status != noErr) {
return [[FBControlCoreError
describeFormat:@"Failed to get Data Pointer %d", status]
failBool:error];
}
// Enumerate the data buffer
size_t dataOffset = 0;
while (dataOffset < dataLength - AVCCHeaderLength) {
// Write start code to the elementary stream
[consumableData appendData:headerData];
// Get our current position in the buffer
void *currentDataPointer = dataPointer + dataOffset;
// Get the length of the NAL Unit, this is contained in the current offset.
// This will tell us how many bytes to write in the current NAL unit, contained in the buffer.
uint32_t nalLength = 0;
memcpy(&nalLength, currentDataPointer, AVCCHeaderLength);
// Convert the length value from Big-endian to Little-endian.
nalLength = CFSwapInt32BigToHost(nalLength);
// Write the NAL unit without the AVCC length header to the elementary stream
void *nalUnitPointer = currentDataPointer + AVCCHeaderLength;
if ([consumer conformsToProtocol:@protocol(FBDataConsumerSync)]) {
NSData *nalUnitData = [NSData dataWithBytesNoCopy:nalUnitPointer length:nalLength freeWhenDone:NO];
[consumableData appendData:nalUnitData];
} else {
NSData *nalUnitData = [NSData dataWithBytes:nalUnitPointer length:nalLength];
[consumableData appendData:nalUnitData];
}
// Increment the offset for the next iteration.
dataOffset += AVCCHeaderLength + nalLength;
}
[consumer consumeData:consumableData];
return YES;
}