Flutter image_picker 选择视频

Posted

技术标签:

【中文标题】Flutter image_picker 选择视频【英文标题】:Flutter image_picker choose video 【发布时间】:2017-11-22 12:14:33 【问题描述】:

我已成功使用 Flutter 插件 Image_picker 选择图像,以便我可以将它们用于上传、显示等...我想知道是否有人对如何修改此插件以也查看视频并允许他们有任何指导被选择并用于上传等......

如果有人对如何继续或示例代码有指导,请寻找 iosandroid 修改。我已经取得了一些进展,但仍然需要让相机保存视频并能够呈现。到目前为止,我将发布代码更改。我让它选择一个视频,但它不会显示回应用程序。

// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

@import UIKit;
#import <MobileCoreServices/MobileCoreServices.h>

#import "ImagePickerPlugin.h"

@interface ImagePickerPlugin ()<UINavigationControllerDelegate, UIImagePickerControllerDelegate>
@end

static const int SOURCE_ASK_USER = 0;
static const int SOURCE_CAMERA = 1;
static const int SOURCE_GALLERY = 2;

@implementation ImagePickerPlugin 
  FlutterResult _result;
  NSDictionary *_arguments;
  UIImagePickerController *_imagePickerController;
  UIViewController *_viewController;


+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar 
  FlutterMethodChannel *channel =
      [FlutterMethodChannel methodChannelWithName:@"image_picker"
                                  binaryMessenger:[registrar messenger]];
  UIViewController *viewController =
      [UIApplication sharedApplication].delegate.window.rootViewController;
  ImagePickerPlugin *instance = [[ImagePickerPlugin alloc] initWithViewController:viewController];
  [registrar addMethodCallDelegate:instance channel:channel];


- (instancetype)initWithViewController:(UIViewController *)viewController 
  self = [super init];
  if (self) 
    _viewController = viewController;
    _imagePickerController = [[UIImagePickerController alloc] init];
  
  return self;


- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result 
  if (_result) 
    _result([FlutterError errorWithCode:@"multiple_request"
                                message:@"Cancelled by a second request"
                                details:nil]);
    _result = nil;
  

  if ([@"pickImage" isEqualToString:call.method]) 
    _imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
    _imagePickerController.delegate = self;

    _result = result;
    _arguments = call.arguments;

    int imageSource = [[_arguments objectForKey:@"source"] intValue];

    switch (imageSource) 
      case SOURCE_ASK_USER:
        [self showImageSourceSelector];
        break;
      case SOURCE_CAMERA:
        [self showCamera];
        break;
      case SOURCE_GALLERY:
        [self showPhotoLibrary];
        break;
      default:
        result([FlutterError errorWithCode:@"invalid_source"
                                   message:@"Invalid image source."
                                   details:nil]);
        break;
    
   else 
    result(FlutterMethodNotImplemented);
  


- (void)showImageSourceSelector 
  UIAlertControllerStyle style = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad
                                     ? UIAlertControllerStyleAlert
                                     : UIAlertControllerStyleActionSheet;

  UIAlertController *alert =
      [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:style];
  UIAlertAction *camera = [UIAlertAction actionWithTitle:@"Take Photo"
                                                   style:UIAlertActionStyleDefault
                                                 handler:^(UIAlertAction *action) 
                                                   [self showCamera];
                                                 ];
  UIAlertAction *library = [UIAlertAction actionWithTitle:@"Choose Photo"
                                                    style:UIAlertActionStyleDefault
                                                  handler:^(UIAlertAction *action) 
                                                    [self showPhotoLibrary];
                                                  ];


  UIAlertAction *cancel =
      [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
  [alert addAction:camera];
  [alert addAction:library];
  [alert addAction:cancel];
  [_viewController presentViewController:alert animated:YES completion:nil];


- (void)showCamera 
  // Camera is not available on simulators
  if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) 
    _imagePickerController.sourceType = UIImagePickerControllerCameraCaptureModeVideo;
    [_viewController presentViewController:_imagePickerController animated:YES completion:nil];
   else 
    [[[UIAlertView alloc] initWithTitle:@"Error"
                                message:@"Camera not available."
                               delegate:nil
                      cancelButtonTitle:@"OK"
                      otherButtonTitles:nil] show];
  


- (void)showPhotoLibrary 
  // No need to check if SourceType is available. It always is.
  //_imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    _imagePickerController.mediaTypes =[UIImagePickerController availableMediaTypesForSourceType:_imagePickerController.sourceType];
  [_viewController presentViewController:_imagePickerController animated:YES completion:nil];


