我使用ffmpeg
实现了一个视频播放器。每个帧都已成功解码,可以保存到有效的jpg文件中,并且可以在模拟器中运行时显示在UIImageView
中。但是,当我在模拟器中运行我的应用程序时,内存无限增长。此外,在设备上运行时,应用程序将在p_diaplayNextFrame
次激活2次后崩溃。如果我评论self.imageView.image = frame;
,则内存不会泄露,应用程序不会在模拟器或设备上崩溃。
-(void)p_displayNextFrame
{
ZCVFrameSec *frameSec = [video getNextVideoFrameSec];
UIImage *frame = [frameSec toUIImage];
static int fi = 0;
NSAssert( [NSThread isMainThread], @"Fatal error: must be main thread" );
NSString *fileName = [Utilities documentsPath:[NSString stringWithFormat:@"image%06d.jpg",fi++]];
NSLog(@"p_displayNextFrame write image file: %@",fileName);
// frame is saved successfully as jpg, I can view it
[UIImageJPEGRepresentation(frame, 0.7) writeToFile:fileName atomically:YES];
// leak(but not crash) in emulator, crash on device
self.imageView.image = frame;
double delayInSeconds = 0.05;//1/30.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self p_displayNextFrame];
});
}
ZCVFrameSec的toUIImage
- (UIImage*) toUIImage
{
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, [self.data bytes], self.width * self.height * 3,kCFAllocatorNull);
NSAssert( [self.data length] == self.width * self.height * 3,
@"Fatal error: data length:%d, width:%d, height:%d, mul3=%d",
[self.data length],
self.width, self.height, self.width * self.height * 3 );
CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGImageRef cgImage = CGImageCreate(self.width,
self.height,
8,
24,
3 * self.width,
colorSpace,
bitmapInfo,
provider,
NULL,
NO,
kCGRenderingIntentDefault);
UIImage *image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
CGColorSpaceRelease(colorSpace);
CGDataProviderRelease(provider);
CFRelease(data);
return image;
}
崩溃信息:
vImage`__vConvert_RGB888toBGRA8888_block_invoke98:
0x2d72aeb0: push {r4, r5, r6, r7, lr}
0x2d72aeb2: add r7, sp, #0xc
0x2d72aeb4: push.w {r8, r10}
0x2d72aeb8: ldr r2, [r0, #0x14]
0x2d72aeba: ldr r2, [r2, #0x4]
0x2d72aebc: ldr r2, [r2, #0x10]
0x2d72aebe: cmp r2, #0x0
0x2d72aec0: beq.w 0x2d72b076 ; __vConvert_RGB888toBGRA8888_block_invoke98 + 454
0x2d72aec4: vldr d16, [pc, #440]
0x2d72aec8: vmov.i32 q10, #0xff000000
0x2d72aecc: lsl.w r9, r1, #0x3
0x2d72aed0: vldr d18, [pc, #436]
0x2d72aed4: mov.w r12, #0x0
0x2d72aed8: ldr.w r8, [r0, #24]
0x2d72aedc: add.w r5, r0, #0x1c
0x2d72aee0: add.w r6, r12, r9
0x2d72aee4: ldm r5, {r2, r4, r5}
0x2d72aee6: ldr r3, [r0, #0x28]
0x2d72aee8: ldr r1, [r0, #0x2c]
0x2d72aeea: mla r2, r2, r6, r8
0x2d72aeee: mla lr, r5, r6, r4
0x2d72aef2: mla r3, r1, r6, r3
0x2d72aef6: tst.w r3, #0xf
0x2d72aefa: bne 0x2d72af44 ; __vConvert_RGB888toBGRA8888_block_invoke98 + 148
0x2d72aefc: tst.w r2, #0xf
0x2d72af00: bne 0x2d72af80 ; __vConvert_RGB888toBGRA8888_block_invoke98 + 208
0x2d72af02: ldr r4, [r0, #0x30]
0x2d72af04: movs r1, #0x0
0x2d72af06: tst.w lr, #0xf
0x2d72af0a: bne 0x2d72afbc ; __vConvert_RGB888toBGRA8888_block_invoke98 + 268
0x2d72af0c: cmp r4, #0x10
0x2d72af0e: blo 0x2d72aff2 ; __vConvert_RGB888toBGRA8888_block_invoke98 + 322
0x2d72af10: ldr r4, [r0, #0x34]
0x2d72af12: vld3.8 {d0, d2, d4}, [r2]!
Thread 1: EXC_BAD_ACCESS (code=1,address=0x7403000)
任何暗示都表示赞赏!!!
答案 0 :(得分:3)
我认为问题是因为ZCVFrameSec *frameSec
在超出-(void)p_displayNextFrame
方法的范围后被解除分配。
如果您在CFDataCreateWithBytesNoCopy
中的字节数组上使用了frameSec
,那么在您frameSec
中使用字节数组后,字节数组可能会随cgImage
消失,或者您可以比方说,在UIImage *frame
。这导致EXC_BAD_ACCESS
例外。
为什么更改为CFDataCreate
而不是CFDataCreateWithBytesNoCopy
。
要回答您的下一个问题 如何避免复制框架 ,我认为有很多方法可以做到这一点。您可以一直按住frameSec
,直到您将imageView
更改为下一帧,然后才能release
它。
为此,您只能声明一个属性来保存当前帧。像这样:
-(void)p_displayNextFrame
{
ZCVFrameSec *frameSec = [video getNextVideoFrameSec];
UIImage *frame = [frameSec toUIImage];
static int fi = 0;
NSAssert( [NSThread isMainThread], @"Fatal error: must be main thread" );
NSString *fileName = [Utilities documentsPath:[NSString stringWithFormat:@"image%06d.jpg",fi++]];
NSLog(@"p_displayNextFrame write image file: %@",fileName);
// frame is saved successfully as jpg, I can view it
[UIImageJPEGRepresentation(frame, 0.7) writeToFile:fileName atomically:YES];
// leak(but not crash) in emulator, crash on device
self.imageView.image = frame;
// At your header you should declare : @property ZCVFrameSec *currentFrameSec;
self.currentFrameSec = frameSec;
double delayInSeconds = 0.05;//1/30.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self p_displayNextFrame];
});
}