1 /*
2 * AVFoundation input device
3 * Copyright (c) 2014 Thilo Borgmann <thilo.borgmann@mail.de>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file
24 * AVFoundation input device
25 * @author Thilo Borgmann <thilo.borgmann@mail.de>
26 */
27
28 #import <AVFoundation/AVFoundation.h>
29 #include <pthread.h>
30
38
40
44 };
45
49 };
50
74 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
76 #endif
78 };
79
81 {
83
92
98
101
103
111
114
116
123
125 {
127 }
128
130 {
132 }
133
134 /** FrameReciever class - delegate for AVCaptureSession
135 */
137 {
139 }
140
142
143 - (
void) captureOutput:(AVCaptureOutput *)captureOutput
144 didOutputSampleBuffer:(CMSampleBufferRef)videoFrame
145 fromConnection:(AVCaptureConnection *)connection;
146
147 @end
148
150
152 {
153 if (
self = [super
init]) {
155 }
156 return self;
157 }
158
159 - (
void) captureOutput:(AVCaptureOutput *)captureOutput
160 didOutputSampleBuffer:(CMSampleBufferRef)videoFrame
161 fromConnection:(AVCaptureConnection *)connection
162 {
164
167 }
168
170
172
174
176 }
177
178 @end
179
180 /** AudioReciever class - delegate for AVCaptureSession
181 */
183 {
185 }
186
188
189 - (
void) captureOutput:(AVCaptureOutput *)captureOutput
190 didOutputSampleBuffer:(CMSampleBufferRef)audioFrame
191 fromConnection:(AVCaptureConnection *)connection;
192
193 @end
194
196
198 {
199 if (
self = [super
init]) {
201 }
202 return self;
203 }
204
205 - (
void) captureOutput:(AVCaptureOutput *)captureOutput
206 didOutputSampleBuffer:(CMSampleBufferRef)audioFrame
207 fromConnection:(AVCaptureConnection *)connection
208 {
210
213 }
214
216
218
220
222 }
223
224 @end
225
227 {
228 [ctx->capture_session stopRunning];
229
230 [ctx->capture_session release];
231 [ctx->video_output release];
232 [ctx->audio_output release];
233 [ctx->avf_delegate release];
234 [ctx->avf_audio_delegate release];
235
241
243
246
249 }
250 }
251
253 {
256 char *save;
257
258 if (tmp[0] != ':') {
261 } else {
263 }
264 }
265
267 {
269 NSError *error = nil;
270 AVCaptureInput* capture_input = nil;
271
273 capture_input = (AVCaptureInput*) [[[AVCaptureDeviceInput alloc] initWithDevice:video_device error:&error] autorelease];
274 } else {
275 capture_input = (AVCaptureInput*) video_device;
276 }
277
278 if (!capture_input) {
280 [[error localizedDescription] UTF8String]);
281 return 1;
282 }
283
285 [ctx->capture_session addInput:capture_input];
286 } else {
288 return 1;
289 }
290
291 // Attaching output
292 ctx->
video_output = [[AVCaptureVideoDataOutput alloc] init];
293
296 return 1;
297 }
298
299 // select pixel format
302
305 pxl_fmt_spec = avf_pixel_formats[i];
306 break;
307 }
308 }
309
310 // check if selected pixel format is supported by AVFoundation
312 av_log(s,
AV_LOG_ERROR,
"Selected pixel format (%s) is not supported by AVFoundation.\n",
314 return 1;
315 }
316
317 // check if the pixel format is available for this device
318 if ([[ctx->
video_output availableVideoCVPixelFormatTypes] indexOfObject:[NSNumber numberWithInt:pxl_fmt_spec.
avf_id]] == NSNotFound) {
319 av_log(s,
AV_LOG_ERROR,
"Selected pixel format (%s) is not supported by the input device.\n",
321
323
325 for (NSNumber *pxl_fmt
in [ctx->
video_output availableVideoCVPixelFormatTypes]) {
329 if ([pxl_fmt intValue] == avf_pixel_formats[i].
avf_id) {
330 pxl_fmt_dummy = avf_pixel_formats[i];
331 break;
332 }
333 }
334
337
338 // select first supported pixel format instead of user selected (or default) pixel format
340 pxl_fmt_spec = pxl_fmt_dummy;
341 }
342 }
343 }
344
345 // fail if there is no appropriate pixel format or print a warning about overriding the pixel format
347 return 1;
348 } else {
351 }
352 }
353
355 NSNumber *pixel_format = [NSNumber numberWithUnsignedInt:pxl_fmt_spec.avf_id];
356 NSDictionary *capture_dict = [NSDictionary dictionaryWithObject:pixel_format
357 forKey:(id)kCVPixelBufferPixelFormatTypeKey];
358
359 [ctx->video_output setVideoSettings:capture_dict];
360 [ctx->video_output setAlwaysDiscardsLateVideoFrames:YES];
361
363
364 dispatch_queue_t queue = dispatch_queue_create(
"avf_queue",
NULL);
365 [ctx->video_output setSampleBufferDelegate:ctx->avf_delegate queue:queue];
366 dispatch_release(queue);
367
369 [ctx->capture_session addOutput:ctx->video_output];
370 } else {
372 return 1;
373 }
374
375 return 0;
376 }
377
379 {
381 NSError *error = nil;
382 AVCaptureDeviceInput* audio_dev_input = [[[AVCaptureDeviceInput alloc] initWithDevice:audio_device error:&error] autorelease];
383
384 if (!audio_dev_input) {
386 [[error localizedDescription] UTF8String]);
387 return 1;
388 }
389
391 [ctx->capture_session addInput:audio_dev_input];
392 } else {
394 return 1;
395 }
396
397 // Attaching output
398 ctx->
audio_output = [[AVCaptureAudioDataOutput alloc] init];
399
402 return 1;
403 }
404
406
407 dispatch_queue_t queue = dispatch_queue_create(
"avf_audio_queue",
NULL);
408 [ctx->audio_output setSampleBufferDelegate:ctx->avf_audio_delegate queue:queue];
409 dispatch_release(queue);
410
412 [ctx->capture_session addOutput:ctx->audio_output];
413 } else {
415 return 1;
416 }
417
418 return 0;
419 }
420
422 {
424
425 // Take stream info from the first frame.
427 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES);
428 }
429
431
433
434 if (!stream) {
435 return 1;
436 }
437
439
441
442 CVImageBufferRef image_buffer = CMSampleBufferGetImageBuffer(ctx->
current_frame);
443 CGSize image_buffer_size = CVImageBufferGetEncodedSize(image_buffer);
444
447 stream->
codec->
width = (int)image_buffer_size.width;
448 stream->
codec->
height = (
int)image_buffer_size.height;
450
453
455
456 return 0;
457 }
458
460 {
462
463 // Take stream info from the first frame.
465 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES);
466 }
467
469
471
472 if (!stream) {
473 return 1;
474 }
475
477
479
480 CMFormatDescriptionRef format_desc = CMSampleBufferGetFormatDescription(ctx->
current_audio_frame);
481 const AudioStreamBasicDescription *basic_desc = CMAudioFormatDescriptionGetStreamBasicDescription(format_desc);
482
483 if (!basic_desc) {
485 return 1;
486 }
487
492
495 ctx->
audio_float = basic_desc->mFormatFlags & kAudioFormatFlagIsFloat;
496 ctx->
audio_be = basic_desc->mFormatFlags & kAudioFormatFlagIsBigEndian;
498 ctx->
audio_packed = basic_desc->mFormatFlags & kAudioFormatFlagIsPacked;
500
501 if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
506 } else if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
511 } else if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
516 } else if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
521 } else {
523 return 1;
524 }
525
532 return 1;
533 }
534 }
535
538
540
541 return 0;
542 }
543
545 {
546 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
550 uint32_t num_screens = 0;
551
554
555 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
556 CGGetActiveDisplayList(0,
NULL, &num_screens);
557 #endif
558
559 // List devices if requested
562 NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
564 for (AVCaptureDevice *device
in devices) {
565 const char *
name = [[device localizedName] UTF8String];
566 index = [devices indexOfObject:device];
568 index++;
569 }
570 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
571 if (num_screens > 0) {
572 CGDirectDisplayID screens[num_screens];
573 CGGetActiveDisplayList(num_screens, screens, &num_screens);
574 for (int i = 0; i < num_screens; i++) {
576 }
577 }
578 #endif
579
581 devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
582 for (AVCaptureDevice *device
in devices) {
583 const char *
name = [[device localizedName] UTF8String];
584 int index = [devices indexOfObject:device];
586 }
587 goto fail;
588 }
589
590 // Find capture device
591 AVCaptureDevice *video_device = nil;
592 AVCaptureDevice *audio_device = nil;
593
594 NSArray *video_devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
596
597 // parse input filename for video and audio device
599
600 // check for device index given in filename
603 }
606 }
607
610 video_device = [video_devices objectAtIndex:ctx->video_device_index];
612 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
613 CGDirectDisplayID screens[num_screens];
614 CGGetActiveDisplayList(num_screens, screens, &num_screens);
615 AVCaptureScreenInput* capture_screen_input = [[[AVCaptureScreenInput alloc] initWithDisplayID:screens[ctx->video_device_index - ctx->
num_video_devices]] autorelease];
616 video_device = (AVCaptureDevice*) capture_screen_input;
617 #endif
618 } else {
620 goto fail;
621 }
625 video_device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
626 } else {
627 // looking for video inputs
628 for (AVCaptureDevice *device
in video_devices) {
630 video_device = device;
631 break;
632 }
633 }
634
635 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
636 // looking for screen inputs
637 if (!video_device) {
638 int idx;
639 if(sscanf(ctx->
video_filename,
"Capture screen %d", &idx) && idx < num_screens) {
640 CGDirectDisplayID screens[num_screens];
641 CGGetActiveDisplayList(num_screens, screens, &num_screens);
642 AVCaptureScreenInput* capture_screen_input = [[[AVCaptureScreenInput alloc] initWithDisplayID:screens[idx]] autorelease];
643 video_device = (AVCaptureDevice*) capture_screen_input;
645 }
646 }
647 #endif
648 }
649
650 if (!video_device) {
652 goto fail;
653 }
654 }
655
656 // get audio device
658 NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
659
662 goto fail;
663 }
664
665 audio_device = [devices objectAtIndex:ctx->audio_device_index];
669 audio_device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
670 } else {
671 NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
672
673 for (AVCaptureDevice *device
in devices) {
675 audio_device = device;
676 break;
677 }
678 }
679 }
680
681 if (!audio_device) {
683 goto fail;
684 }
685 }
686
687 // Video nor Audio capture device not found, looking for AVMediaTypeVideo/Audio
688 if (!video_device && !audio_device) {
690 goto fail;
691 }
692
693 if (video_device) {
696 } else {
698 }
699 }
700 if (audio_device) {
701 av_log(s,
AV_LOG_DEBUG,
"audio device '%s' opened\n", [[audio_device localizedName] UTF8String]);
702 }
703
704 // Initialize capture session
706
708 goto fail;
709 }
711 }
712
713 [ctx->capture_session startRunning];
714
716 goto fail;
717 }
718
719 // set audio stream
721 goto fail;
722 }
723
724 [pool release];
725 return 0;
726
727 fail:
728 [pool release];
731 }
732
734 {
736
737 do {
739
740 CVImageBufferRef image_buffer = CMSampleBufferGetImageBuffer(ctx->
current_frame);
741
743 if (
av_new_packet(pkt, (
int)CVPixelBufferGetDataSize(image_buffer)) < 0) {
745 }
746
749 avf_time_base_q);
752
753 CVPixelBufferLockBaseAddress(image_buffer, 0);
754
755 void*
data = CVPixelBufferGetBaseAddress(image_buffer);
757
758 CVPixelBufferUnlockBaseAddress(image_buffer, 0);
763 int block_buffer_size = CMBlockBufferGetDataLength(block_buffer);
764
765 if (!block_buffer || !block_buffer_size) {
767 }
768
771 }
772
775 }
776
779 avf_time_base_q);
780
783
786
788 if (ret != kCMBlockBufferNoErr) {
790 }
791
793
794 // transform decoded frame into output format
795 #define INTERLEAVE_OUTPUT(bps) \
796 { \
797 int##bps##_t **src; \
798 int##bps##_t *dest; \
799 src = av_malloc(ctx->audio_channels * sizeof(int##bps##_t*)); \
800 if (!src) return AVERROR(EIO); \
801 for (c = 0; c < ctx->audio_channels; c++) { \
802 src[c] = ((int##bps##_t*)ctx->audio_buffer) + c * num_samples; \
803 } \
804 dest = (int##bps##_t*)pkt->data; \
805 shift = bps - ctx->audio_bits_per_sample; \
806 for (sample = 0; sample < num_samples; sample++) \
807 for (c = 0; c < ctx->audio_channels; c++) \
808 *dest++ = src[c][sample] << shift; \
809 av_freep(&src); \
810 }
811
814 } else {
816 }
817 } else {
818 OSStatus
ret = CMBlockBufferCopyDataBytes(block_buffer, 0, pkt->
size, pkt->
data);
819 if (ret != kCMBlockBufferNoErr) {
821 }
822 }
823
826 } else {
829 }
830
833
834 return 0;
835 }
836
838 {
841 return 0;
842 }
843
845 {
"list_devices",
"list available devices", offsetof(
AVFContext, list_devices),
AV_OPT_TYPE_INT, {.i64=0}, 0, 1,
AV_OPT_FLAG_DECODING_PARAM,
"list_devices" },
852 };
853
860 };
861
863 .
name =
"avfoundation",
870 .priv_class = &avf_class,
871 };