- (void)imagePickerController:(UIImagePickerController *)picker
    didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info 
  [_imagePickerController dismissViewControllerAnimated:YES completion:nil];
  UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
  NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
  if (image == nil) 
    image = [info objectForKey:UIImagePickerControllerOriginalImage];
   else 
      image = [self normalizedImage:image];
  
  if (videoURL == nil) 

   else 
      //image = videoURL;
  


  NSNumber *maxWidth = [_arguments objectForKey:@"maxWidth"];
  NSNumber *maxHeight = [_arguments objectForKey:@"maxHeight"];

  if (maxWidth != (id)[NSNull null] || maxHeight != (id)[NSNull null]) 
    image = [self scaledImage:image maxWidth:maxWidth maxHeight:maxHeight];
  

  NSData *data = UIImageJPEGRepresentation(image, 1.0);
  NSString *tmpDirectory = NSTemporaryDirectory();
  NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
  // TODO(jackson): Using the cache directory might be better than temporary
  // directory.
  NSString *tmpFile = [NSString stringWithFormat:@"image_picker_%@.jpg", guid];
  NSString *tmpPath = [tmpDirectory stringByAppendingPathComponent:tmpFile];
  if ([[NSFileManager defaultManager] createFileAtPath:tmpPath contents:data attributes:nil]) 
    _result(tmpPath);
   else 
    _result([FlutterError errorWithCode:@"create_error"
                                message:@"Temporary file could not be created"
                                details:nil]);
  
  _result = nil;
  _arguments = nil;


// The way we save images to the tmp dir currently throws away all EXIF data
// (including the orientation of the image). That means, pics taken in portrait
// will not be orientated correctly as is. To avoid that, we rotate the actual
// image data.
// TODO(goderbauer): investigate how to preserve EXIF data.
- (UIImage *)normalizedImage:(UIImage *)image 
  if (image.imageOrientation == UIImageOrientationUp) return image;

  UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
  [image drawInRect:(CGRect)0, 0, image.size];
  UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return normalizedImage;



- (UIImage *)scaledImage:(UIImage *)image
                maxWidth:(NSNumber *)maxWidth
               maxHeight:(NSNumber *)maxHeight 
  double originalWidth = image.size.width;
  double originalHeight = image.size.height;

  bool hasMaxWidth = maxWidth != (id)[NSNull null];
  bool hasMaxHeight = maxHeight != (id)[NSNull null];

  double width = hasMaxWidth ? MIN([maxWidth doubleValue], originalWidth) : originalWidth;
  double height = hasMaxHeight ? MIN([maxHeight doubleValue], originalHeight) : originalHeight;

  bool shouldDownscaleWidth = hasMaxWidth && [maxWidth doubleValue] < originalWidth;
  bool shouldDownscaleHeight = hasMaxHeight && [maxHeight doubleValue] < originalHeight;
  bool shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight;

  if (shouldDownscale) 
    double downscaledWidth = (height / originalHeight) * originalWidth;
    double downscaledHeight = (width / originalWidth) * originalHeight;

    if (width < height) 
      if (!hasMaxWidth) 
        width = downscaledWidth;
       else 
        height = downscaledHeight;
      
     else if (height < width) 
      if (!hasMaxHeight) 
        height = downscaledHeight;
       else 
        width = downscaledWidth;
      
     else 
      if (originalWidth < originalHeight) 
        width = downscaledWidth;
       else if (originalHeight < originalWidth) 
        height = downscaledHeight;
      
    
  

  UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 1.0);
  [image drawInRect:CGRectMake(0, 0, width, height)];

  UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();

  return scaledImage;


@end

谢谢

【问题讨论】:

【参考方案1】:

这是我已经完成的IOS代码,如果有人想提供帮助,我还在Android上工作,我会发布我目前的位置。此代码替换了 IOS 文件夹的 .m 文件中的内容,无需进行其他更改即可拾取和捕获视频以及图像。您必须弄清楚如何在您的应用程序中显示选定的视频/图像,但这就是您想要处理的方式。如果您想协助完成他的 Android 方面,请再次告诉我。

@import UIKit;
#import <MobileCoreServices/MobileCoreServices.h>

#import "MediaPickerPlugin.h"

@interface MediaPickerPlugin ()<UINavigationControllerDelegate, UIImagePickerControllerDelegate>
@end

static const int SOURCE_ASK_USER = 0;
//static const int SOURCE_CAMERA = 0;
//static const int SOURCE_GALLERY = 0;

@implementation MediaPickerPlugin 
    FlutterResult _result;
    NSDictionary *_arguments;
    UIImagePickerController *_imagePickerController;
    UIViewController *_viewController;


+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar 
    FlutterMethodChannel *channel =
    [FlutterMethodChannel methodChannelWithName:@"media_picker"
                                binaryMessenger:[registrar messenger]];
    UIViewController *viewController =
        [UIApplication sharedApplication].delegate.window.rootViewController;
    MediaPickerPlugin *instance =
        [[MediaPickerPlugin alloc] initWithViewController:viewController];
    [registrar addMethodCallDelegate:instance channel:channel];


