// // YOUPAIXLVideoShotTool.m // XLChat // // Created by 张灿 on 2018/2/4. // Copyright © 2018年 张灿. All rights reserved. // #import "YOUPAIXLVideoShotTool.h" #import "YOUPAIAGVideoBuffer.h" #import "libyuv.h" @interface YOUPAIXLVideoShotTool () @property (assign, nonatomic) BOOL isPushing,isUpload; @property (strong, nonatomic) YOUPAIAGVideoBuffer *localVideoBuffer; @property (strong, nonatomic) YOUPAIAGVideoBuffer *remoteVideoBuffer; @property (strong, nonatomic) dispatch_source_t videoPublishTimer; @property (strong, nonatomic) dispatch_source_t uploadPublishTimer; @end @implementation YOUPAIXLVideoShotTool + (YOUPAIXLVideoShotTool *)sharedPusher { static YOUPAIXLVideoShotTool *sharedPusher = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedPusher = [[YOUPAIXLVideoShotTool alloc] init]; sharedPusher.shotImageArray = [NSMutableArray array]; sharedPusher.shotImageSem = dispatch_semaphore_create(1); }); return sharedPusher; } + (dispatch_queue_t)sharedQueue { static dispatch_queue_t queue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ queue = dispatch_queue_create("cn.sharesmile.ShotQueue", NULL); }); return queue; } + (void)start { [[self sharedPusher] start]; } + (void)stop { [[self sharedPusher] stop]; } - (void)start { if (self.isPushing) { return; } self.isPushing = YES; self.isUpload =YES; __block int timeout =0; __block int checkTime =0; WeakSelf; // dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,[YOUPAIXLVideoShotTool sharedQueue]); _videoPublishTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,[YOUPAIXLVideoShotTool sharedQueue]); // self.videoPublishTimer = timer; __block NSMutableArray *startArray = [[self.periodArray valueForKeyPath:@"start"] mutableCopy]; __block NSMutableArray *endArray = [[self.periodArray valueForKeyPath:@"end"] mutableCopy]; dispatch_source_set_timer(_videoPublishTimer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行 dispatch_source_set_event_handler(_videoPublishTimer, ^{ // timeout++; //add by leo period 检测周期 shotcycle 截图周期 __strong typeof (weakSelf)strongSelf = weakSelf; #ifdef OLOPornCheck if (strongSelf.period==0) { if (timeout%weakSelf.shotCycle==0 ) { [strongSelf shotImage]; } }else{ if(timeout<2*strongSelf.period) { if(timeout<=strongSelf.period) { if(timeout!=0&&timeout%strongSelf.shotCycle==0) { [strongSelf shotImage]; } } } else if((timeout/strongSelf.period)%2==0) { checkTime = timeout / strongSelf.period; if(timeout!=strongSelf.period*checkTime) { if((timeout%strongSelf.period)%strongSelf.shotCycle==0) { [strongSelf shotImage]; } } } else if(timeout%strongSelf.period==0) { if((strongSelf.period)%strongSelf.shotCycle==0) { [strongSelf shotImage]; } } } #endif if(startArray!=nil&&startArray.count>0) { NSInteger startTime = [[startArray objectAtIndex:0] integerValue]; NSInteger endTime = [[endArray objectAtIndex:0] integerValue]; if(timeout>startTime&&(timeout%startTime)%strongSelf.shotCycle==0&&timeoutendTime) { [startArray removeObjectAtIndex:0]; [endArray removeObjectAtIndex:0]; } } timeout++; }); dispatch_resume(_videoPublishTimer); } - (void)stop { if (!self.isPushing) { return; } dispatch_source_cancel(self.videoPublishTimer); self.videoPublishTimer = nil; // self.uploadPublishTimer = nil; self.isPushing = NO; [self.shotImageArray removeAllObjects]; } #pragma mark add video data + (void)youpaifaddLocalYBuffer:(void *)yBuffer uBuffer:(void *)uBuffer vBuffer:(void *)vBuffer yStride:(int)yStride uStride:(int)uStride vStride:(int)vStride width:(int)width height:(int)height rotation:(int)rotation { [[self sharedPusher] youpaifaddLocalYBuffer:yBuffer uBuffer:uBuffer vBuffer:vBuffer yStride:yStride uStride:uStride vStride:vStride width:width height:height rotation:rotation]; } + (void)youpaifaddRemoteOfUId:(unsigned int)uid yBuffer:(void *)yBuffer uBuffer:(void *)uBuffer vBuffer:(void *)vBuffer yStride:(int)yStride uStride:(int)uStride vStride:(int)vStride width:(int)width height:(int)height rotation:(int)rotation { [[self sharedPusher] youpaifaddRemoteOfUId:uid yBuffer:yBuffer uBuffer:uBuffer vBuffer:vBuffer yStride:yStride uStride:uStride vStride:vStride width:width height:height rotation:rotation]; } - (void)youpaifaddLocalYBuffer:(void *)yBuffer uBuffer:(void *)uBuffer vBuffer:(void *)vBuffer yStride:(int)yStride uStride:(int)uStride vStride:(int)vStride width:(int)width height:(int)height rotation:(int)rotation { YOUPAIAGVideoBuffer *buffer = [[YOUPAIAGVideoBuffer alloc] initWithUId:0 yBuffer:yBuffer uBuffer:uBuffer vBuffer:vBuffer yStride:yStride uStride:uStride vStride:vStride width:width height:height rotation:rotation]; dispatch_async([YOUPAIXLVideoShotTool sharedQueue], ^{ if (self.localVideoBuffer != nil && self.localVideoBuffer.width == width && self.localVideoBuffer.height == height) { [self.localVideoBuffer youpaifupdateWithYBuffer:buffer.yBuffer uBuffer:buffer.uBuffer vBuffer:buffer.vBuffer yStride:buffer.yStride uStride:buffer.uStride vStride:buffer.vStride width:buffer.width height:buffer.height rotation:buffer.rotation]; } else { self.localVideoBuffer = buffer; } }); } - (void)youpaifaddRemoteOfUId:(unsigned int)uid yBuffer:(void *)yBuffer uBuffer:(void *)uBuffer vBuffer:(void *)vBuffer yStride:(int)yStride uStride:(int)uStride vStride:(int)vStride width:(int)width height:(int)height rotation:(int)rotation { // NSLog(@"youpaifaddRemoteOfUId CALLBALCK\n"); return; dispatch_semaphore_wait(self.shotImageSem, DISPATCH_TIME_FOREVER); self.remoteVideoBuffer = [[YOUPAIAGVideoBuffer alloc] initWithUId:0 yBuffer:yBuffer uBuffer:uBuffer vBuffer:vBuffer yStride:yStride uStride:uStride vStride:vStride width:width height:height rotation:rotation]; dispatch_semaphore_signal(self.shotImageSem); // dispatch_async([YOUPAIXLVideoShotTool sharedQueue], ^{ // if (self.remoteVideoBuffer != nil && self.remoteVideoBuffer.width == width && self.remoteVideoBuffer.height == height) { // [self.remoteVideoBuffer youpaifupdateWithYBuffer:buffer.yBuffer uBuffer:buffer.uBuffer vBuffer:buffer.vBuffer yStride:buffer.yStride uStride:buffer.uStride vStride:buffer.vStride width:buffer.width height:buffer.height rotation:buffer.rotation]; // } else { // self.remoteVideoBuffer = buffer; // } // }); } -(void)shotImage { //modify by leo dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [UIImage screenView:self.localVideo]; //UIImage *image= [UIImage imageNamed:@"jianhuang"]; NSLog(@"上传截图"); NSDate * newData = [NSDate date]; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSString* dataString = [dateFormatter stringFromDate:newData]; NSLog(@"截图时间 is %@\n",dataString); if(self->_isUpload) { [self youpaifuploadImage:image]; // [self SavePhotoLibary:image]; } // self.remoteVideoBuffer = nil; }); } #pragma mark - 访问相册 -(void)SavePhotoLibary:(UIImage *)image{ if(@available(iOS 11.0, *)) { [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { if (status == PHAuthorizationStatusNotDetermined || status == PHAuthorizationStatusAuthorized) { [self loadImageFinished:image]; }else { // [ZCHUDHelper showTitle:@"请给予相册保存权限" showtime:2]; return ; } }]; } else { PHAuthorizationStatus authStatus = [PHPhotoLibrary authorizationStatus]; if (authStatus == PHAuthorizationStatusRestricted|| authStatus == PHAuthorizationStatusDenied) { // [ZCHUDHelper showTitle:@"请给予相册保存权限" showtime:2]; return; } else{ [self loadImageFinished:image]; } } } - (void)loadImageFinished:(UIImage *)image { [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ //写入图片到相册 [PHAssetChangeRequest creationRequestForAssetFromImage:image]; } completionHandler:^(BOOL success, NSError * _Nullable error) { NSLog(@"success = %d, error = %@", success, error); if(success){ dispatch_async(dispatch_get_main_queue(), ^{ // [ZCHUDHelper showTitle:@"保存成功" showtime:1]; }); } }]; } - (void)shotImage2{ dispatch_semaphore_wait(self.shotImageSem, DISPATCH_TIME_FOREVER); if(self.remoteVideoBuffer){ //add by leo for test NSLog(@"shotImage called \n"); int retomeYBufferSize = self.remoteVideoBuffer.yStride * self.remoteVideoBuffer.height; int retomeUBufferSize = self.remoteVideoBuffer.uStride * self.remoteVideoBuffer.height / 2; int retomeVBufferSize = self.remoteVideoBuffer.vStride * self.remoteVideoBuffer.height / 2; unsigned char *retomeYBuffer = [YOUPAIAGVideoBuffer copy:self.remoteVideoBuffer.yBuffer size:retomeYBufferSize]; unsigned char *retomeUBuffer = [YOUPAIAGVideoBuffer copy:self.remoteVideoBuffer.uBuffer size:retomeUBufferSize]; unsigned char *retomeVBuffer = [YOUPAIAGVideoBuffer copy:self.remoteVideoBuffer.vBuffer size:retomeVBufferSize]; NSLog(@"retomeUBuffer is %s",retomeUBuffer); //rotate local unsigned char *rotatedretomeYBuffer = malloc(retomeYBufferSize); unsigned char *rotatedretomeUBuffer = malloc(retomeUBufferSize); unsigned char *rotatedretomeVBuffer = malloc(retomeVBufferSize); I420Rotate(retomeYBuffer, self.remoteVideoBuffer.yStride, retomeUBuffer, self.remoteVideoBuffer.uStride, retomeVBuffer, self.remoteVideoBuffer.vStride, rotatedretomeYBuffer, self.remoteVideoBuffer.height, rotatedretomeUBuffer, self.remoteVideoBuffer.height/2, rotatedretomeVBuffer, self.remoteVideoBuffer.height/2, self.remoteVideoBuffer.height, self.remoteVideoBuffer.width, self.remoteVideoBuffer.rotation); free(retomeYBuffer); free(retomeUBuffer); free(retomeVBuffer); int dataLength = retomeYBufferSize + retomeUBufferSize + retomeVBufferSize; unsigned char *youpaifyuvData = malloc(dataLength); memcpy(youpaifyuvData, rotatedretomeYBuffer, retomeYBufferSize); memcpy(youpaifyuvData + retomeYBufferSize, rotatedretomeUBuffer, retomeUBufferSize); memcpy(youpaifyuvData + retomeYBufferSize + retomeUBufferSize, rotatedretomeVBuffer, retomeVBufferSize); UIImage* image =[self makeUIImage:self.remoteVideoBuffer]; [self youpaifuploadImage:image]; free(youpaifyuvData); free(rotatedretomeYBuffer); free(rotatedretomeUBuffer); free(rotatedretomeVBuffer); self.remoteVideoBuffer = nil; } dispatch_semaphore_signal(self.shotImageSem); } - (UIImage *)makeUIImage:(YOUPAIAGVideoBuffer*)videoBuffer { unsigned char* argb = (unsigned char*)malloc(videoBuffer.width * videoBuffer.height * 4 * sizeof(unsigned char)); I420ToARGB(videoBuffer.yBuffer, videoBuffer.yStride, videoBuffer.uBuffer, videoBuffer.uStride, videoBuffer.vBuffer, videoBuffer.vStride, argb, 4*videoBuffer.width, videoBuffer.width, videoBuffer.height); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(argb, videoBuffer.width, videoBuffer.height, 8, videoBuffer.width * 4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); CGImageRef quartzImage = CGBitmapContextCreateImage(context); CGContextRelease(context); CGColorSpaceRelease(colorSpace); UIImage *image = [UIImage imageWithCGImage:quartzImage]; CGImageRelease(quartzImage); free(argb); argb = NULL; return image; } - (UIImage *)makeUIImage:(unsigned char *)inBaseAddress width:(int)inWidth height:(int)inHeight { int scale = 1; unsigned char* y0 = inBaseAddress; unsigned char* uv0 = inBaseAddress + inWidth * inHeight * sizeof(unsigned char); // int outWidth = inWidth / scale; // int outHeight = inHeight / scale; inWidth = (inWidth >> 2) << 2; inHeight = (inHeight >> 2) << 2; align_buffer_64(sdata, inWidth * inHeight * 1.5 * sizeof(unsigned char)); unsigned char* y1 = sdata; unsigned char* uv1 = sdata + inWidth * inHeight; memset(uv1, 128, 0.5 * inWidth * inHeight); ScalePlane_16((uint16*)uv0, inWidth / 2, inWidth / 2, inHeight / 2, (uint16*)uv1, inWidth / 2, inWidth / 2, inHeight / 2, kFilterNone); ScalePlane(y0, inWidth, inWidth, inHeight, y1, inWidth, inWidth, inHeight, kFilterNone); unsigned char* argb = (unsigned char*)malloc(inWidth * inHeight * 4 * sizeof(unsigned char)); NV12ToARGB(y1, inWidth, uv1, inWidth, argb, inWidth * 4, inWidth, inHeight); // int NV12ToARGB(const uint8* src_y, int src_stride_y, // const uint8* src_uv, int src_stride_uv, // uint8* dst_argb, int dst_stride_argb, // int width, int height); // int I420ToARGB(const uint8* src_y, int src_stride_y, // const uint8* src_u, int src_stride_u, // const uint8* src_v, int src_stride_v, // uint8* dst_argb, int dst_stride_argb, // int width, int height); free_aligned_buffer_64(sdata); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(argb, inWidth, inHeight, 8, inWidth * 4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); CGImageRef quartzImage = CGBitmapContextCreateImage(context); CGContextRelease(context); CGColorSpaceRelease(colorSpace); UIImage *image = [UIImage imageWithCGImage:quartzImage]; CGImageRelease(quartzImage); free(argb); argb = NULL; return image; } - (void)youpaifuploadImage:(UIImage*)image{ NSData *imageData = [UIImage zipNSDataWithImage:image maxFileSize:20*1024]; NSString *encodedImageStr = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; NSMutableDictionary* parm = [NSMutableDictionary dictionary]; [parm setObject:self.room_id forKey:@"room_id"]; [parm setObject:encodedImageStr forKey:@"from"]; //[parm setObject:encodedImageStr forKey:@"to"]; if (self.isFrom) { // 主叫 [parm setObject:self.uid forKey:@"uid"]; }else{ // 被叫 [parm setObject:self.uid forKey:@"uid"]; } WeakSelf; [LCHttpHelper requestWithURLString:VideoPornCheck parameters:parm needToken:YES type:(HttpRequestTypePost) success:^(id responseObject) { NSLog(@"输出🍀VideoPornCheck\n%@",responseObject); NSDictionary* dict = (NSDictionary*)responseObject; NSInteger code = [[dict objectForKey:@"code"] integerValue]; if (code==0) { if ([dict[@"data"][@"is_upload"] integerValue] == 0) { weakSelf.isUpload = NO; } } } failure:^(NSError *error) { NSLog(@"输出🍀VideoPornCheck\n%@",error); }]; } @end