我使用ffmpeg实现了视频播放器。每个帧都可以成功解码,并且可以保存到有效的jpg文件中,并且可以在模拟器中运行时以UIImageView显示。但是,当我在模拟器中运行我的应用程序时,内存无限增长。此外,在设备上运行两次p_diaplayNextFrame后,该应用程序将崩溃。如果我评论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)

任何提示表示赞赏!

最佳答案

我认为问题是因为ZCVFrameSec *frameSec超出了-(void)p_displayNextFrame方法的范围后才进行了重新分配。

如果您在CFDataCreateWithBytesNoCopy的字节数组上使用了frameSec,则在完成在frameSeccgImage中的使用之前,字节数组可能会与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];
    });
}

关于ios - UIImage setImage崩溃,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24179937/

10-13 03:26