Rewrite buffer patching logic to avoid leaking if the first NALU has an unexpected offset

This commit is contained in:
Cameron Gutman
2022-09-28 17:42:48 -05:00
parent c21f638399
commit 2ae79c5827

View File

@@ -156,82 +156,37 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
} }
} }
- (void)updateBufferForRange:(CMBlockBufferRef)existingBuffer data:(unsigned char *)data offset:(int)offset length:(int)nalLength - (void)updateBufferForRange:(CMBlockBufferRef)frameBuffer dataBlock:(CMBlockBufferRef)dataBuffer offset:(int)offset length:(int)nalLength
{ {
OSStatus status; OSStatus status;
size_t oldOffset = CMBlockBufferGetDataLength(existingBuffer); size_t oldOffset = CMBlockBufferGetDataLength(frameBuffer);
// If we're the first NALU in frame, enqueue this buffer to the memory block // Append a 4 byte buffer to the frame block for the length prefix
// so it can handle freeing it when the block buffer is destroyed status = CMBlockBufferAppendMemoryBlock(frameBuffer, NULL,
if (offset == 0 || offset == 1) { NAL_LENGTH_PREFIX_SIZE,
int dataLength = nalLength - NALU_START_PREFIX_SIZE; kCFAllocatorDefault, NULL, 0,
NAL_LENGTH_PREFIX_SIZE, 0);
// If we get here with offset 0, this is a 3 byte Annex B prefix. This means if (status != noErr) {
// we don't have enough space to do an in-place Annex B -> AVCC fixup. Log(LOG_E, @"CMBlockBufferAppendMemoryBlock failed: %d", (int)status);
// To allow the in-place fixup to work, prepend a 1 byte buffer. return;
if (offset == 0) { }
status = CMBlockBufferAppendMemoryBlock(existingBuffer, NULL, 1,
kCFAllocatorDefault, // Write the length prefix to the new buffer
NULL, 0, 1, 0); const int dataLength = nalLength - NALU_START_PREFIX_SIZE;
if (status != noErr) { const uint8_t lengthBytes[] = {(uint8_t)(dataLength >> 24), (uint8_t)(dataLength >> 16),
Log(LOG_E, @"CMBlockBufferAppendMemoryBlock failed: %d", (int)status); (uint8_t)(dataLength >> 8), (uint8_t)dataLength};
return; status = CMBlockBufferReplaceDataBytes(lengthBytes, frameBuffer,
} oldOffset, NAL_LENGTH_PREFIX_SIZE);
} if (status != noErr) {
Log(LOG_E, @"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
// Pass the real buffer pointer directly (no offset) return;
// This will give it to the block buffer to free when it's released. }
// All further calls to CMBlockBufferAppendMemoryBlock will do so
// at an offset and will not be asking the buffer to be freed. // Attach the data buffer to the frame buffer by reference
status = CMBlockBufferAppendMemoryBlock(existingBuffer, data, status = CMBlockBufferAppendBufferReference(frameBuffer, dataBuffer, offset + NALU_START_PREFIX_SIZE, dataLength, 0);
nalLength + offset, if (status != noErr) {
kCFAllocatorDefault, Log(LOG_E, @"CMBlockBufferAppendBufferReference failed: %d", (int)status);
NULL, 0, nalLength + offset, 0); return;
if (status != noErr) {
Log(LOG_E, @"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
return;
}
// Write the length prefix to existing buffer
const uint8_t lengthBytes[] = {(uint8_t)(dataLength >> 24), (uint8_t)(dataLength >> 16),
(uint8_t)(dataLength >> 8), (uint8_t)dataLength};
status = CMBlockBufferReplaceDataBytes(lengthBytes, existingBuffer,
oldOffset, NAL_LENGTH_PREFIX_SIZE);
if (status != noErr) {
Log(LOG_E, @"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
return;
}
} else {
// Append a 4 byte buffer to this block for the length prefix
status = CMBlockBufferAppendMemoryBlock(existingBuffer, NULL,
NAL_LENGTH_PREFIX_SIZE,
kCFAllocatorDefault, NULL, 0,
NAL_LENGTH_PREFIX_SIZE, 0);
if (status != noErr) {
Log(LOG_E, @"CMBlockBufferAppendMemoryBlock failed: %d", (int)status);
return;
}
// Write the length prefix to the new buffer
int dataLength = nalLength - NALU_START_PREFIX_SIZE;
const uint8_t lengthBytes[] = {(uint8_t)(dataLength >> 24), (uint8_t)(dataLength >> 16),
(uint8_t)(dataLength >> 8), (uint8_t)dataLength};
status = CMBlockBufferReplaceDataBytes(lengthBytes, existingBuffer,
oldOffset, NAL_LENGTH_PREFIX_SIZE);
if (status != noErr) {
Log(LOG_E, @"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
return;
}
// Attach the buffer by reference to the block buffer
status = CMBlockBufferAppendMemoryBlock(existingBuffer, &data[offset+NALU_START_PREFIX_SIZE],
dataLength,
kCFAllocatorNull, // Don't deallocate data on free
NULL, 0, dataLength, 0);
if (status != noErr) {
Log(LOG_E, @"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
return;
}
} }
} }
@@ -335,12 +290,22 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
} }
// Now we're decoding actual frame data here // Now we're decoding actual frame data here
CMBlockBufferRef blockBuffer; CMBlockBufferRef frameBlockBuffer;
CMBlockBufferRef dataBlockBuffer;
status = CMBlockBufferCreateEmpty(NULL, 0, 0, &blockBuffer); status = CMBlockBufferCreateWithMemoryBlock(NULL, data, length, kCFAllocatorDefault, NULL, 0, length, 0, &dataBlockBuffer);
if (status != noErr) {
Log(LOG_E, @"CMBlockBufferCreateWithMemoryBlock failed: %d", (int)status);
free(data);
return DR_NEED_IDR;
}
// From now on, CMBlockBuffer owns the data pointer and will free it when it's dereferenced
status = CMBlockBufferCreateEmpty(NULL, 0, 0, &frameBlockBuffer);
if (status != noErr) { if (status != noErr) {
Log(LOG_E, @"CMBlockBufferCreateEmpty failed: %d", (int)status); Log(LOG_E, @"CMBlockBufferCreateEmpty failed: %d", (int)status);
free(data); CFRelease(dataBlockBuffer);
return DR_NEED_IDR; return DR_NEED_IDR;
} }
@@ -351,7 +316,7 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
// It's the start of a new NALU // It's the start of a new NALU
if (lastOffset != -1) { if (lastOffset != -1) {
// We've seen a start before this so enqueue that NALU // We've seen a start before this so enqueue that NALU
[self updateBufferForRange:blockBuffer data:data offset:lastOffset length:i - lastOffset]; [self updateBufferForRange:frameBlockBuffer dataBlock:dataBlockBuffer offset:lastOffset length:i - lastOffset];
} }
lastOffset = i; lastOffset = i;
@@ -360,22 +325,21 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
if (lastOffset != -1) { if (lastOffset != -1) {
// Enqueue the remaining data // Enqueue the remaining data
[self updateBufferForRange:blockBuffer data:data offset:lastOffset length:length - lastOffset]; [self updateBufferForRange:frameBlockBuffer dataBlock:dataBlockBuffer offset:lastOffset length:length - lastOffset];
} }
// From now on, CMBlockBuffer owns the data pointer and will free it when it's dereferenced
CMSampleBufferRef sampleBuffer; CMSampleBufferRef sampleBuffer;
status = CMSampleBufferCreate(kCFAllocatorDefault, status = CMSampleBufferCreate(kCFAllocatorDefault,
blockBuffer, frameBlockBuffer,
true, NULL, true, NULL,
NULL, formatDesc, 1, 0, NULL, formatDesc, 1, 0,
NULL, 0, NULL, NULL, 0, NULL,
&sampleBuffer); &sampleBuffer);
if (status != noErr) { if (status != noErr) {
Log(LOG_E, @"CMSampleBufferCreate failed: %d", (int)status); Log(LOG_E, @"CMSampleBufferCreate failed: %d", (int)status);
CFRelease(blockBuffer); CFRelease(dataBlockBuffer);
CFRelease(frameBlockBuffer);
return DR_NEED_IDR; return DR_NEED_IDR;
} }
@@ -407,7 +371,8 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
} }
// Dereference the buffers // Dereference the buffers
CFRelease(blockBuffer); CFRelease(dataBlockBuffer);
CFRelease(frameBlockBuffer);
CFRelease(sampleBuffer); CFRelease(sampleBuffer);
return DR_OK; return DR_OK;