mirror of https://git.ffmpeg.org/ffmpeg.git
lavd/avfoundation: Split adding a device and getting the device configuration into separate functions.
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
parent
88c937fdc8
commit
a69c70e148
|
@ -172,6 +172,148 @@ static void destroy_context(AVFContext* ctx)
|
|||
}
|
||||
}
|
||||
|
||||
static int add_video_device(AVFormatContext *s, AVCaptureDevice *video_device)
|
||||
{
|
||||
AVFContext *ctx = (AVFContext*)s->priv_data;
|
||||
NSError *error = nil;
|
||||
AVCaptureDeviceInput* capture_dev_input = [[[AVCaptureDeviceInput alloc] initWithDevice:video_device error:&error] autorelease];
|
||||
|
||||
if (!capture_dev_input) {
|
||||
av_log(s, AV_LOG_ERROR, "Failed to create AV capture input device: %s\n",
|
||||
[[error localizedDescription] UTF8String]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ([ctx->capture_session canAddInput:capture_dev_input]) {
|
||||
[ctx->capture_session addInput:capture_dev_input];
|
||||
} else {
|
||||
av_log(s, AV_LOG_ERROR, "can't add video input to capture session\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Attaching output
|
||||
ctx->video_output = [[AVCaptureVideoDataOutput alloc] init];
|
||||
|
||||
if (!ctx->video_output) {
|
||||
av_log(s, AV_LOG_ERROR, "Failed to init AV video output\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// select pixel format
|
||||
struct AVFPixelFormatSpec pxl_fmt_spec;
|
||||
pxl_fmt_spec.ff_id = AV_PIX_FMT_NONE;
|
||||
|
||||
for (int i = 0; avf_pixel_formats[i].ff_id != AV_PIX_FMT_NONE; i++) {
|
||||
if (ctx->pixel_format == avf_pixel_formats[i].ff_id) {
|
||||
pxl_fmt_spec = avf_pixel_formats[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if selected pixel format is supported by AVFoundation
|
||||
if (pxl_fmt_spec.ff_id == AV_PIX_FMT_NONE) {
|
||||
av_log(s, AV_LOG_ERROR, "Selected pixel format (%s) is not supported by AVFoundation.\n",
|
||||
av_get_pix_fmt_name(pxl_fmt_spec.ff_id));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check if the pixel format is available for this device
|
||||
if ([[ctx->video_output availableVideoCVPixelFormatTypes] indexOfObject:[NSNumber numberWithInt:pxl_fmt_spec.avf_id]] == NSNotFound) {
|
||||
av_log(s, AV_LOG_ERROR, "Selected pixel format (%s) is not supported by the input device.\n",
|
||||
av_get_pix_fmt_name(pxl_fmt_spec.ff_id));
|
||||
|
||||
pxl_fmt_spec.ff_id = AV_PIX_FMT_NONE;
|
||||
|
||||
av_log(s, AV_LOG_ERROR, "Supported pixel formats:\n");
|
||||
for (NSNumber *pxl_fmt in [ctx->video_output availableVideoCVPixelFormatTypes]) {
|
||||
struct AVFPixelFormatSpec pxl_fmt_dummy;
|
||||
pxl_fmt_dummy.ff_id = AV_PIX_FMT_NONE;
|
||||
for (int i = 0; avf_pixel_formats[i].ff_id != AV_PIX_FMT_NONE; i++) {
|
||||
if ([pxl_fmt intValue] == avf_pixel_formats[i].avf_id) {
|
||||
pxl_fmt_dummy = avf_pixel_formats[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pxl_fmt_dummy.ff_id != AV_PIX_FMT_NONE) {
|
||||
av_log(s, AV_LOG_ERROR, " %s\n", av_get_pix_fmt_name(pxl_fmt_dummy.ff_id));
|
||||
|
||||
// select first supported pixel format instead of user selected (or default) pixel format
|
||||
if (pxl_fmt_spec.ff_id == AV_PIX_FMT_NONE) {
|
||||
pxl_fmt_spec = pxl_fmt_dummy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fail if there is no appropriate pixel format or print a warning about overriding the pixel format
|
||||
if (pxl_fmt_spec.ff_id == AV_PIX_FMT_NONE) {
|
||||
return 1;
|
||||
} else {
|
||||
av_log(s, AV_LOG_WARNING, "Overriding selected pixel format to use %s instead.\n",
|
||||
av_get_pix_fmt_name(pxl_fmt_spec.ff_id));
|
||||
}
|
||||
}
|
||||
|
||||
ctx->pixel_format = pxl_fmt_spec.ff_id;
|
||||
NSNumber *pixel_format = [NSNumber numberWithUnsignedInt:pxl_fmt_spec.avf_id];
|
||||
NSDictionary *capture_dict = [NSDictionary dictionaryWithObject:pixel_format
|
||||
forKey:(id)kCVPixelBufferPixelFormatTypeKey];
|
||||
|
||||
[ctx->video_output setVideoSettings:capture_dict];
|
||||
[ctx->video_output setAlwaysDiscardsLateVideoFrames:YES];
|
||||
|
||||
ctx->avf_delegate = [[AVFFrameReceiver alloc] initWithContext:ctx];
|
||||
|
||||
dispatch_queue_t queue = dispatch_queue_create("avf_queue", NULL);
|
||||
[ctx->video_output setSampleBufferDelegate:ctx->avf_delegate queue:queue];
|
||||
dispatch_release(queue);
|
||||
|
||||
if ([ctx->capture_session canAddOutput:ctx->video_output]) {
|
||||
[ctx->capture_session addOutput:ctx->video_output];
|
||||
} else {
|
||||
av_log(s, AV_LOG_ERROR, "can't add video output to capture session\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_video_config(AVFormatContext *s)
|
||||
{
|
||||
AVFContext *ctx = (AVFContext*)s->priv_data;
|
||||
|
||||
// Take stream info from the first frame.
|
||||
while (ctx->frames_captured < 1) {
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES);
|
||||
}
|
||||
|
||||
lock_frames(ctx);
|
||||
|
||||
AVStream* stream = avformat_new_stream(s, NULL);
|
||||
|
||||
if (!stream) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
avpriv_set_pts_info(stream, 64, 1, avf_time_base);
|
||||
|
||||
CVImageBufferRef image_buffer = CMSampleBufferGetImageBuffer(ctx->current_frame);
|
||||
CGSize image_buffer_size = CVImageBufferGetEncodedSize(image_buffer);
|
||||
|
||||
stream->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
stream->codec->width = (int)image_buffer_size.width;
|
||||
stream->codec->height = (int)image_buffer_size.height;
|
||||
stream->codec->pix_fmt = ctx->pixel_format;
|
||||
|
||||
CFRelease(ctx->current_frame);
|
||||
ctx->current_frame = nil;
|
||||
|
||||
unlock_frames(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avf_read_header(AVFormatContext *s)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
@ -226,7 +368,7 @@ static int avf_read_header(AVFormatContext *s)
|
|||
goto fail;
|
||||
}
|
||||
} else {
|
||||
video_device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeMuxed];
|
||||
video_device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
||||
}
|
||||
|
||||
// Video capture device not found, looking for AVMediaTypeVideo
|
||||
|
@ -245,142 +387,16 @@ static int avf_read_header(AVFormatContext *s)
|
|||
// Initialize capture session
|
||||
ctx->capture_session = [[AVCaptureSession alloc] init];
|
||||
|
||||
NSError *error = nil;
|
||||
AVCaptureDeviceInput* capture_dev_input = [[[AVCaptureDeviceInput alloc] initWithDevice:video_device error:&error] autorelease];
|
||||
|
||||
if (!capture_dev_input) {
|
||||
av_log(s, AV_LOG_ERROR, "Failed to create AV capture input device: %s\n",
|
||||
[[error localizedDescription] UTF8String]);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!capture_dev_input) {
|
||||
av_log(s, AV_LOG_ERROR, "Failed to add AV capture input device to session: %s\n",
|
||||
[[error localizedDescription] UTF8String]);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ([ctx->capture_session canAddInput:capture_dev_input]) {
|
||||
[ctx->capture_session addInput:capture_dev_input];
|
||||
} else {
|
||||
av_log(s, AV_LOG_ERROR, "can't add video input to capture session\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Attaching output
|
||||
ctx->video_output = [[AVCaptureVideoDataOutput alloc] init];
|
||||
|
||||
if (!ctx->video_output) {
|
||||
av_log(s, AV_LOG_ERROR, "Failed to init AV video output\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// select pixel format
|
||||
struct AVFPixelFormatSpec pxl_fmt_spec;
|
||||
pxl_fmt_spec.ff_id = AV_PIX_FMT_NONE;
|
||||
|
||||
for (int i = 0; avf_pixel_formats[i].ff_id != AV_PIX_FMT_NONE; i++) {
|
||||
if (ctx->pixel_format == avf_pixel_formats[i].ff_id) {
|
||||
pxl_fmt_spec = avf_pixel_formats[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if selected pixel format is supported by AVFoundation
|
||||
if (pxl_fmt_spec.ff_id == AV_PIX_FMT_NONE) {
|
||||
av_log(s, AV_LOG_ERROR, "Selected pixel format (%s) is not supported by AVFoundation.\n",
|
||||
av_get_pix_fmt_name(pxl_fmt_spec.ff_id));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// check if the pixel format is available for this device
|
||||
if ([[ctx->video_output availableVideoCVPixelFormatTypes] indexOfObject:[NSNumber numberWithInt:pxl_fmt_spec.avf_id]] == NSNotFound) {
|
||||
av_log(s, AV_LOG_ERROR, "Selected pixel format (%s) is not supported by the input device.\n",
|
||||
av_get_pix_fmt_name(pxl_fmt_spec.ff_id));
|
||||
|
||||
pxl_fmt_spec.ff_id = AV_PIX_FMT_NONE;
|
||||
|
||||
av_log(s, AV_LOG_ERROR, "Supported pixel formats:\n");
|
||||
for (NSNumber *pxl_fmt in [ctx->video_output availableVideoCVPixelFormatTypes]) {
|
||||
struct AVFPixelFormatSpec pxl_fmt_dummy;
|
||||
pxl_fmt_dummy.ff_id = AV_PIX_FMT_NONE;
|
||||
for (int i = 0; avf_pixel_formats[i].ff_id != AV_PIX_FMT_NONE; i++) {
|
||||
if ([pxl_fmt intValue] == avf_pixel_formats[i].avf_id) {
|
||||
pxl_fmt_dummy = avf_pixel_formats[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pxl_fmt_dummy.ff_id != AV_PIX_FMT_NONE) {
|
||||
av_log(s, AV_LOG_ERROR, " %s\n", av_get_pix_fmt_name(pxl_fmt_dummy.ff_id));
|
||||
|
||||
// select first supported pixel format instead of user selected (or default) pixel format
|
||||
if (pxl_fmt_spec.ff_id == AV_PIX_FMT_NONE) {
|
||||
pxl_fmt_spec = pxl_fmt_dummy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fail if there is no appropriate pixel format or print a warning about overriding the pixel format
|
||||
if (pxl_fmt_spec.ff_id == AV_PIX_FMT_NONE) {
|
||||
goto fail;
|
||||
} else {
|
||||
av_log(s, AV_LOG_WARNING, "Overriding selected pixel format to use %s instead.\n",
|
||||
av_get_pix_fmt_name(pxl_fmt_spec.ff_id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NSNumber *pixel_format = [NSNumber numberWithUnsignedInt:pxl_fmt_spec.avf_id];
|
||||
NSDictionary *capture_dict = [NSDictionary dictionaryWithObject:pixel_format
|
||||
forKey:(id)kCVPixelBufferPixelFormatTypeKey];
|
||||
|
||||
[ctx->video_output setVideoSettings:capture_dict];
|
||||
[ctx->video_output setAlwaysDiscardsLateVideoFrames:YES];
|
||||
|
||||
ctx->avf_delegate = [[AVFFrameReceiver alloc] initWithContext:ctx];
|
||||
|
||||
dispatch_queue_t queue = dispatch_queue_create("avf_queue", NULL);
|
||||
[ctx->video_output setSampleBufferDelegate:ctx->avf_delegate queue:queue];
|
||||
dispatch_release(queue);
|
||||
|
||||
if ([ctx->capture_session canAddOutput:ctx->video_output]) {
|
||||
[ctx->capture_session addOutput:ctx->video_output];
|
||||
} else {
|
||||
av_log(s, AV_LOG_ERROR, "can't add video output to capture session\n");
|
||||
if (add_video_device(s, video_device)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
[ctx->capture_session startRunning];
|
||||
|
||||
// Take stream info from the first frame.
|
||||
while (ctx->frames_captured < 1) {
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES);
|
||||
}
|
||||
|
||||
lock_frames(ctx);
|
||||
|
||||
AVStream* stream = avformat_new_stream(s, NULL);
|
||||
|
||||
if (!stream) {
|
||||
if (get_video_config(s)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
avpriv_set_pts_info(stream, 64, 1, avf_time_base);
|
||||
|
||||
CVImageBufferRef image_buffer = CMSampleBufferGetImageBuffer(ctx->current_frame);
|
||||
CGSize image_buffer_size = CVImageBufferGetEncodedSize(image_buffer);
|
||||
|
||||
stream->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
stream->codec->width = (int)image_buffer_size.width;
|
||||
stream->codec->height = (int)image_buffer_size.height;
|
||||
stream->codec->pix_fmt = pxl_fmt_spec.ff_id;
|
||||
|
||||
CFRelease(ctx->current_frame);
|
||||
ctx->current_frame = nil;
|
||||
|
||||
unlock_frames(ctx);
|
||||
[pool release];
|
||||
return 0;
|
||||
|
||||
|
|
Loading…
Reference in New Issue