回答編集履歴

1

サンプルコードを追加

2020/10/01 13:52

投稿

TsukubaDepot
TsukubaDepot

スコア5086

test CHANGED
@@ -41,3 +41,395 @@
41
41
 
42
42
 
43
43
  というステップで実行できると思います。
44
+
45
+
46
+
47
+ ##2020年10月1日追記
48
+
49
+ 参考にされたコードは iOS10 以降ではそのまま使えないため、使えるように一部変更を加えてみました。
50
+
51
+
52
+
53
+ また、オリジナルのコードはだと、録画終了後指定したフォルダに映像を保存するだけで、実はビデオライブラリには保存されないため、録画状態を確認することができません(要は、「写真」アプリで録画された映像を確認することができない)。
54
+
55
+
56
+
57
+ また、UIの一部が少し紛らわしいため、その修正も加えたコードを下記に提示しますので、コード中のコメントをよく読まれた上、試してみていただけますでしょうか。
58
+
59
+
60
+
61
+ ```Swift
62
+
63
+ // This code is originally based on http://faboplatform.github.io/SwiftDocs/5.avfoundation/003_avfoundation/
64
+
65
+ // Some lines of this program are modified to enable to run this code correctly after iOS 10.0 ,and added codes to save captured image to the Photo library.
66
+
67
+
68
+
69
+ import UIKit
70
+
71
+ import AVFoundation
72
+
73
+
74
+
75
+ // MARK: - フォトライブラリに保存するために必要
76
+
77
+ import Photos
78
+
79
+
80
+
81
+ class ViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
82
+
83
+
84
+
85
+ // ビデオのアウトプット.
86
+
87
+ private var myVideoOutput: AVCaptureMovieFileOutput!
88
+
89
+
90
+
91
+ // スタートボタン.
92
+
93
+ private var myButtonStart: UIButton!
94
+
95
+
96
+
97
+ // ストップボタン.
98
+
99
+ private var myButtonStop: UIButton!
100
+
101
+
102
+
103
+ override func viewDidLoad() {
104
+
105
+ super.viewDidLoad()
106
+
107
+ print(#function)
108
+
109
+ // セッションの作成.
110
+
111
+ let mySession = AVCaptureSession()
112
+
113
+
114
+
115
+ // デバイス.
116
+
117
+ var myDevice = AVCaptureDevice.default(for: .video)
118
+
119
+
120
+
121
+ // 出力先を生成.
122
+
123
+ // let myImageOutput = AVCaptureStillImageOutput()
124
+
125
+ // MARK: - 'AVCaptureStillImageOutput' was deprecated in iOS 10.0: Use AVCapturePhotoOutput instead.
126
+
127
+ // 警告通り、AVCapturePhotoOutput() でインスタンスを生成するよういする
128
+
129
+ let myImageOutput = AVCapturePhotoOutput()
130
+
131
+
132
+
133
+ // デバイス一覧の取得.
134
+
135
+ // let devices = AVCaptureDevice.devices()
136
+
137
+ // MARK: - 'devices()' was deprecated in iOS 10.0: Use AVCaptureDeviceDiscoverySession instead.
138
+
139
+ // 警告に従うが、警告で出力されているメソッド名が微妙に異なる(AVCaptureDevice.DiscoverySessionが正解)なので注意する
140
+
141
+ // 詳しい使い方は公式ドキュメントを参照
142
+
143
+ // refer to: https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/choosing_a_capture_device
144
+
145
+ let devices = AVCaptureDevice.DiscoverySession(deviceTypes:
146
+
147
+ [.builtInTrueDepthCamera, .builtInDualCamera, .builtInWideAngleCamera],
148
+
149
+ mediaType: .video, position: .unspecified).devices
150
+
151
+ // マイクを取得.
152
+
153
+ let audioCaptureDevice = AVCaptureDevice.default(for: .audio)
154
+
155
+
156
+
157
+ // マイクをセッションのInputに追加.
158
+
159
+ let audioInput = try! AVCaptureDeviceInput.init(device: audioCaptureDevice!)
160
+
161
+
162
+
163
+ // バックライトをmyDeviceに格納.
164
+
165
+
166
+
167
+ // for device in devices {
168
+
169
+ // if(device.position == AVCaptureDevice.Position.back){
170
+
171
+ // myDevice = device
172
+
173
+ // }
174
+
175
+ // }
176
+
177
+ // MARK: - オリジナルのコードから一部変更。
178
+
179
+ guard !devices.isEmpty else { fatalError("選択されたデバイスは存在しません") }
180
+
181
+ myDevice = devices.first { $0.position == .back }
182
+
183
+
184
+
185
+ // バックカメラを取得.
186
+
187
+ let videoInput = try! AVCaptureDeviceInput.init(device: myDevice!)
188
+
189
+
190
+
191
+ // ビデオをセッションのInputに追加.
192
+
193
+ mySession.addInput(videoInput)
194
+
195
+
196
+
197
+ // オーディオをセッションに追加.
198
+
199
+ mySession.addInput(audioInput)
200
+
201
+
202
+
203
+ // セッションに追加.
204
+
205
+ mySession.addOutput(myImageOutput)
206
+
207
+
208
+
209
+ // 動画の保存.
210
+
211
+ myVideoOutput = AVCaptureMovieFileOutput()
212
+
213
+
214
+
215
+ // ビデオ出力をOutputに追加.
216
+
217
+ mySession.addOutput(myVideoOutput)
218
+
219
+
220
+
221
+ // 画像を表示するレイヤーを生成.
222
+
223
+ let myVideoLayer = AVCaptureVideoPreviewLayer.init(session: mySession)
224
+
225
+ myVideoLayer.frame = self.view.bounds
226
+
227
+ // myVideoLayer.session = myDevice
228
+
229
+ myVideoLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
230
+
231
+
232
+
233
+ // Viewに追加.
234
+
235
+ self.view.layer.addSublayer(myVideoLayer)
236
+
237
+
238
+
239
+ // セッション開始.
240
+
241
+ mySession.startRunning()
242
+
243
+
244
+
245
+ // UIボタンを作成.
246
+
247
+ myButtonStart = UIButton(frame: CGRect(x: 0, y: 0, width: 120, height: 50))
248
+
249
+ myButtonStop = UIButton(frame: CGRect(x: 0, y: 0, width: 120, height: 50))
250
+
251
+
252
+
253
+ myButtonStart.backgroundColor = UIColor.red
254
+
255
+ myButtonStop.backgroundColor = UIColor.gray
256
+
257
+
258
+
259
+ myButtonStart.layer.masksToBounds = true
260
+
261
+ myButtonStop.layer.masksToBounds = true
262
+
263
+
264
+
265
+ myButtonStart.setTitle("撮影", for: .normal)
266
+
267
+ myButtonStop.setTitle("停止", for: .normal)
268
+
269
+
270
+
271
+ myButtonStart.layer.cornerRadius = 20.0
272
+
273
+ myButtonStop.layer.cornerRadius = 20.0
274
+
275
+
276
+
277
+ myButtonStart.layer.position = CGPoint(x: self.view.bounds.width/2 - 70, y:self.view.bounds.height-50)
278
+
279
+ myButtonStop.layer.position = CGPoint(x: self.view.bounds.width/2 + 70, y:self.view.bounds.height-50)
280
+
281
+
282
+
283
+ myButtonStart.addTarget(self, action: #selector(ViewController.onClickMyButton), for: .touchUpInside)
284
+
285
+ myButtonStop.addTarget(self, action: #selector(ViewController.onClickMyButton), for: .touchUpInside)
286
+
287
+
288
+
289
+ // UIボタンをViewに追加.
290
+
291
+ self.view.addSubview(myButtonStart)
292
+
293
+ self.view.addSubview(myButtonStop)
294
+
295
+
296
+
297
+ //
298
+
299
+ myButtonStop.isHidden = true
300
+
301
+ }
302
+
303
+
304
+
305
+ // MARK:
306
+
307
+ func toggleButtonStatus() {
308
+
309
+ //
310
+
311
+ myButtonStop.isHidden.toggle()
312
+
313
+ myButtonStart.isHidden.toggle()
314
+
315
+ }
316
+
317
+
318
+
319
+ /*
320
+
321
+ ボタンイベント.
322
+
323
+ */
324
+
325
+ @objc internal func onClickMyButton(sender: UIButton){
326
+
327
+ // 撮影開始.
328
+
329
+ if( sender == myButtonStart ){
330
+
331
+ // MARK:
332
+
333
+ toggleButtonStatus()
334
+
335
+
336
+
337
+ let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
338
+
339
+
340
+
341
+ // フォルダ.
342
+
343
+ let documentsDirectory = paths[0]
344
+
345
+
346
+
347
+ // ファイル名.
348
+
349
+ let filePath = "(documentsDirectory)/test.mp4"
350
+
351
+
352
+
353
+ // URL.
354
+
355
+ let fileURL = URL(fileURLWithPath: filePath)
356
+
357
+
358
+
359
+ // 録画開始.
360
+
361
+ myVideoOutput.startRecording(to: fileURL, recordingDelegate: self)
362
+
363
+
364
+
365
+ }
366
+
367
+ // 撮影停止.
368
+
369
+ else if ( sender == myButtonStop ){
370
+
371
+ //
372
+
373
+ toggleButtonStatus()
374
+
375
+
376
+
377
+ myVideoOutput.stopRecording()
378
+
379
+ }
380
+
381
+ }
382
+
383
+
384
+
385
+ // MARK: - AVCaptureFileOutputRecordingDelegate
386
+
387
+
388
+
389
+ /*
390
+
391
+ 動画がキャプチャーされた後に呼ばれるメソッド.
392
+
393
+ */
394
+
395
+ func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
396
+
397
+ print("didFinishRecordingTo outputFileURL")
398
+
399
+
400
+
401
+ // MARK: - フォトライブラリにビデオを保存するのであれば、以下のコードを追加した上、NSPhotoLibraryAddUsageDescription を Info.plist に追加する必要がある。
402
+
403
+ PHPhotoLibrary.shared().performChanges({
404
+
405
+ PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputFileURL)
406
+
407
+ }) { completed, error in
408
+
409
+ if completed {
410
+
411
+ print("Video is saved!")
412
+
413
+ }
414
+
415
+ }
416
+
417
+ }
418
+
419
+
420
+
421
+ /*
422
+
423
+ 動画のキャプチャーが開始された時に呼ばれるメソッド.
424
+
425
+ */
426
+
427
+ func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {
428
+
429
+ print("didStartRecordingTo fileURL")
430
+
431
+ }
432
+
433
+ }
434
+
435
+ ```