OC录制小视频

用 AVCaptureSession + AVCaptureMovieFileOutput 来录制视频,并通过AVAssetExportSeeion 手段来压缩视频并转换为 MP4 格

AVFoundation 介绍

AVCaptureSession

  1. AVCaptureSession:媒体(音、视频)捕获会话,负责把捕获的音视频数据输出到输出设备中。一个AVCaptureSession可以有多个输入输出。
  2. AVCaptureDevice :输入设备,包括麦克风、摄像头,通过该对象可以设置物理设备的一些属性(例如相机聚焦、白平衡等)。
  3. AVCaptureDeviceInput :设备输入数据管理对象,可以根据AVCaptureDevice创建对应的AVCaptureDeviceInput对象,该对象将会被添加到AVCaptureSession中管理。
  4. AVCaptureVideoPreviewLayer :相机拍摄预览图层,是CALayer的子类,使用该对象可以实时查看拍照或视频录制效果,创建该对象需要指定对应的 AVCaptureSession对象。
  5.  
  6. AVCaptureOutput :输出数据管理对象,用于接收各类输出数据,通常使用对应的子类AVCaptureAudioDataOutputAVCaptureStillImageOutput
  7. AVCaptureVideoDataOutputAVCaptureFileOutput, 该对象将会被添加到AVCaptureSession中管理。
  8. 注意:前面几个对象的输出数据都是NSData类型,而AVCaptureFileOutput代表数据以文件形式输出,类似的,AVCcaptureFileOutput也不会直接创建使用,通常会使用其子类:
  9. AVCaptureAudioFileOutputAVCaptureMovieFileOutput。当把一个输入或者输出添加到AVCaptureSession之后AVCaptureSession就会在所有相符的输入、输出设备之间
  10. 建立连接(AVCaptionConnection)。

那么建立视频拍摄的步骤如下 :
1.创建AVCaptureSession对象。

  1. // 创建会话 (AVCaptureSession) 对象。
  2. _captureSession = [[AVCaptureSession alloc] init];
  3. if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) {
  4. // 设置会话的 sessionPreset 属性, 这个属性影响视频的分辨率
  5. [_captureSession setSessionPreset:AVCaptureSessionPreset640x480];
  6. }

2.使用AVCaptureDevice的静态方法获得需要使用的设备,例如拍照和录像就需要获得摄像头设备,录音就要获得麦克风设备。

  1. // 获取摄像头输入设备, 创建 AVCaptureDeviceInput 对象
  2. // 在获取摄像头的时候,摄像头分为前后摄像头,我们创建了一个方法通过用摄像头的位置来获取摄像头
  3. AVCaptureDevice *videoCaptureDevice = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];
  4. if (!captureDevice) {
  5. NSLog(@"---- 取得后置摄像头时出现问题---- ");
  6. return;
  7. }
  8.  
  9. // 添加一个音频输入设备
  10. // 直接可以拿数组中的数组中的第一个
  11. AVCaptureDevice *audioCaptureDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];

3.利用输入设备AVCaptureDevice初始化AVCaptureDeviceInput对象。

  1. // 视频输入对象
  2. // 根据输入设备初始化输入对象,用户获取输入数据
  3. _videoCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];
  4. if (error) {
  5. NSLog(@"---- 取得设备输入对象时出错 ------ %@",error);
  6. return;
  7. }
  8.  
  9. // 音频输入对象
  10. //根据输入设备初始化设备输入对象,用于获得输入数据
  11. _audioCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioCaptureDevice error:&error];
  12. if (error) {
  13. NSLog(@"取得设备输入对象时出错 ------ %@",error);
  14. return;
  15. }

4.初始化输出数据管理对象,如果要拍照就初始化AVCaptureStillImageOutput对象;如果拍摄视频就初始化AVCaptureMovieFileOutput对象。

  1. // 拍摄视频输出对象
  2. // 初始化输出设备对象,用户获取输出数据
  3. _caputureMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];

