Skip to content
Permalink
Browse files

avdevice/dshow: Add support for capturing H264 (FORMAT_UVCH264Video) …

…from webcams

The Logitech C930e webcam (and possibly others) present their H264 capabilities through a KS_H264VIDEOINFO structure (format type FORMAT_UVCH264Video) rather than through the VIDEOINFOHEADER or VIDEOINFOHEADER2 structures used for other formats.
This commit adds support to dshow (dshow.c) to allow these formats to be listed and captured.
The GUIDs required for this along with the KS_H264VIDEOINFO structure are also included (dshow_capture.h) as they do not appear to be in the MingGW headers.

TODO: Add support to choose the H264 profile used. The C930e seems to present both a Constrained Baseline and a High Profile + 0x0C. Currently this is ignored and the first one matching the requested resolution is used.
  • Loading branch information...
ltchuan committed Dec 12, 2018
1 parent 0e833f6 commit b2bebbd97dd67370afb4c832e03c3e450ddb1781
Showing with 175 additions and 60 deletions.
  1. +118 −60 libavdevice/dshow.c
  2. +57 −0 libavdevice/dshow_capture.h
@@ -349,7 +349,10 @@ dshow_cycle_formats(AVFormatContext *avctx, enum dshowDeviceType devtype,
VIDEO_STREAM_CONFIG_CAPS *vcaps = caps;
BITMAPINFOHEADER *bih;
int64_t *fr;
int64_t fr_temp;
const AVCodecTag *const tags[] = { avformat_get_riff_video_tags(), NULL };
int is_uvch264 = 0;
KS_H264VIDEOINFO *v_uvch264;
#if DSHOWDEBUG
ff_print_VIDEO_STREAM_CONFIG_CAPS(vcaps);
#endif
@@ -361,53 +364,95 @@ dshow_cycle_formats(AVFormatContext *avctx, enum dshowDeviceType devtype,
VIDEOINFOHEADER2 *v = (void *) type->pbFormat;
fr = &v->AvgTimePerFrame;
bih = &v->bmiHeader;
} else if (IsEqualGUID(&type->formattype, &FORMAT_UVCH264Video)) {
av_log(avctx, AV_LOG_DEBUG, "FORMAT UVCH264Video found.\n");
is_uvch264 = 1;
v_uvch264 = (void *) type->pbFormat;
fr_temp = v_uvch264->dwFrameInterval;
fr = &fr_temp;
} else {
goto next;
}
if (!pformat_set) {
enum AVPixelFormat pix_fmt = dshow_pixfmt(bih->biCompression, bih->biBitCount);
if (pix_fmt == AV_PIX_FMT_NONE) {
enum AVCodecID codec_id = av_codec_get_id(tags, bih->biCompression);
AVCodec *codec = avcodec_find_decoder(codec_id);
if (codec_id == AV_CODEC_ID_NONE || !codec) {
av_log(avctx, AV_LOG_INFO, " unknown compression type 0x%X", (int) bih->biCompression);
if (!is_uvch264) { // Not UVC H264
enum AVPixelFormat pix_fmt = dshow_pixfmt(bih->biCompression, bih->biBitCount);
if (pix_fmt == AV_PIX_FMT_NONE) {
enum AVCodecID codec_id = av_codec_get_id(tags, bih->biCompression);
AVCodec *codec = avcodec_find_decoder(codec_id);
if (codec_id == AV_CODEC_ID_NONE || !codec) {
av_log(avctx, AV_LOG_INFO, " unknown compression type 0x%X", (int) bih->biCompression);
} else {
av_log(avctx, AV_LOG_INFO, " vcodec=%s", codec->name);
}
} else {
av_log(avctx, AV_LOG_INFO, " pixel_format=%s", av_get_pix_fmt_name(pix_fmt));
}
} else { // UVC H264
if (IsEqualGUID(&type->subtype, &MEDIASUBTYPE_H264)) {
AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
av_log(avctx, AV_LOG_INFO, " vcodec=%s", codec->name);
} else {
av_log(avctx, AV_LOG_INFO, " incompatible H264 subtype");
}
}
if (!IsEqualGUID(&vcaps->guid, &GUID_NULL)) {
av_log(avctx, AV_LOG_INFO, " min s=%ldx%ld fps=%g max s=%ldx%ld fps=%g\n",
vcaps->MinOutputSize.cx, vcaps->MinOutputSize.cy,
1e7 / vcaps->MaxFrameInterval,
vcaps->MaxOutputSize.cx, vcaps->MaxOutputSize.cy,
1e7 / vcaps->MinFrameInterval);
} else {
av_log(avctx, AV_LOG_INFO, " pixel_format=%s", av_get_pix_fmt_name(pix_fmt));
av_log(avctx, AV_LOG_INFO, " s=%dx%d fps=%g\n",
v_uvch264->wWidth, v_uvch264->wHeight,
1e7 / v_uvch264->dwFrameInterval);
}
av_log(avctx, AV_LOG_INFO, " min s=%ldx%ld fps=%g max s=%ldx%ld fps=%g\n",
vcaps->MinOutputSize.cx, vcaps->MinOutputSize.cy,
1e7 / vcaps->MaxFrameInterval,
vcaps->MaxOutputSize.cx, vcaps->MaxOutputSize.cy,
1e7 / vcaps->MinFrameInterval);
continue;
}
if (ctx->video_codec_id != AV_CODEC_ID_RAWVIDEO) {
if (ctx->video_codec_id != av_codec_get_id(tags, bih->biCompression))
if (!is_uvch264) { // Not UVC H264
if (ctx->video_codec_id != AV_CODEC_ID_RAWVIDEO) {
if (ctx->video_codec_id != av_codec_get_id(tags, bih->biCompression))
goto next;
}
if (ctx->pixel_format != AV_PIX_FMT_NONE &&
ctx->pixel_format != dshow_pixfmt(bih->biCompression, bih->biBitCount)) {
goto next;
}
if (ctx->pixel_format != AV_PIX_FMT_NONE &&
ctx->pixel_format != dshow_pixfmt(bih->biCompression, bih->biBitCount)) {
goto next;
}
if (ctx->framerate) {
int64_t framerate = ((int64_t) ctx->requested_framerate.den*10000000)
/ ctx->requested_framerate.num;
if (framerate > vcaps->MaxFrameInterval ||
framerate < vcaps->MinFrameInterval)
}
if (ctx->framerate) {
int64_t framerate = ((int64_t) ctx->requested_framerate.den*10000000)
/ ctx->requested_framerate.num;
if (framerate > vcaps->MaxFrameInterval ||
framerate < vcaps->MinFrameInterval)
goto next;
*fr = framerate;
}
if (ctx->requested_width && ctx->requested_height) {
if (ctx->requested_width > vcaps->MaxOutputSize.cx ||
ctx->requested_width < vcaps->MinOutputSize.cx ||
ctx->requested_height > vcaps->MaxOutputSize.cy ||
ctx->requested_height < vcaps->MinOutputSize.cy)
goto next;
bih->biWidth = ctx->requested_width;
bih->biHeight = ctx->requested_height;
}
} else { // UVC H264
if (ctx->video_codec_id != AV_CODEC_ID_H264) {
goto next;
*fr = framerate;
}
if (ctx->requested_width && ctx->requested_height) {
if (ctx->requested_width > vcaps->MaxOutputSize.cx ||
ctx->requested_width < vcaps->MinOutputSize.cx ||
ctx->requested_height > vcaps->MaxOutputSize.cy ||
ctx->requested_height < vcaps->MinOutputSize.cy)
}
if (!IsEqualGUID(&type->subtype, &MEDIASUBTYPE_H264)) {
goto next;
bih->biWidth = ctx->requested_width;
bih->biHeight = ctx->requested_height;
}
if (ctx->framerate) {
int64_t framerate = ((int64_t) ctx->requested_framerate.den*10000000)
/ ctx->requested_framerate.num;
if (framerate > v_uvch264->dwFrameInterval)
goto next;
*fr = framerate;
}
if (ctx->requested_width && ctx->requested_height) {
if (ctx->requested_width != v_uvch264->wWidth ||
ctx->requested_height != v_uvch264->wHeight)
goto next;
}
}
} else {
AUDIO_STREAM_CONFIG_CAPS *acaps = caps;
@@ -956,6 +1001,8 @@ dshow_add_device(AVFormatContext *avctx,
if (devtype == VideoDevice) {
BITMAPINFOHEADER *bih = NULL;
AVRational time_base;
int is_uvch264 = 0;
KS_H264VIDEOINFO *v_uvch264;

if (IsEqualGUID(&type.formattype, &FORMAT_VideoInfo)) {
VIDEOINFOHEADER *v = (void *) type.pbFormat;
@@ -965,8 +1012,13 @@ dshow_add_device(AVFormatContext *avctx,
VIDEOINFOHEADER2 *v = (void *) type.pbFormat;
time_base = (AVRational) { v->AvgTimePerFrame, 10000000 };
bih = &v->bmiHeader;
} else if (IsEqualGUID(&type.formattype, &FORMAT_UVCH264Video)) {
av_log(avctx, AV_LOG_DEBUG, "FORMAT UVCH264Video found.\n");
is_uvch264 = 1;
v_uvch264 = (void *) type.pbFormat;
time_base = (AVRational) { v_uvch264->dwFrameInterval, 10000000 };
}
if (!bih) {
if ((!bih) && (!is_uvch264)) {
av_log(avctx, AV_LOG_ERROR, "Could not get media type.\n");
goto error;
}
@@ -975,37 +1027,43 @@ dshow_add_device(AVFormatContext *avctx,
st->r_frame_rate = av_inv_q(time_base);

par->codec_type = AVMEDIA_TYPE_VIDEO;
par->width = bih->biWidth;
par->height = bih->biHeight;
par->codec_tag = bih->biCompression;
par->format = dshow_pixfmt(bih->biCompression, bih->biBitCount);
if (bih->biCompression == MKTAG('H', 'D', 'Y', 'C')) {
av_log(avctx, AV_LOG_DEBUG, "attempt to use full range for HDYC...\n");
par->color_range = AVCOL_RANGE_MPEG; // just in case it needs this...
}
if (par->format == AV_PIX_FMT_NONE) {
const AVCodecTag *const tags[] = { avformat_get_riff_video_tags(), NULL };
par->codec_id = av_codec_get_id(tags, bih->biCompression);
if (par->codec_id == AV_CODEC_ID_NONE) {
av_log(avctx, AV_LOG_ERROR, "Unknown compression type. "
"Please report type 0x%X.\n", (int) bih->biCompression);
return AVERROR_PATCHWELCOME;
if (!is_uvch264) { // Not UVC H264
par->width = bih->biWidth;
par->height = bih->biHeight;
par->codec_tag = bih->biCompression;
par->format = dshow_pixfmt(bih->biCompression, bih->biBitCount);
if (bih->biCompression == MKTAG('H', 'D', 'Y', 'C')) {
av_log(avctx, AV_LOG_DEBUG, "attempt to use full range for HDYC...\n");
par->color_range = AVCOL_RANGE_MPEG; // just in case it needs this...
}
par->bits_per_coded_sample = bih->biBitCount;
} else {
par->codec_id = AV_CODEC_ID_RAWVIDEO;
if (bih->biCompression == BI_RGB || bih->biCompression == BI_BITFIELDS) {
if (par->format == AV_PIX_FMT_NONE) {
const AVCodecTag *const tags[] = { avformat_get_riff_video_tags(), NULL };
par->codec_id = av_codec_get_id(tags, bih->biCompression);
if (par->codec_id == AV_CODEC_ID_NONE) {
av_log(avctx, AV_LOG_ERROR, "Unknown compression type. "
"Please report type 0x%X.\n", (int) bih->biCompression);
return AVERROR_PATCHWELCOME;
}
par->bits_per_coded_sample = bih->biBitCount;
if (par->height < 0) {
par->height *= -1;
} else {
par->extradata = av_malloc(9 + AV_INPUT_BUFFER_PADDING_SIZE);
if (par->extradata) {
par->extradata_size = 9;
memcpy(par->extradata, "BottomUp", 9);
} else {
par->codec_id = AV_CODEC_ID_RAWVIDEO;
if (bih->biCompression == BI_RGB || bih->biCompression == BI_BITFIELDS) {
par->bits_per_coded_sample = bih->biBitCount;
if (par->height < 0) {
par->height *= -1;
} else {
par->extradata = av_malloc(9 + AV_INPUT_BUFFER_PADDING_SIZE);
if (par->extradata) {
par->extradata_size = 9;
memcpy(par->extradata, "BottomUp", 9);
}
}
}
}
} else { // UVC H264
par->width = v_uvch264->wWidth;
par->height = v_uvch264->wHeight;
par->codec_id = AV_CODEC_ID_H264;
}
} else {
WAVEFORMATEX *fx = NULL;
@@ -35,6 +35,63 @@

#include "libavcodec/internal.h"

#include <initguid.h>

/* GUIDs and KS_H264VIDEOINFO structure required for H264 UVC capture
are not missing from MinGW headers. */
#ifndef OUR_GUID_ENTRY
#define OUR_GUID_ENTRY(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8);
#endif

OUR_GUID_ENTRY(FORMAT_UVCH264Video,
0x2017be05,0x6629,0x4248,0xaa,0xed,0x7e,0x1a,0x47,0xbc,0x9b,0x9c)

OUR_GUID_ENTRY(MEDIASUBTYPE_H264,
0x34363248,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71)

typedef struct tagKS_H264VIDEOINFO {
WORD wWidth;
WORD wHeight;
WORD wSARwidth;
WORD wSARheight;
WORD wProfile;
BYTE bLevelIDC;
WORD wConstrainedToolset;
DWORD bmSupportedUsages;
WORD bmCapabilities;
DWORD bmSVCCapabilities;
DWORD bmMVCCapabilities;
DWORD dwFrameInterval;
BYTE bMaxCodecConfigDelay;
BYTE bmSupportedSliceModes;
BYTE bmSupportedSyncFrameTypes;
BYTE bResolutionScaling;
BYTE bSimulcastSupport;
BYTE bmSupportedRateControlModes;
WORD wMaxMBperSecOneResolutionNoScalability;
WORD wMaxMBperSecTwoResolutionsNoScalability;
WORD wMaxMBperSecThreeResolutionsNoScalability;
WORD wMaxMBperSecFourResolutionsNoScalability;
WORD wMaxMBperSecOneResolutionTemporalScalability;
WORD wMaxMBperSecTwoResolutionsTemporalScalablility;
WORD wMaxMBperSecThreeResolutionsTemporalScalability;
WORD wMaxMBperSecFourResolutionsTemporalScalability;
WORD wMaxMBperSecOneResolutionTemporalQualityScalability;
WORD wMaxMBperSecTwoResolutionsTemporalQualityScalability;
WORD wMaxMBperSecThreeResolutionsTemporalQualityScalablity;
WORD wMaxMBperSecFourResolutionsTemporalQualityScalability;
WORD wMaxMBperSecOneResolutionTemporalSpatialScalability;
WORD wMaxMBperSecTwoResolutionsTemporalSpatialScalability;
WORD wMaxMBperSecThreeResolutionsTemporalSpatialScalablity;
WORD wMaxMBperSecFourResolutionsTemporalSpatialScalability;
WORD wMaxMBperSecOneResolutionFullScalability;
WORD wMaxMBperSecTwoResolutionsFullScalability;
WORD wMaxMBperSecThreeResolutionsFullScalability;
WORD wMaxMBperSecFourResolutionsFullScalability;
} KS_H264VIDEOINFO,*PKS_H264VIDEOINFO;

#undef OUR_GUID_ENTRY

/* EC_DEVICE_LOST is not defined in MinGW dshow headers. */
#ifndef EC_DEVICE_LOST
#define EC_DEVICE_LOST 0x1f

0 comments on commit b2bebbd

Please sign in to comment.
You can’t perform that action at this time.