| // |
| // DecodedFrame.m |
| // DecoderWrapper |
| // |
| // Created by Mike Montalbo on 6/4/12. |
| // Copyright (c) 2012 Dropcam. All rights reserved. |
| // |
| |
| #import "DecodedFrame.h" |
| #import "NLCommonLoggingNVP.h" |
| |
| static const CGFloat kDCSceneChangeThreshold = 100.0; |
| static const NSInteger kDCSubsampleRatio = 500; |
| |
| @implementation DecodedFrame |
| |
| + (DecodedFrame *)decodedFrameWithWidth:(NSUInteger)width |
| height:(NSUInteger)height |
| imageBuffer:(CVImageBufferRef)imageBuffer |
| timestamp:(int64_t)pts { |
| return [[DecodedFrame alloc] initWithWidth:width |
| height:height |
| imageBuffer:imageBuffer |
| timestamp:pts]; |
| } |
| |
| - (id)initWithWidth:(NSUInteger)width |
| height:(NSUInteger)height |
| yPlane:(NSData *)yPlane |
| uPlane:(NSData *)uPlane |
| vPlane:(NSData *)vPlane |
| timestamp:(int64_t)pts { |
| if((self = [super init])) { |
| _width = width; |
| _height = height; |
| |
| _yPlane = yPlane; |
| _uPlane = uPlane; |
| _vPlane = vPlane; |
| _PTS = pts; |
| } |
| |
| return self; |
| } |
| |
| - (id)initWithWidth:(NSUInteger)width |
| height:(NSUInteger)height |
| imageBuffer:(CVImageBufferRef)imageBuffer |
| timestamp:(int64_t)pts { |
| if((self = [super init])) { |
| _width = width; |
| _height = height; |
| |
| _imageBuffer = imageBuffer; |
| CVBufferRetain(_imageBuffer); |
| |
| _PTS = pts; |
| } |
| |
| return self; |
| } |
| |
| - (NSData *)sceneChangeData { |
| |
| // only the Y'UV planes version of DecodedFrame is used, it seems. |
| // we'll just use the luma (Y') plane. |
| |
| unsigned char *lumaBytes; |
| size_t lumaLength; |
| |
| if (self.imageBuffer) { |
| CVPixelBufferLockBaseAddress(self.imageBuffer, 0); |
| lumaBytes = (unsigned char *)CVPixelBufferGetBaseAddress(self.imageBuffer); |
| lumaLength = CVPixelBufferGetDataSize(self.imageBuffer); |
| } |
| else { |
| lumaBytes = (unsigned char *)self.yPlane.bytes; |
| lumaLength = self.yPlane.length; |
| } |
| |
| NSMutableData *data = [NSMutableData dataWithLength:lumaLength / kDCSubsampleRatio]; |
| unsigned char *dataBytes = (unsigned char *)[data mutableBytes]; |
| |
| float sum = 0; |
| |
| for(int i = 0; i < data.length; i++) { |
| dataBytes[i] = lumaBytes[i * kDCSubsampleRatio]; |
| sum += dataBytes[i]; |
| } |
| |
| if (self.imageBuffer) { |
| CVPixelBufferUnlockBaseAddress(self.imageBuffer, 0); |
| } |
| |
| return [NSData dataWithData:data]; |
| } |
| |
| + (BOOL)isSceneChangeFromData:(NSData *)data1 toData:(NSData *)data2 { |
| if((data1.length) != (data2.length)) |
| return NO; |
| |
| unsigned char *bytes1 = (unsigned char *)[data1 bytes]; |
| unsigned char *bytes2 = (unsigned char *)[data2 bytes]; |
| |
| float diffSum = 0; |
| |
| for(int i = 0; i < data1.length; i++) { |
| int diff = bytes1[i]-bytes2[i]; |
| diffSum += diff * diff; |
| } |
| |
| BOOL isSceneChange = ((diffSum / data1.length) > kDCSceneChangeThreshold); |
| |
| if(isSceneChange) |
| NLLogNVPInfo(@"scenechange detected, diff = %f", (diffSum / data1.length)); |
| |
| return isSceneChange; |
| } |
| |
| - (void)dealloc { |
| CVBufferRelease(_imageBuffer); |
| } |
| |
| @end |