- (instancetype)initWithViewController:(UIViewController *)viewController 
    self = [super init];
    if (self) 
      _viewController = viewController;
      _imagePickerController = [[UIImagePickerController alloc] init];
    
    return self;


- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result 
  if (_result) 
    _result([FlutterError errorWithCode:@"multiple_request"
                                message:@"Cancelled by a second request"
                                details:nil]);
    _result = nil;
      _arguments = nil;


if ([@"pickImage" isEqualToString:call.method]) 
  _imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
  _imagePickerController.delegate = self;

  _result = result;
  _arguments = call.arguments;

  int imageSource = [[_arguments objectForKey:@"source"] intValue];

    switch (imageSource) 
        case SOURCE_ASK_USER:
            [self showImageSourceSelector];
            break;
        default:
            result([FlutterError errorWithCode:@"invalid_source"
                                       message:@"Invalid image source."
                                       details:nil]);
            break;
        
     else 
      result(FlutterMethodNotImplemented);
    


- (void)showImageSourceSelector 
    UIAlertControllerStyle style = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad
                                        ? UIAlertControllerStyleAlert
                                        : UIAlertControllerStyleActionSheet;

    UIAlertController *alert =
        [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:style];
    UIAlertAction *camera = [UIAlertAction actionWithTitle:@"Camera"
                                                     style:UIAlertActionStyleDefault
                                                   handler:^(UIAlertAction *action) 
                                                       [self showCamera];
                                                   ];
    UIAlertAction *library = [UIAlertAction actionWithTitle:@"Gallery"
                                                      style:UIAlertActionStyleDefault
                                                    handler:^(UIAlertAction *action) 
                                                        [self showPhotoLibrary];
                                                    ];
    UIAlertAction *cancel =
    [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
    [alert addAction:camera];
    [alert addAction:library];
    [alert addAction:cancel];
    [_viewController presentViewController:alert animated:YES completion:nil];


- (void)showCamera 
    // Camera is not available on simulators
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) 
        _imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
        _imagePickerController.mediaTypes = [NSArray arrayWithObjects:@"public.movie", @"public.image", nil];
        _imagePickerController.delegate = self;
        _imagePickerController.restoresFocusAfterTransition = false;
        _imagePickerController.allowsEditing = NO;
        _imagePickerController.videoQuality = UIImagePickerControllerQualityTypeLow;
        _imagePickerController.videoMaximumDuration = 30.0f; // 30 seconds
        [_viewController presentViewController:_imagePickerController animated:YES completion:nil];
     else 
        [[[UIAlertView alloc] initWithTitle:@"Error"
                                    message:@"Camera not available."
                                   delegate:nil
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    


- (void)showPhotoLibrary 
// No need to check if SourceType is available. It always is.
_imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    _imagePickerController.mediaTypes = [NSArray arrayWithObjects:@"public.movie", @"public.image", nil];
//_imagePickerController.mediaTypes =[UIImagePickerController availableMediaTypesForSourceType:_imagePickerController.sourceType];
[_viewController presentViewController:_imagePickerController animated:YES completion:nil];


- (void)imagePickerController:(UIImagePickerController *)picker
      didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info 
  [_imagePickerController dismissViewControllerAnimated:YES completion:nil];
  NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
  if ([mediaType isEqualToString:@"public.movie"]) 
    NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
    NSString *videoString = [videoURL absoluteString];
    NSLog(@"Video File:%@", videoString);
    _result(videoString);
   else 
    UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
    if (image == nil) 
      image = [info objectForKey:UIImagePickerControllerOriginalImage];
    
    image = [self normalizedImage:image];

    NSNumber *maxWidth = [_arguments objectForKey:@"maxWidth"];
    NSNumber *maxHeight = [_arguments objectForKey:@"maxHeight"];

    if (maxWidth != (id)[NSNull null] || maxHeight != (id)[NSNull null]) 
      image = [self scaledImage:image maxWidth:maxWidth maxHeight:maxHeight];
    

    NSData *data = UIImageJPEGRepresentation(image, 1.0);
    NSString *tmpDirectory = NSTemporaryDirectory();
    NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
    // TODO(jackson): Using the cache directory might be better than temporary
    // directory.
    NSString *tmpFile = [NSString stringWithFormat:@"image_picker_%@.jpg", guid];
    NSString *tmpPath = [tmpDirectory stringByAppendingPathComponent:tmpFile];
    NSLog(@"Image File:%@", tmpPath);
    if ([[NSFileManager defaultManager] createFileAtPath:tmpPath contents:data attributes:nil]) 
        _result(tmpPath);
     else 
        _result([FlutterError errorWithCode:@"create_error"
                                    message:@"Temporary file could not be created"
                                    details:nil]);
    
      _result = nil;
      _arguments = nil;
  
  _result = nil;
  _arguments = nil;


// The way we save images to the tmp dir currently throws away all EXIF data
    // (including the orientation of the image). That means, pics taken in portrait
    // will not be orientated correctly as is. To avoid that, we rotate the actual
    // image data.
// TODO(goderbauer): investigate how to preserve EXIF data.
- (UIImage *)normalizedImage:(UIImage *)image 
    if (image.imageOrientation == UIImageOrientationUp) return image;

    UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
    [image drawInRect:(CGRect)0, 0, image.size];
    UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return normalizedImage;

//- (NSString *)normalVideo:(NSURL *)videoURL 
  //    NSString *normalVideo = UIImagePickerControllerMediaURL;
  //    return normalVideo;
//

- (UIImage *)scaledImage:(UIImage *)image
    maxWidth:(NSNumber *)maxWidth
    maxHeight:(NSNumber *)maxHeight 
    double originalWidth = image.size.width;
    double originalHeight = image.size.height;

    bool hasMaxWidth = maxWidth != (id)[NSNull null];
    bool hasMaxHeight = maxHeight != (id)[NSNull null];

    double width = hasMaxWidth ? MIN([maxWidth doubleValue], originalWidth) : originalWidth;
    double height = hasMaxHeight ? MIN([maxHeight doubleValue], originalHeight) : originalHeight;

    bool shouldDownscaleWidth = hasMaxWidth && [maxWidth doubleValue] < originalWidth;
    bool shouldDownscaleHeight = hasMaxHeight && [maxHeight doubleValue] < originalHeight;
    bool shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight;

    if (shouldDownscale) 
          double downscaledWidth = (height / originalHeight) * originalWidth;
          double downscaledHeight = (width / originalWidth) * originalHeight;

          if (width < height) 
            if (!hasMaxWidth) 
              width = downscaledWidth;
             else 
              height = downscaledHeight;
            
           else if (height < width) 
            if (!hasMaxHeight) 
              height = downscaledHeight;
             else 
              width = downscaledWidth;
            
           else 
            if (originalWidth < originalHeight) 
              width = downscaledWidth;
             else if (originalHeight < originalWidth) 
              height = downscaledHeight;
            
          
    

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 1.0);
    [image drawInRect:CGRectMake(0, 0, width, height)];

    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return scaledImage;


@end

【讨论】:

【参考方案2】:

我可能会在image_picker.dart 中添加另一个方法pickVideo,然后在imagePickerPlugin.mImagePickerPlugin.java 中添加相应的Android 和iOS 实现。

在后两者中,我会使用 iOS 和 Android API 来处理视频,例如在 iOS 上是这样的: Objective c - ios : How to pick video from Camera Roll?

【讨论】:

有任何代码示例或者你之前为这个插件做过吗? 任何人都有一个代码示例,我试图看看代码如何适合当前的插件,但现在不太精通Objective c。我知道它是如何工作的,但不知道如何修改当前代码来进行更改。任何帮助将不胜感激。 我有它选择视频,但无法弄清楚如何让它呈现回插件,当我选择它时它崩溃或挂起,任何想法或有人做过,需要也可以在相机上捕捉视频。 我添加了一些我一直在处理的代码,但仍在寻求帮助。 @Slabo 我刚刚发布了我完成的 ios 代码,android 也快完成了,我可能会在完成后做一个 PR,或者制作一个名为 media_picker 的新的并作为一个包发布。【参考方案3】:

从 0.4.2 版本开始,该插件允许选择视频

添加了对挑选视频的支持。更新了示例应用以显示视频 预览。

【讨论】:

【参考方案4】:

您现在可以使用 image_picker 中的 pickVideo 来做到这一点

final _picker = ImagePicker();
PickedFile video = await _picker.getVideo(...)
...

参考 - https://pub.dev/packages/image_picker

【讨论】:

【参考方案5】:

您可以使用图像选择器来录制视频,并使用咀嚼库通过视频控制器显示视频。 如需更多参考,请使用此视频链接 - https://www.youtube.com/watch?time_continue=17&v=XSn5EwWBG-4&feature=emb_logo

【讨论】:

以上是关于Flutter image_picker 选择视频的主要内容,如果未能解决你的问题,请参考以下文章

Flutter从相册选择图片和相机拍照(image_picker)

如何在Flutter中保存图像文件?使用Image_picker插件选择文件

flutter 图片上传到oss,直接cv就可以使用

Flutter image_picker踩坑记录

android 应用中的 image_picker 崩溃 - Flutter App

Flutter 图像选择器不会出现在 Android 10 中