使用系统界面拍摄视频及系统问题

目前,将视频捕获集成到你的应用中的最简单的方法是使用 UIImagePickerController。这是一个封装了完整视频捕获管线和相机 UI 的 view controller。用UIImagePickerController可以自定义系统提供的界面来拍摄并保存视频和照片。

UIImagePickerController 里有几个比较重要的属性.
  • ** Source type** :
    这个参数是用来确定是调用摄像头还是调用图片库.
    UIImagePickerControllerSourceTypeCamera : 提供调用摄像头的界面来拍摄照片视频。
    UIImagePickerControllerSourceTypePhotoLibrary 调用图片库作为UIImagePickerController数据源。
    UIImagePickerControllerSourceTypeSavedPhotosAlbum iOS设备中的胶卷相机的图片.作为UIImagePickerController数据源

  • Mediatypes:
    NSArray数组。拍摄时:这个属性决定UIImagePickerController界面模式是拍照还是录像.查看已保存多媒体数据,可决定展示的是照片还是视频。
    kUTTypeImage 表示静态图片, kUTTypeMovie表示录像.把空数组传给这个属性会报异常。在设置此属性前,需要用availableMediaTypesForSourceType:类方法判断这种多媒体类型是否被支持。

  • **Editing controls **:
    用来指定是否可编辑.将allowsEditing 属性设置为YES表示可编辑,NO表示不可编辑.

  • delegate
    id <UINavigationControllerDelegate, UIImagePickerControllerDelegate> delegate:
    使用 UIImagePickerController,必须提供遵守UIImagePickerControllerDelegate和UINavigationControllerDelegate协议
    的delegate对象.UIImagePickerControllerDelegate提供了两个方法来在照相完成后,与UIImagePickerController对象界面进行交互。
    imagePickerController:didFinishPickingMediaWithInfo: delegate使用拍摄的照片或者视频的处理逻辑.
    imagePickerControllerDidCancel(_:) 取消picker view。

步骤

1.首先要检查设备是否支持相机录制, 否则设备不支持相机时,会crash 。

   if ([UIImagePickerController
         isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] == NO) return;
    UIImagePickerController *camera = [[UIImagePickerController alloc] init];
    camera.sourceType = UIImagePickerControllerSourceTypeCamera;

2.检查在 source type中支持的多媒体类型,并设置mediatypes

camera.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];

如果只想让摄像头只能摄像,而不能拍照,那应该设置mediaTypes
cameraUI.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeMovie , nil];
kUTTypeImage 对应拍照
kUTTypeMovie 对应摄像
这要导入MobileCoreServices.framework,然后再
#import <MobileCoreServices/UTCoreTypes.h>

3.设置delegate,定义拍摄完成后的交互事件处理

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
    NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
    UIAlertView *alertView =  [[UIAlertView alloc] initWithTitle:@"选择视频" message:nil delegate:self cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
    [alertView show];
    NSLog(@"didFinishPickingMediaWithInfo info=%@;type = %@", info, mediaType);
}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    NSLog(@"imagePickerControllerDidCancel");
    
    [picker dismissViewControllerAnimated:YES completion:nil];
}

完整代码:

ViewController.m

#import <MobileCoreServices/UTCoreTypes.h>
#import <MediaPlayer/MediaPlayer.h>
#import <AVFoundation/AVFoundation.h>

#import "ViewController.h"

@interface ViewController () <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIAlertViewDelegate>

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithTitle:@"拍摄" style:UIBarButtonItemStylePlain target:self action:@selector(startCameraControllerFromViewController:)];
    self.navigationItem.rightBarButtonItem = rightItem;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
    NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
    UIAlertView *alertView =  [[UIAlertView alloc] initWithTitle:@"选择视频" message:nil delegate:self cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
    [alertView show];
    NSLog(@"didFinishPickingMediaWithInfo info=%@;type = %@", info, mediaType);
}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    NSLog(@"imagePickerControllerDidCancel");
    
    [picker dismissViewControllerAnimated:YES completion:nil];
}

// 设备是否被授权访问相机
-(BOOL)isCameraAuthorized
{
    AVAuthorizationStatus auth = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    return (auth == AVAuthorizationStatusAuthorized);
}

// 请访问求相机授权
- (void)requestCameraAuthorization
{
    NSString *title = @"请在\设置-隐私-相机\"选项中,允许访问你的相机。";
    AVAuthorizationStatus auth = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (auth == AVAuthorizationStatusNotDetermined) {
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
            dispatch_async(dispatch_get_main_queue(), ^{
                UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title message:nil delegate:self cancelButtonTitle:@"好" otherButtonTitles:nil];
                [alertView show];
            });
        }];
    }
    else {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title message:nil delegate:self cancelButtonTitle:@"好" otherButtonTitles:nil];
        [alertView show];
    }
}