5.将数据输入对象AVCaptureDeviceInput、数据输出对象AVCaptureOutput添加到媒体会话管理对象AVCaptureSession中。

  1. // 将视频输入对象添加到会话 (AVCaptureSession) 中
  2. if ([_captureSession canAddInput:_videoCaptureDeviceInput]) {
  3. [_captureSession addInput:_videoCaptureDeviceInput];
  4. }
  5.  
  6. // 将音频输入对象添加到会话 (AVCaptureSession) 中
  7. if ([_captureSession canAddInput:_captureDeviceInput]) {
  8. [_captureSession addInput:audioCaptureDeviceInput];
  9. AVCaptureConnection *captureConnection = [_caputureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
  10. // 标识视频录入时稳定音频流的接受,我们这里设置为自动
  11. if ([captureConnection isVideoStabilizationSupported]) {
  12. captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
  13. }
  14. }

6.创建视频预览图层AVCaptureVideoPreviewLayer并指定媒体会话,添加图层到显示容器中,调用AVCaptureSession的startRuning方法开始捕获。

  1. // 通过会话 (AVCaptureSession) 创建预览层
  2. _captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
  3.  
  4. // 显示在视图表面的图层
  5. CALayer *layer = self.viewContrain.layer;
  6. layer.masksToBounds = true;
  7.  
  8. _captureVideoPreviewLayer.frame = layer.bounds;
  9. _captureVideoPreviewLayer.masksToBounds = true;
  10. _captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式
  11. [layer addSublayer:_captureVideoPreviewLayer];
  12.  
  13. // 让会话(AVCaptureSession)勾搭好输入输出,然后把视图渲染到预览层上
  14. [_captureSession startRunning];

7.将捕获的音频或视频数据输出到指定文件。

  1. 创建一个拍摄的按钮,当我们点击这个按钮就会触发视频录制,并将这个录制的视频放到 temp 文件夹中
  1. - (IBAction)takeMovie:(id)sender {
  2. [(UIButton *)sender setSelected:![(UIButton *)sender isSelected]];
  3. if ([(UIButton *)sender isSelected]) {
  4. AVCaptureConnection *captureConnection=[self.caputureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
  5. // 开启视频防抖模式
  6. AVCaptureVideoStabilizationMode stabilizationMode = AVCaptureVideoStabilizationModeCinematic;
  7. if ([self.captureDeviceInput.device.activeFormat isVideoStabilizationModeSupported:stabilizationMode]) {
  8. [captureConnection setPreferredVideoStabilizationMode:stabilizationMode];
  9. }
  10.  
  11. //如果支持多任务则则开始多任务
  12. if ([[UIDevice currentDevice] isMultitaskingSupported]) {
  13. self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
  14. }
  15. // 预览图层和视频方向保持一致,这个属性设置很重要,如果不设置,那么出来的视频图像可以是倒向左边的。
  16. captureConnection.videoOrientation=[self.captureVideoPreviewLayer connection].videoOrientation;
  17.  
  18. // 设置视频输出的文件路径,这里设置为 temp 文件
  19. NSString *outputFielPath=[NSTemporaryDirectory() stringByAppendingString:MOVIEPATH];
  20.  
  21. // 路径转换成 URL 要用这个方法,用 NSBundle 方法转换成 URL 的话可能会出现读取不到路径的错误
  22. NSURL *fileUrl=[NSURL fileURLWithPath:outputFielPath];
  23.  
  24. // 往路径的 URL 开始写入录像 Buffer ,边录边写
  25. [self.caputureMovieFileOutput startRecordingToOutputFileURL:fileUrl recordingDelegate:self];
  26. }
  27. else {
  28. // 取消视频拍摄
  29. [self.caputureMovieFileOutput stopRecording];
  30. [self.captureSession stopRunning];
  31. [self completeHandle];
  32. }
  33. }
  1.  

当然我们录制的开始与结束都是有监听方法的,AVCaptureFileOutputRecordingDelegate 这个代理里面就有我们想要做的

  1. - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections
  2. {
  3. NSLog(@"---- 开始录制 ----");
  4. }
  5.  
  6. - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
  7. {
  8. NSLog(@"---- 录制结束 ----");
  9. }

到此,我们录制视频就结束了,那么是不是我们录制好了视频,就可以马上把这个视频上传给服务器分享给你的小伙伴们看了呢?
我们可以用如下方法测试一下我们录制出来的视频有多大 (m)

  1. - (CGFloat)getfileSize:(NSString *)path
  2. {
  3. NSDictionary *outputFileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
  4. NSLog (@"file size: %f", (unsigned long long)[outputFileAttributes fileSize]/1024.00 /1024.00);
  5. return (CGFloat)[outputFileAttributes fileSize]/1024.00 /1024.00;
  6. }

个人在这里做过测试,录制了 10s 的小视频得到的文件大小为 4.1M 左右,而且我用的分辨率还是640x480。。。很无语了是不是?
如果我们录制的视频,录制完成后要与服务器进行必要的上传,那么,我们肯定不能把这个刚刚录制出来的视频上传给服务器的,我们有必要对这个视频进行压缩了。那么我们的压缩方法,就要用到 AVAssetExportSeeion 这个类了。

  1. // 这里我们创建一个按钮,当点击这个按钮,我们就会调用压缩视频的方法,然后再去重新计算大小,这样就会跟未被压缩前的大小有个明显的对比了
  2.  
  3. // 压缩视频
  4. - (IBAction)compressVideo:(id)sender
  5. {
  6. NSString *cachePath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
  7. NSString *savePath=[cachePath stringByAppendingPathComponent:MOVIEPATH];
  8. NSURL *saveUrl=[NSURL fileURLWithPath:savePath];
  9.  
  10. // 通过文件的 url 获取到这个文件的资源
  11. AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:saveUrl options:nil];
  12. // 用 AVAssetExportSession 这个类来导出资源中的属性
  13. NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
  14.  
  15. // 压缩视频
  16. if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) { // 导出属性是否包含低分辨率
  17. // 通过资源(AVURLAsset)来定义 AVAssetExportSession,得到资源属性来重新打包资源 (AVURLAsset, 将某一些属性重新定义
  18. AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPresetLowQuality];
  19. // 设置导出文件的存放路径
  20. NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
  21. [formatter setDateFormat:@"yyyy-MM-dd-HH:mm:ss"];
  22. NSDate *date = [[NSDate alloc] init];
  23. NSString *outPutPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"output-%@.mp4",[formatter stringFromDate:date]]];
  24. exportSession.outputURL = [NSURL fileURLWithPath:outPutPath];
  25.  
  26. // 是否对网络进行优化
  27. exportSession.shouldOptimizeForNetworkUse = true;
  28.  
  29. // 转换成MP4格式
  30. exportSession.outputFileType = AVFileTypeMPEG4;
  31.  
  32. // 开始导出,导出后执行完成的block
  33. [exportSession exportAsynchronouslyWithCompletionHandler:^{
  34. // 如果导出的状态为完成
  35. if ([exportSession status] == AVAssetExportSessionStatusCompleted) {
  36. dispatch_async(dispatch_get_main_queue(), ^{
  37. // 更新一下显示包的大小
  38. self.videoSize.text = [NSString stringWithFormat:@"%f MB",[self getfileSize:outPutPath]];
  39. });
  40. }
  41. }];
  42. }
  43. }

