github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/attachments/preview_darwin.go (about)

     1  //go:build darwin
     2  // +build darwin
     3  
     4  package attachments
     5  
     6  /*
     7  #cgo CFLAGS: -x objective-c -fobjc-arc
     8  #cgo LDFLAGS: -framework AVFoundation -framework CoreFoundation -framework ImageIO -framework CoreMedia  -framework Foundation -framework CoreGraphics -lobjc
     9  
    10  #include <TargetConditionals.h>
    11  #include <AVFoundation/AVFoundation.h>
    12  #include <CoreFoundation/CoreFoundation.h>
    13  #include <Foundation/Foundation.h>
    14  #include <ImageIO/ImageIO.h>
    15  #if TARGET_OS_IPHONE
    16  #include <MobileCoreServices/MobileCoreServices.h>
    17  #endif
    18  
    19  NSData* imageData = NULL;
    20  int duration = 0;
    21  
    22  void MakeVideoThumbnail(const char* inFilename) {
    23  	NSString* filename = [NSString stringWithUTF8String:inFilename];
    24  	NSURL *videoURL = [NSURL fileURLWithPath:filename];
    25  
    26  	AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
    27  	AVAssetImageGenerator *generateImg = [[AVAssetImageGenerator alloc] initWithAsset:asset];
    28  	[generateImg setAppliesPreferredTrackTransform:YES];
    29  	NSError *error = NULL;
    30  	CMTime time = CMTimeMake(1, 1);
    31  	CGImageRef image = [generateImg copyCGImageAtTime:time actualTime:NULL error:&error];
    32  	duration = CMTimeGetSeconds([asset duration]);
    33  
    34  	CFMutableDataRef mutableData = CFDataCreateMutable(NULL, 0);
    35  	CGImageDestinationRef idst = CGImageDestinationCreateWithData(
    36  		mutableData, kUTTypeJPEG, 1, NULL
    37  	);
    38  	NSInteger exif             =    1;
    39  	CGFloat compressionQuality = 0.70;
    40  	NSDictionary *props = [
    41  		[NSDictionary alloc]
    42  		initWithObjectsAndKeys:[NSNumber numberWithFloat:compressionQuality],
    43  		kCGImageDestinationLossyCompressionQuality,
    44  		[NSNumber numberWithInteger:exif],
    45  		kCGImagePropertyOrientation, nil
    46  	];
    47  	CGImageDestinationAddImage(idst, image, (CFDictionaryRef)props);
    48  	CGImageDestinationFinalize(idst);
    49  	imageData = [NSData dataWithData:(__bridge_transfer NSData *)mutableData];
    50  	CFRelease(idst);
    51  	CGImageRelease(image);
    52  }
    53  
    54  const void* ImageData() {
    55  	return [imageData bytes];
    56  }
    57  
    58  int ImageLength() {
    59  	return [imageData length];
    60  }
    61  
    62  int VideoDuration() {
    63  	return duration;
    64  }
    65  */
    66  import "C"
    67  import (
    68  	"bytes"
    69  	"errors"
    70  	"io"
    71  	"unsafe"
    72  
    73  	"github.com/keybase/client/go/chat/types"
    74  	"github.com/keybase/client/go/chat/utils"
    75  	"golang.org/x/net/context"
    76  )
    77  
    78  func previewVideo(ctx context.Context, log utils.DebugLabeler, src io.Reader,
    79  	basename string, nvh types.NativeVideoHelper) (res *PreviewRes, err error) {
    80  	defer log.Trace(ctx, &err, "previewVideo")()
    81  	cbasename := C.CString(basename)
    82  	defer C.free(unsafe.Pointer(cbasename))
    83  	C.MakeVideoThumbnail(cbasename)
    84  	duration := int(C.VideoDuration())
    85  	if duration < 1 {
    86  		// clamp to 1 so we know it is a video, but also not to compute a duration for it
    87  		duration = 1
    88  	} else {
    89  		duration *= 1000
    90  	}
    91  	log.Debug(ctx, "previewVideo: length: %d duration: %ds", C.ImageLength(), duration)
    92  	if C.ImageLength() == 0 {
    93  		return res, errors.New("no data returned from native")
    94  	}
    95  	localDat := make([]byte, C.ImageLength())
    96  	copy(localDat, (*[1 << 30]byte)(C.ImageData())[0:C.ImageLength()])
    97  	imagePreview, err := previewImage(ctx, log, bytes.NewReader(localDat), basename, "image/jpeg")
    98  	if err != nil {
    99  		return res, err
   100  	}
   101  	return &PreviewRes{
   102  		Source:         imagePreview.Source,
   103  		ContentType:    "image/jpeg",
   104  		BaseWidth:      imagePreview.BaseWidth,
   105  		BaseHeight:     imagePreview.BaseHeight,
   106  		BaseDurationMs: duration,
   107  		PreviewHeight:  imagePreview.PreviewHeight,
   108  		PreviewWidth:   imagePreview.PreviewWidth,
   109  	}, nil
   110  }
   111  
   112  func LinkNoop() {}