- (void)startCameraControllerFromViewController: (UIViewController*)controller
{
    if (![self isCameraAuthorized]) {
        [self requestCameraAuthorization];
    }
    
    if ([UIImagePickerController
         isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] == NO) return;
    
    UIImagePickerController *camera = [[UIImagePickerController alloc] init];
    camera.sourceType = UIImagePickerControllerSourceTypeCamera;
    camera.delegate = self;
    
    camera.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
    
    [self presentViewController:camera animated:YES completion:nil];

}
@end

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.viewController = [[ViewController alloc] init];
    self.navigationController = [[UINavigationController alloc] init];
    [self.navigationController pushViewController:self.viewController animated:YES];
    self.window.rootViewController = self.navigationController;
    return YES;
}
bug:

横向拍摄视频,完成拍摄后点击“使用视频”,摄像头被打开,背景出现摄像流。

bug.png

设备和系统
iPhone6Plus 9.1 ,上述情况
iPhone5s 8.3 , 上述情况
iPhone4s 7.1.1, 上述情况,但是显示是已拍摄视频的最后一帧。

问题原因:
调用系统摄像机录像时,UIImagePickerController视图有两个关键子view,CMKVideoPreviewViewPLAVPlayerView
PLAVPlayerView 用来播放已录制的视频。
CMKVideoPreviewView 负责展示摄像头实时拍摄的视频流。 PreviewView在不同系统版本有不同的名字:

  • 9.1系统的类名是CMKVideoPreviewView;
  • 8.3系统是CAMVideoPreviewView;
  • 7.1系统是 PLVideoPreviewView)

视图层次结构:CMKVideoPreviewView在下,PLAVPlayerView 在上。横屏拍摄视频,完成拍摄后,由于UIImagePickerController官方只支持竖屏模式,系统自动转为竖屏模式并更新PLVideoPosterFrameView的UIImageView(已录制视频的快照)的frame为 (0, 212,75, 414, 310,5),所以原本被遮挡的CMKVideoPreviewView就显示出来了。

Paste_Image.png

解决方案:
思路:在点击“使用视频”之后,弹出“压缩”toast提示之前,获取CMKVideoPreviewView对象的指针,并讲该对象的hidden属性设为YES.由于CMKVideoPreviewView是private frameworks中的类,无法直接获取对象,但可通过public api : view.subviews ,在子view中递归查找目标对象,安全地获取到该对象。此方法可以通过appstore的审查。 如何安全使用私有库类

- (void)setVideoPreviewHidden:(BOOL)hidden inPicker:(UIImagePickerController *)picker
{
    UIView *view = nil;
    float systemVersion = UIDevice.currentDevice.systemVersion.floatValue;
    if (systemVersion>= 10.0) {
        return;
    }
    else if (systemVersion>= 9.0) {
        view = UIViewFindSubview(picker.view, [NSClassFromString(@"CMKVideoPreviewView") class]);
    }
    else if (systemVersion >= 8.0) {
        view = UIViewFindSubview(picker.view, [NSClassFromString(@"CAMVideoPreviewView") class]);
    }
    else if (systemVersion >= 7.0) {
        view = UIViewFindSubview(picker.view, [NSClassFromString(@"PLVideoPreviewView") class]);
    }
    view.hidden = hidden;
}

NS_INLINE UIView *UIViewFindSubview(UIView *view, Class viewClass)
{
 for (UIView *subview in view.subviews) {
  if ([subview isKindOfClass:viewClass])  {
   return subview;
  } else {
   UIView *ret = UIViewFindSubview(subview, viewClass);
   if (ret) return ret;
  }
 }
 
 return nil;
}

Taking Pictures and Movies

ios设备上捕获视频

文档翻译

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容

  • 原文链接http://www.cnblogs.com/kenshincui/p/4186022.html 音频在i...
    Hyman0819阅读 21,668评论 4 74
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,024评论 4 62
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,510评论 25 707
  • 我用了一个月的时候,把每天晚上的梦,用一首诗记录下来。 于是有了这本诗集。 梦中的门 这是最不真实的梦境 还没醒 ...
    安和然阅读 440评论 2 9
  • 有的人说你看你这么年轻,只要你努力,总会好的,我知道成功没有捷径可走,只能靠日积月累的辛苦。可是能不能让我看到希望...
    逆光blacklight阅读 233评论 0 0