Skip to content
This repository was archived by the owner on Oct 31, 2022. It is now read-only.

Commit 2ab589f

Browse files
committed
Compressor: improve recovery from low memory
Respond to out-of-memory errors when allocating per-frame resources by waiting for pending frames to complete (thus freeing up resources) and trying again. Partial solution to #20
1 parent 4088036 commit 2ab589f

File tree

1 file changed

+106
-53
lines changed

1 file changed

+106
-53
lines changed

source/HapCompressor.c

Lines changed: 106 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ struct HapCodecCompressTask
114114
ICMMutableEncodedFrameRef encodedFrame;
115115
void * encodedFrameBaseAddress;
116116
unsigned long encodedFrameActualSize;
117+
HapCodecBufferRef formatConvertBuffer;
118+
HapCodecBufferRef dxtBuffer;
117119
ComponentResult error;
118120
HapCodecBufferRef next; // Used to queue finished tasks
119121
};
@@ -145,7 +147,11 @@ struct HapCodecCompressTask
145147
#endif
146148

147149
static void Background_Encode(void *info);
148-
static void releaseTaskFrames(HapCodecCompressTask *task);
150+
/*
151+
createTask() returns a complete task or NULL on error (which is likely to be due to buffer allocation failure)
152+
*/
153+
static HapCodecBufferRef createTask(HapCompressorGlobals glob, ICMCompressorSourceFrameRef sourceFrame, ICMMutableEncodedFrameRef encodedFrame);
154+
static void disposeTask(HapCodecCompressTask *task);
149155
static ComponentResult finishFrame(HapCodecBufferRef buffer);
150156
static void queueEncodedFrame(HapCompressorGlobals glob, HapCodecBufferRef frame);
151157
static HapCodecBufferRef dequeueNextFrameOut(HapCompressorGlobals glob);
@@ -261,7 +267,7 @@ Hap_CClose(
261267
HapCodecBufferRef next;
262268
if (task)
263269
{
264-
releaseTaskFrames(task);
270+
disposeTask(task);
265271
next = task->next;
266272
}
267273
else
@@ -585,8 +591,6 @@ Hap_CPrepareToCompressFrames(
585591

586592
static void Background_Encode(void *info)
587593
{
588-
HapCodecBufferRef formatConvertBuffer = NULL;
589-
HapCodecBufferRef dxtBuffer = NULL;
590594
HapCodecCompressTask *task = (HapCodecCompressTask *)HapCodecBufferGetBaseAddress((HapCodecBufferRef)info);
591595
HapCompressorGlobals glob = task->glob;
592596

@@ -641,22 +645,14 @@ static void Background_Encode(void *info)
641645
// If necessary, convert the pixels to a format the encoder can ingest
642646
if (wantedPixelFormat != sourcePixelFormat)
643647
{
644-
formatConvertBuffer = HapCodecBufferCreate(glob->formatConvertPool);
645-
646-
if (formatConvertBuffer == NULL)
647-
{
648-
err = memFullErr;
649-
goto bail;
650-
}
651-
652648
// for all these we pass 0 as last argument so we don't tile, we are already multithreaded
653649
switch (wantedPixelFormat)
654650
{
655651
case kHapCVPixelFormat_CoCgXY:
656652
if (sourcePixelFormat == k32BGRAPixelFormat)
657653
{
658654
ConvertBGR_ToCoCg_Y8888((uint8_t *)sourceBaseAddress,
659-
(uint8_t *)HapCodecBufferGetBaseAddress(formatConvertBuffer),
655+
(uint8_t *)HapCodecBufferGetBaseAddress(task->formatConvertBuffer),
660656
glob->width,
661657
glob->height,
662658
sourceBytesPerRow,
@@ -666,7 +662,7 @@ static void Background_Encode(void *info)
666662
else
667663
{
668664
ConvertRGB_ToCoCg_Y8888((uint8_t *)sourceBaseAddress,
669-
(uint8_t *)HapCodecBufferGetBaseAddress(formatConvertBuffer),
665+
(uint8_t *)HapCodecBufferGetBaseAddress(task->formatConvertBuffer),
670666
glob->width,
671667
glob->height,
672668
sourceBytesPerRow,
@@ -680,7 +676,7 @@ static void Background_Encode(void *info)
680676
uint8_t permuteMap[] = {2, 1, 0, 3};
681677
ImageMath_Permute8888(sourceBaseAddress,
682678
sourceBytesPerRow,
683-
HapCodecBufferGetBaseAddress(formatConvertBuffer),
679+
HapCodecBufferGetBaseAddress(task->formatConvertBuffer),
684680
glob->formatConvertBufferBytesPerRow,
685681
glob->width,
686682
glob->height,
@@ -699,7 +695,7 @@ static void Background_Encode(void *info)
699695
break;
700696
}
701697

702-
encode_src = HapCodecBufferGetBaseAddress(formatConvertBuffer);
698+
encode_src = HapCodecBufferGetBaseAddress(task->formatConvertBuffer);
703699
encode_src_bytes_per_row = glob->formatConvertBufferBytesPerRow;
704700
}
705701
else // wantedPixelFormat == sourcePixelFormat
@@ -710,18 +706,11 @@ static void Background_Encode(void *info)
710706

711707
// Encode the DXT frame
712708

713-
dxtBuffer = HapCodecBufferCreate(glob->dxtBufferPool);
714-
if (dxtBuffer == NULL)
715-
{
716-
err = memFullErr;
717-
goto bail;
718-
}
719-
720709
result = glob->dxtEncoder->encode_function(glob->dxtEncoder,
721710
encode_src,
722711
encode_src_bytes_per_row,
723712
wantedPixelFormat,
724-
HapCodecBufferGetBaseAddress(dxtBuffer),
713+
HapCodecBufferGetBaseAddress(task->dxtBuffer),
725714
glob->width,
726715
glob->height);
727716

@@ -742,11 +731,11 @@ static void Background_Encode(void *info)
742731
#endif
743732

744733
// Return the format conversion buffer as soon as we can to minimise the number created
745-
HapCodecBufferReturn(formatConvertBuffer);
746-
formatConvertBuffer = NULL;
734+
HapCodecBufferReturn(task->formatConvertBuffer);
735+
task->formatConvertBuffer = NULL;
747736

748-
codec_src = HapCodecBufferGetBaseAddress(dxtBuffer);
749-
codec_src_length = HapCodecBufferGetSize(dxtBuffer);
737+
codec_src = HapCodecBufferGetBaseAddress(task->dxtBuffer);
738+
codec_src_length = HapCodecBufferGetSize(task->dxtBuffer);
750739
}
751740

752741
hapResult = HapEncode(codec_src,
@@ -774,9 +763,11 @@ static void Background_Encode(void *info)
774763
bail:
775764
debug_print_err(glob, err);
776765
if (task->sourceBuffer) CVPixelBufferUnlockBaseAddress(task->sourceBuffer, kHapCodecCVPixelBufferLockFlags);
777-
HapCodecBufferReturn(formatConvertBuffer);
778-
HapCodecBufferReturn(dxtBuffer);
779-
766+
HapCodecBufferReturn(task->formatConvertBuffer);
767+
task->formatConvertBuffer = NULL;
768+
HapCodecBufferReturn(task->dxtBuffer);
769+
task->dxtBuffer = NULL;
770+
780771
task->error = err;
781772

782773
// Queue the encoded frame for output
@@ -798,8 +789,7 @@ Hap_CEncodeFrame(
798789

799790
CVPixelBufferRef sourcePixelBuffer = ICMCompressorSourceFrameGetPixelBuffer(sourceFrame);
800791
OSType sourceFormat = CVPixelBufferGetPixelFormatType(sourcePixelBuffer);
801-
HapCodecBufferRef buffer;
802-
HapCodecCompressTask *task;
792+
HapCodecBufferRef buffer = NULL;
803793
ICMMutableEncodedFrameRef encodedFrame = NULL;
804794

805795
if (!isDXTPixelFormat(sourceFormat))
@@ -869,31 +859,40 @@ Hap_CEncodeFrame(
869859

870860
}
871861
}
872-
862+
873863
// Create an empty encoded frame
874864
err = ICMEncodedFrameCreateMutable(glob->session, sourceFrame, glob->maxEncodedDataSize, &encodedFrame);
875-
if (err)
876-
goto bail;
865+
if (err != noErr)
866+
encodedFrame = NULL;
877867

878-
// Create a task and dispatch it
879-
buffer = HapCodecBufferCreate(glob->compressTaskPool);
880-
task = (HapCodecCompressTask *)HapCodecBufferGetBaseAddress(buffer);
881-
if (task == NULL)
868+
if (err == noErr)
869+
{
870+
buffer = createTask(glob, sourceFrame, encodedFrame);
871+
if (buffer == NULL)
872+
err = memFullErr;
873+
}
874+
875+
if (err == memFullErr)
876+
{
877+
// in case of memory allocation failure, finish all pending tasks and try again
878+
Hap_CCompleteFrame(glob, NULL, 0);
879+
880+
if (encodedFrame == NULL)
881+
err = ICMEncodedFrameCreateMutable(glob->session, sourceFrame, glob->maxEncodedDataSize, &encodedFrame);
882+
883+
if (err == noErr)
884+
buffer = createTask(glob, sourceFrame, encodedFrame);
885+
}
886+
887+
ICMEncodedFrameRelease(encodedFrame);
888+
encodedFrame = NULL;
889+
890+
if (buffer == NULL)
882891
{
883892
err = memFullErr;
884893
goto bail;
885894
}
886-
887-
task->sourceFrame = ICMCompressorSourceFrameRetain(sourceFrame);
888-
task->sourceBuffer = ICMCompressorSourceFrameGetPixelBuffer(sourceFrame);
889-
task->glob = glob;
890-
task->error = noErr;
891-
task->encodedFrame = encodedFrame;
892-
task->encodedFrameBaseAddress = ICMEncodedFrameGetDataPtr(encodedFrame);
893-
task->next = NULL;
894-
895-
encodedFrame = NULL;
896-
895+
897896
HapCodecTasksAddTask(glob->taskGroup, buffer);
898897

899898
// Dequeue and deliver any encoded frames
@@ -959,12 +958,14 @@ Hap_CCompleteFrame(
959958
return err;
960959
}
961960

962-
static void releaseTaskFrames(HapCodecCompressTask *task)
961+
static void disposeTask(HapCodecCompressTask *task)
963962
{
964963
if (task)
965964
{
966965
ICMCompressorSourceFrameRelease(task->sourceFrame);
967966
ICMEncodedFrameRelease(task->encodedFrame);
967+
HapCodecBufferReturn(task->dxtBuffer);
968+
HapCodecBufferReturn(task->formatConvertBuffer);
968969
}
969970
}
970971

@@ -999,7 +1000,7 @@ static ComponentResult finishFrame(HapCodecBufferRef buffer)
9991000
{
10001001
ICMCompressorSessionDropFrame(task->glob->session, task->sourceFrame);
10011002
}
1002-
releaseTaskFrames(task);
1003+
disposeTask(task);
10031004
}
10041005
return err;
10051006
}
@@ -1051,3 +1052,55 @@ static HapCodecBufferRef dequeueNextFrameOut(HapCompressorGlobals glob)
10511052
}
10521053
return found;
10531054
}
1055+
1056+
static HapCodecBufferRef createTask(HapCompressorGlobals glob, ICMCompressorSourceFrameRef sourceFrame, ICMMutableEncodedFrameRef encodedFrame)
1057+
{
1058+
HapCodecBufferRef buffer = HapCodecBufferCreate(glob->compressTaskPool);
1059+
if (buffer)
1060+
{
1061+
HapCodecCompressTask *task = (HapCodecCompressTask *)HapCodecBufferGetBaseAddress(buffer);
1062+
1063+
task->sourceFrame = ICMCompressorSourceFrameRetain(sourceFrame);
1064+
task->sourceBuffer = ICMCompressorSourceFrameGetPixelBuffer(sourceFrame);
1065+
task->glob = glob;
1066+
task->encodedFrame = (ICMMutableEncodedFrameRef)ICMEncodedFrameRetain(encodedFrame);
1067+
task->encodedFrameBaseAddress = ICMEncodedFrameGetDataPtr(task->encodedFrame);
1068+
task->error = noErr;
1069+
task->next = NULL;
1070+
task->formatConvertBuffer = NULL;
1071+
task->dxtBuffer = NULL;
1072+
1073+
if (task->error == noErr)
1074+
{
1075+
// Create any necessary temporary buffers
1076+
OSType sourcePixelFormat = CVPixelBufferGetPixelFormatType(task->sourceBuffer);
1077+
if (!isDXTPixelFormat(sourcePixelFormat))
1078+
{
1079+
OSType wantedPixelFormat = glob->dxtEncoder->pixelformat_function(glob->dxtEncoder, sourcePixelFormat);
1080+
1081+
if (wantedPixelFormat != sourcePixelFormat)
1082+
{
1083+
task->formatConvertBuffer = HapCodecBufferCreate(glob->formatConvertPool);
1084+
1085+
if (task->formatConvertBuffer == NULL)
1086+
task->error = memFullErr;
1087+
}
1088+
1089+
if (task->error == noErr)
1090+
{
1091+
task->dxtBuffer = HapCodecBufferCreate(glob->dxtBufferPool);
1092+
1093+
if (task->dxtBuffer == NULL)
1094+
task->error = memFullErr;
1095+
}
1096+
}
1097+
}
1098+
if (task->error != noErr)
1099+
{
1100+
disposeTask(task);
1101+
HapCodecBufferReturn(buffer);
1102+
buffer = NULL;
1103+
}
1104+
}
1105+
return buffer;
1106+
}

0 commit comments

Comments
 (0)