经过我们的压缩,这个时候10s 的 4M 视频就只剩下不够 1M 了。

以下是一些扩展:

自动闪光灯开启

  1. - (IBAction)flashAutoClick:(UIButton *)sender {
  2. [self setFlashMode:AVCaptureFlashModeAuto];
  3. [self setFlashModeButtonStatus];
  4. }

打开闪光灯

  1. - (IBAction)flashOnClick:(UIButton *)sender {
  2. [self setFlashMode:AVCaptureFlashModeOn];
  3. [self setFlashModeButtonStatus];
  4. }

关闭闪光灯

  1. - (IBAction)flashOffClick:(UIButton *)sender {
  2. [self setFlashMode:AVCaptureFlashModeOff];
  3. [self setFlashModeButtonStatus];
  4. }

通知

  1. /**
  2. * 给输入设备添加通知
  3. */
  4. -(void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice{
  5. //注意添加区域改变捕获通知必须首先设置设备允许捕获
  6. [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
  7. captureDevice.subjectAreaChangeMonitoringEnabled=YES;
  8. }];
  9. NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
  10. //捕获区域发生改变
  11. [notificationCenter addObserver:self selector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
  12. }
  13. -(void)removeNotificationFromCaptureDevice:(AVCaptureDevice *)captureDevice{
  14. NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
  15. [notificationCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
  16. }
  17. /**
  18. * 移除所有通知
  19. */
  20. -(void)removeNotification{
  21. NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
  22. [notificationCenter removeObserver:self];
  23. }
  24.  
  25. -(void)addNotificationToCaptureSession:(AVCaptureSession *)captureSession{
  26. NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
  27. //会话出错
  28. [notificationCenter addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:captureSession];
  29. }
  30.  
  31. /**
  32. * 设备连接成功
  33. *
  34. * @param notification 通知对象
  35. */
  36. -(void)deviceConnected:(NSNotification *)notification{
  37. NSLog(@"设备已连接...");
  38. }
  39. /**
  40. * 设备连接断开
  41. *
  42. * @param notification 通知对象
  43. */
  44. -(void)deviceDisconnected:(NSNotification *)notification{
  45. NSLog(@"设备已断开.");
  46. }
  47. /**
  48. * 捕获区域改变
  49. *
  50. * @param notification 通知对象
  51. */
  52. -(void)areaChange:(NSNotification *)notification{
  53. NSLog(@"捕获区域改变...");
  54. }
  55.  
  56. /**
  57. * 会话出错
  58. *
  59. * @param notification 通知对象
  60. */
  61. -(void)sessionRuntimeError:(NSNotification *)notification{
  62. NSLog(@"会话发生错误.");
  63. }

私有方法

  1. /**
  2. * 取得指定位置的摄像头
  3. *
  4. * @param position 摄像头位置
  5. *
  6. * @return 摄像头设备
  7. */
  8. -(AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position{
  9. NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
  10. for (AVCaptureDevice *camera in cameras) {
  11. if ([camera position]==position) {
  12. return camera;
  13. }
  14. }
  15. return nil;
  16. }
  17.  
  18. /**
  19. * 改变设备属性的统一操作方法
  20. *
  21. * @param propertyChange 属性改变操作
  22. */
  23. -(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{
  24. AVCaptureDevice *captureDevice= [self.captureDeviceInput device];
  25. NSError *error;
  26. //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁
  27. if ([captureDevice lockForConfiguration:&error]) {
  28. propertyChange(captureDevice);
  29. [captureDevice unlockForConfiguration];
  30. }else{
  31. NSLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription);
  32. }
  33. }
  34.  
  35. /**
  36. * 设置闪光灯模式
  37. *
  38. * @param flashMode 闪光灯模式
  39. */
  40. -(void)setFlashMode:(AVCaptureFlashMode )flashMode{
  41. [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
  42. if ([captureDevice isFlashModeSupported:flashMode]) {
  43. [captureDevice setFlashMode:flashMode];
  44. }
  45. }];
  46. }
  47. /**
  48. * 设置聚焦模式
  49. *
  50. * @param focusMode 聚焦模式
  51. */
  52. -(void)setFocusMode:(AVCaptureFocusMode )focusMode{
  53. [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
  54. if ([captureDevice isFocusModeSupported:focusMode]) {
  55. [captureDevice setFocusMode:focusMode];
  56. }
  57. }];
  58. }
  59. /**
  60. * 设置曝光模式
  61. *
  62. * @param exposureMode 曝光模式
  63. */
  64. -(void)setExposureMode:(AVCaptureExposureMode)exposureMode{
  65. [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
  66. if ([captureDevice isExposureModeSupported:exposureMode]) {
  67. [captureDevice setExposureMode:exposureMode];
  68. }
  69. }];
  70. }
  71.  
  72. /**
  73. * 设置聚焦点
  74. *
  75. * @param point 聚焦点
  76. */
  77. -(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{
  78. [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
  79. if ([captureDevice isFocusModeSupported:focusMode]) {
  80. [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
  81. }
  82. if ([captureDevice isFocusPointOfInterestSupported]) {
  83. [captureDevice setFocusPointOfInterest:point];
  84. }
  85. if ([captureDevice isExposureModeSupported:exposureMode]) {
  86. [captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];
  87. }
  88. if ([captureDevice isExposurePointOfInterestSupported]) {
  89. [captureDevice setExposurePointOfInterest:point];
  90. }
  91. }];
  92. }
  93.  
  94. /**
  95. * 添加点按手势,点按时聚焦
  96. */
  97. -(void)addGenstureRecognizer{
  98. UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapScreen:)];
  99. [self.viewContainer addGestureRecognizer:tapGesture];
  100. }
  101. -(void)tapScreen:(UITapGestureRecognizer *)tapGesture{
  102. CGPoint point= [tapGesture locationInView:self.viewContainer];
  103. //将UI坐标转化为摄像头坐标
  104. CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
  105. [self setFocusCursorWithPoint:point];
  106. [self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
  107. }
  108.  
  109. /**
  110. * 设置闪光灯按钮状态
  111. */
  112. -(void)setFlashModeButtonStatus{
  113. AVCaptureDevice *captureDevice=[self.captureDeviceInput device];
  114. AVCaptureFlashMode flashMode=captureDevice.flashMode;
  115. if([captureDevice isFlashAvailable]){
  116. self.flashAutoButton.hidden=NO;
  117. self.flashOnButton.hidden=NO;
  118. self.flashOffButton.hidden=NO;
  119. self.flashAutoButton.enabled=YES;
  120. self.flashOnButton.enabled=YES;
  121. self.flashOffButton.enabled=YES;
  122. switch (flashMode) {
  123. case AVCaptureFlashModeAuto:
  124. self.flashAutoButton.enabled=NO;
  125. break;
  126. case AVCaptureFlashModeOn:
  127. self.flashOnButton.enabled=NO;
  128. break;
  129. case AVCaptureFlashModeOff:
  130. self.flashOffButton.enabled=NO;
  131. break;
  132. default:
  133. break;
  134. }
  135. }else{
  136. self.flashAutoButton.hidden=YES;
  137. self.flashOnButton.hidden=YES;
  138. self.flashOffButton.hidden=YES;
  139. }
  140. }
  141.  
  142. /**
  143. * 设置聚焦光标位置
  144. *
  145. * @param point 光标位置
  146. */
  147. -(void)setFocusCursorWithPoint:(CGPoint)point{
  148. self.focusCursor.center=point;
  149. self.focusCursor.transform=CGAffineTransformMakeScale(1.5, 1.5);
  150. self.focusCursor.alpha=1.0;
  151. [UIView animateWithDuration:1.0 animations:^{
  152. self.focusCursor.transform=CGAffineTransformIdentity;
  153. } completion:^(BOOL finished) {
  154. self.focusCursor.alpha=0;
  155.  
  156. }];
  157. }

OC录制小视频的更多相关文章

  1. 第五十九篇、OC录制小视频

    用 AVCaptureSession + AVCaptureMovieFileOutput 来录制视频,并通过AVAssetExportSeeion 手段来压缩视频并转换为 MP4 格 AVFound ...

  2. FFMpeg笔记(五) 录制小视频时几个问题解决

    1. YUV数据在使用avfilter scale时在特定的分辨率下UV分量不对 由于是小视频,那么分辨率不需要太高,但是有的视频源是1080p,甚至有的是4K的,所以对视频源进行scale非常有必要 ...

  3. Android 仿微信小视频录制

    Android 仿微信小视频录制 WechatShortVideo和WechatShortVideo文章

  4. Webcast / 技术小视频制作方法——自己动手录制video轻松搞定

    Webcast / 技术小视频制作方法——自己动手录制video轻松搞定 http://blog.sina.com.cn/s/blog_67d387490100wdnh.html 最近申请加入MSP的 ...

  5. ios设备突破微信小视频6S限制的方法

    刷微信朋友圈只发文字和图片怎能意犹未竟,微信小视频是一个很好的补充,音视频到位,流行流行最流行.但小视频时长不能超过6S,没有滤镜等是很大的遗憾.but有人突破限制玩出了花样,用ios设备在朋友圈晒出 ...

  6. [iOS]手把手教你实现微信小视频

    本文个人原创,转载请注明出处,谢谢. 前段时间项目要求需要在聊天模块中加入类似微信的小视频功能,这边博客主要是为了总结遇到的问题和解决方法,希望能够对有同样需求的朋友有所帮助. 效果预览: 这里先罗列 ...

  7. vivo如何录制手机视频 分享简单的操作方法

    智能手机功能不断的发展更新,手机已经普及到每一个人,在日常的生活或者工作中都离不开手机,手机中的功能例如一些小视频软件都是非常有趣的,vivo如何录制手机视频?下面我们一起来看看吧! 使用工具:手机 ...

  8. oppor9手机怎么录制屏幕视频

    我们已经进入互联网时代,每个人都寸步不离手机.电脑等电子产品,看到美丽好看的视频总想记录下来,毕竟看到喜欢的视频还真不太容易,所以问题来了,oppor9手机怎么录制屏幕视频呢?安卓手机上怎么录制屏幕视 ...

  9. Android 仿微信朋友圈拍小视频上传到服务器

    这个接上一个写的实现拍小视频和传到服务器的  界面是这个样子滴. 我也知不知道怎么给图片搞小一点o(╯□╰)o 布局文件是这样的[认真脸] <?xml version="1.0&quo ...

随机推荐

  1. [国家集训队2012]middle(陈立杰)

    我是萌萌的传送门 我是另一个萌萌的传送门 脑残错误毁一下午…… 其实题解早就烂大街了,然而很久之前我只知道是二分答案+主席树却想不出来这俩玩意儿怎么一块儿用的……今天又翻了几篇题解才恍然大悟,是把权值 ...

  2. Python代码 变量None的使用

    代码中经常会有变量是否为None的判断,有三种主要的写法: 第一种是'if x is None': 第二种是 'if not x:': 第三种是'if not x is None'(这句这样理解更清晰 ...

  3. 操作Hadoop集群

    操作Hadoop集群 所有必要的配置完成后,将文件分发到所有机器上的HADOOP_CONF_DIR目录.这应该是所有机器上相同的目录. 一般来说,建议HDFS和YARN作为单独的用户运行.在大多数安装 ...

  4. Creator4.2建模心得与技巧1——树的建立与跟随摄像机旋转

    Creator建模: 树一般在虚拟现实程序中都用面来实现,一种方法是通过两个面相互垂直成90度叠放在一起,另一种方法是让树面正对着视角一起旋转.这里主要说一下第二种方法. 主要思路:把树面一直正对着摄 ...

  5. Windows API 编程----将错误代码转换成错误描述信息

    Windows编程有时会因为调用函数而产生错误,调用GetLastError()函数可以得到错误代码.如果错误代码为0,说明没有错误:如果错误代码不为0,则说明存在错误. 而错误代码不方便编程人员或用 ...

  6. 基础架构之Gitlab Runner

    基础架构之Gitlab Runner也是常用的基础设施,我们接着GitLab操作,具体使用GitlabRunner,如果不熟悉可以见官方详细介绍https://docs.gitlab.com/runn ...

  7. android中的内部存储与外部存储

    我们先来考虑这样一个问题: 打开手机设置,选择应用管理,选择任意一个App,然后你会看到两个按钮,一个是清除缓存,另一个是清除数据,那么当我们点击清除缓存的时候清除的是哪里的数据?当我们点击清除数据的 ...

  8. 【阿里云产品公测】ACE下上传文件永久存储实践

    本帖主要内容: ;$,=VB:'   在阿里云的ACE下,我是如何实现让上传的文件永久保存的? ,%"!8T   本文以PHP为例,具体知识点如下: WD# 96V   第一,扩展服务“存储 ...

  9. Python爬虫教程-32-Scrapy 爬虫框架项目 Settings.py 介绍

    本篇介绍项目开发的过程中,对 Setting 文件的配置和使用 Python爬虫教程-32-Scrapy 爬虫框架项目 Settings.py 介绍 settings.py 文件的使用 想要详细查看 ...

  10. HighChart利用servlet导出中文PNG图片乱码问题解决

    最近用到HighChart作图,在图片导出时,出现了图片中中文乱码的问题,在网络上找了很多资料,但都没有解决,最后才发现了最容易被忽略的问题.具体见下. 由于之前有同事使用过HighChart,所以毫 ...