#ffmpeg #avfoundation #chromium #pixelformat #fourcc
Вопрос:
Я пытаюсь транслировать видео с веб — камеры на локальный компьютер. Поток имеет разрешение 3840×2160 и 30 кадров в секунду. Компьютер, которым я пользуюсь, — Mac Pro. Однако, когда я запускаю его с помощью следующей команды:
ffmpeg -f avfoundation -framerate 30 -video_size 3840x2160 -pix_fmt nv12 -probesize "50M" -i "0" -pix_fmt nv12 -preset ultrafast -vcodec libx264 -tune zerolatency -f mpegts udp://192.168.1.5:5100/mystream
он имеет задержку в 3-4 секунды. Эта проблема отсутствует в Chromium, при использовании MediaStream API поток отображается в реальном времени.
Я полагаю, что это связано с тем, что в Chromium поддерживается код из четырех символов «dmb1» :
(media::VideoPixelFormat)FourCCToChromiumPixelFormat:(FourCharCode)code { switch (code) { case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: return media::PIXEL_FORMAT_NV12; // Mac fourcc: "420v". case kCVPixelFormatType_422YpCbCr8: return media::PIXEL_FORMAT_UYVY; // Mac fourcc: "2vuy". case kCMPixelFormat_422YpCbCr8_yuvs: return media::PIXEL_FORMAT_YUY2; case kCMVideoCodecType_JPEG_OpenDML: return media::PIXEL_FORMAT_MJPEG; // Mac fourcc: "dmb1". default: return media::PIXEL_FORMAT_UNKNOWN; } }
Для установки формата пикселей Chromium использует следующий фрагмент кода:
NSDictionary* videoSettingsDictionary = @{ (id)kCVPixelBufferWidthKey : @(width), (id)kCVPixelBufferHeightKey : @(height), (id)kCVPixelBufferPixelFormatTypeKey : @(best_fourcc), AVVideoScalingModeKey : AVVideoScalingModeResizeAspectFill }; [_captureVideoDataOutput setVideoSettings:videoSettingsDictionary];
Я попытался сделать то же самое в ffmpeg, изменив файл avfoundation.m. Сначала я добавил новый формат пикселей AV_PIX_FMT_MJPEG
:
static const struct AVFPixelFormatSpec avf_pixel_formats[] = { { AV_PIX_FMT_MONOBLACK, kCVPixelFormatType_1Monochrome }, { AV_PIX_FMT_RGB555BE, kCVPixelFormatType_16BE555 }, { AV_PIX_FMT_RGB555LE, kCVPixelFormatType_16LE555 }, { AV_PIX_FMT_RGB565BE, kCVPixelFormatType_16BE565 }, { AV_PIX_FMT_RGB565LE, kCVPixelFormatType_16LE565 }, { AV_PIX_FMT_RGB24, kCVPixelFormatType_24RGB }, { AV_PIX_FMT_BGR24, kCVPixelFormatType_24BGR }, { AV_PIX_FMT_0RGB, kCVPixelFormatType_32ARGB }, { AV_PIX_FMT_BGR0, kCVPixelFormatType_32BGRA }, { AV_PIX_FMT_0BGR, kCVPixelFormatType_32ABGR }, { AV_PIX_FMT_RGB0, kCVPixelFormatType_32RGBA }, { AV_PIX_FMT_BGR48BE, kCVPixelFormatType_48RGB }, { AV_PIX_FMT_UYVY422, kCVPixelFormatType_422YpCbCr8 }, { AV_PIX_FMT_YUVA444P, kCVPixelFormatType_4444YpCbCrA8R }, { AV_PIX_FMT_YUVA444P16LE, kCVPixelFormatType_4444AYpCbCr16 }, { AV_PIX_FMT_YUV444P, kCVPixelFormatType_444YpCbCr8 }, { AV_PIX_FMT_YUV422P16, kCVPixelFormatType_422YpCbCr16 }, { AV_PIX_FMT_YUV422P10, kCVPixelFormatType_422YpCbCr10 }, { AV_PIX_FMT_YUV444P10, kCVPixelFormatType_444YpCbCr10 }, { AV_PIX_FMT_YUV420P, kCVPixelFormatType_420YpCbCr8Planar }, { AV_PIX_FMT_NV12, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange }, { AV_PIX_FMT_YUYV422, kCVPixelFormatType_422YpCbCr8_yuvs }, { AV_PIX_FMT_MJPEG, kCMVideoCodecType_JPEG_OpenDML }, //dmb1 #if !TARGET_OS_IPHONE amp;amp; __MAC_OS_X_VERSION_MIN_REQUIRED gt;= 1080 { AV_PIX_FMT_GRAY8, kCVPixelFormatType_OneComponent8 }, #endif { AV_PIX_FMT_NONE, 0 } };
After that I tried to hardcode it:
pxl_fmt_spec = avf_pixel_formats[22]; ctx-gt;pixel_format = pxl_fmt_spec.ff_id; pixel_format = [NSNumber numberWithUnsignedInt:pxl_fmt_spec.avf_id]; capture_dict = [NSDictionary dictionaryWithObject:pixel_format forKey:(id)kCVPixelBufferPixelFormatTypeKey]; [ctx-gt;video_output setVideoSettings:capture_dict];
Code compiles and builds successfully, but when I run it with above command, without -pix_fmt specified, program enters infinite loop in get_video_config
function:
while (ctx-gt;frames_captured lt; 1) { CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES); }
Очевидно, что ffmpeg не может загрузить первый кадр. Моя камера более чем способна поддерживать эти форматы пикселей и потоков. Я доказал это с помощью этого фрагмента кода, который появляется после того, как ffmpeg выбирает, какой формат использовать для указанной ширины, высоты и кадров в секунду:
FourCharCode fcc = CMFormatDescriptionGetMediaSubType([selected_format formatDescription]); char fcc_string[5] = { 0, 0, 0, 0, ''}; fcc_string[0] = (char) (fcc gt;gt; 24); fcc_string[1] = (char) (fcc gt;gt; 16); fcc_string[2] = (char) (fcc gt;gt; 8); fcc_string[3] = (char) fcc; av_log(s, AV_LOG_ERROR, "Selected format: %sn", fcc_string);
Приведенный выше код выводит «Выбранный формат: dmb1».
Может кто-нибудь сказать мне, почему ffmpeg не может загрузить первый кадр и как добавить новый формат пикселей в эту библиотеку?
Кроме того, любое предложение о том, как разрешить задержку ввода в 3 секунды каким-либо другим способом, более чем приветствуется.
Редактировать:
Если вы попытаетесь установить любой другой формат пикселей в Chromium, отличный от MJPEG, задержка составит 2 секунды. Когда я говорю «настройка», я имею в виду изменение исходного кода Chromium и его перекомпиляцию. Я почти уверен, что проблема в формате пикселей, потому что камера отправляет dmb1, а ffmpeg не знает об этом формате.
Кроме того, задержка присутствует только в macOS.
Комментарии:
1. AFAIK для добавления нового формата пикселей в ffmpeg требуется отредактировать код ffmpeg и скомпилировать его
2. MJPEG-это кодек, а не пиксельный формат. Вы получаете задержку из-за длины GOP, т. е. интервала ключевых кадров. Добавьте
-g X
в свою команду и замените X небольшим целым числом. 1 для всего внутреннего потока до15
0,5 секунды GOP.3. @404pio Я знаю, что изменения, которые я внес, находятся в исходном коде ffmpeg. @Gyan Я запустил команду
ffmpeg -f avfoundation -framerate 30 -video_size 3840x2160 -pix_fmt nv12 -probesize "50M" -i "0" -pix_fmt nv12 -preset ultrafast -vcodec libx264 -tune zerolatency -g 1 -f mpegts udp://192.168.1.5:5100/mystream
, и задержка ввода все еще была там. Да, MJPEG-это кодек, но в Chromium он рассматривается как пиксельный формат.