summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2016-09-29 04:04:22 +0000
committerNanang Izzuddin <nanang@teluu.com>2016-09-29 04:04:22 +0000
commite02afb3d542fb7df6f1e7af7a31e3a64dee0a8a5 (patch)
treebe0d6095da8a08643813283b63c22b453fbf80c6
parentcc7c55b8595b8128dee3ec5d4a6edbf08c134cf7 (diff)
Misc (re #1945): Added feature of listing dshow device in ffmpeg video device (experimental).
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@5441 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/src/pjmedia-videodev/ffmpeg_dev.c262
-rw-r--r--pjsip/src/pjsua2/endpoint.cpp2
2 files changed, 206 insertions, 58 deletions
diff --git a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
index e506b2c5..30c267d4 100644
--- a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
+++ b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
@@ -89,6 +89,7 @@ typedef struct ffmpeg_stream
pj_pool_t *pool;
pjmedia_vid_dev_param param;
AVFormatContext *ff_fmt_ctx;
+ void *frame_buf;
} ffmpeg_stream;
@@ -161,7 +162,11 @@ static void print_ffmpeg_err(int err)
static void print_ffmpeg_log(void* ptr, int level, const char* fmt, va_list vl)
{
PJ_UNUSED_ARG(ptr);
- PJ_UNUSED_ARG(level);
+
+ /* Custom callback needs to filter log level by itself */
+ if (level > av_log_get_level())
+ return;
+
vfprintf(stdout, fmt, vl);
}
@@ -199,12 +204,13 @@ static pj_status_t ffmpeg_capture_open(AVFormatContext **ctx,
#if LIBAVFORMAT_VER_AT_LEAST(53,2)
/* Init ffmpeg dictionary */
+ /*
snprintf(buf, sizeof(buf), "%d/%d", vfd->fps.num, vfd->fps.denum);
av_dict_set(&format_opts, "framerate", buf, 0);
snprintf(buf, sizeof(buf), "%dx%d", vfd->size.w, vfd->size.h);
av_dict_set(&format_opts, "video_size", buf, 0);
av_dict_set(&format_opts, "pixel_format", av_get_pix_fmt_name(av_fmt), 0);
-
+ */
/* Open capture stream */
err = avformat_open_input(ctx, dev_name, ifmt, &format_opts);
#else
@@ -282,91 +288,212 @@ static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f)
return PJ_SUCCESS;
}
+
+#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
+ (defined(PJ_WIN64) && PJ_WIN64!=0)
+
+#ifdef _MSC_VER
+# pragma warning(push, 3)
+#endif
+
+#define COBJMACROS
+#include <DShow.h>
+#pragma comment(lib, "Strmiids.lib")
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+#define MAX_DEV_NAME_LEN 80
+
+static pj_status_t dshow_enum_devices(unsigned *dev_cnt,
+ char dev_names[][MAX_DEV_NAME_LEN])
+{
+ unsigned max_cnt = *dev_cnt;
+ ICreateDevEnum *dev_enum = NULL;
+ IEnumMoniker *enum_cat = NULL;
+ IMoniker *moniker = NULL;
+ HRESULT hr;
+ ULONG fetched;
+ unsigned i = 0;
+
+ CoInitialize(0);
+
+ *dev_cnt = 0;
+ hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL,
+ CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
+ (void**)&dev_enum);
+ if (FAILED(hr) ||
+ ICreateDevEnum_CreateClassEnumerator(dev_enum,
+ &CLSID_VideoInputDeviceCategory, &enum_cat, 0) != S_OK)
+ {
+ PJ_LOG(4,(THIS_FILE, "Windows found no video input devices"));
+ if (dev_enum)
+ ICreateDevEnum_Release(dev_enum);
+
+ return PJ_SUCCESS;
+ }
+
+ while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK &&
+ *dev_cnt < max_cnt)
+ {
+ (*dev_cnt)++;
+ }
+
+ if (*dev_cnt == 0) {
+ IEnumMoniker_Release(enum_cat);
+ ICreateDevEnum_Release(dev_enum);
+ return PJ_SUCCESS;
+ }
+
+ IEnumMoniker_Reset(enum_cat);
+ while (i < max_cnt &&
+ IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK)
+ {
+ IPropertyBag *prop_bag;
+
+ hr = IMoniker_BindToStorage(moniker, 0, 0, &IID_IPropertyBag,
+ (void**)&prop_bag);
+ if (SUCCEEDED(hr)) {
+ VARIANT var_name;
+
+ VariantInit(&var_name);
+ hr = IPropertyBag_Read(prop_bag, L"FriendlyName", &var_name,
+ NULL);
+ if (SUCCEEDED(hr) && var_name.bstrVal) {
+ char tmp[MAX_DEV_NAME_LEN] = {0};
+ WideCharToMultiByte(CP_ACP, 0, var_name.bstrVal,
+ (int)wcslen(var_name.bstrVal),
+ tmp, MAX_DEV_NAME_LEN, NULL, NULL);
+ pj_ansi_snprintf(dev_names[i++], MAX_DEV_NAME_LEN,
+ "video=%s", tmp);
+ }
+ VariantClear(&var_name);
+ IPropertyBag_Release(prop_bag);
+ }
+ IMoniker_Release(moniker);
+ }
+
+ IEnumMoniker_Release(enum_cat);
+ ICreateDevEnum_Release(dev_enum);
+
+ PJ_LOG(4, (THIS_FILE, "DShow has %d devices:", *dev_cnt));
+ for (i = 0; i < *dev_cnt; ++i) {
+ PJ_LOG(4, (THIS_FILE, " %d: %s", (i+1), dev_names[i]));
+ }
+
+ return PJ_SUCCESS;
+}
+
+#endif /* PJ_WIN32 or PJ_WIN64 */
+
+
/* API: refresh the list of devices */
static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f)
{
ffmpeg_factory *ff = (ffmpeg_factory*)f;
AVInputFormat *p;
- ffmpeg_dev_info *info;
av_log_set_callback(&print_ffmpeg_log);
- av_log_set_level(AV_LOG_DEBUG);
+ av_log_set_level(AV_LOG_ERROR);
if (ff->dev_pool) {
pj_pool_release(ff->dev_pool);
ff->dev_pool = NULL;
}
- /* TODO: this should enumerate devices, now it enumerates host APIs */
ff->dev_count = 0;
ff->dev_pool = pj_pool_create(ff->pf, "ffmpeg_cap_dev", 500, 500, NULL);
- p = av_iformat_next(NULL);
- while (p) {
- AVFormatContext *ctx;
- AVCodecContext *codec = NULL;
- pjmedia_format_id fmt_id;
- pj_status_t status;
- unsigned i;
-
+ /* Iterate host APIs */
+ p = av_input_video_device_next(NULL);
+ while (p && ff->dev_count < MAX_DEV_CNT) {
+ char dev_names[MAX_DEV_CNT][MAX_DEV_NAME_LEN];
+ unsigned dev_cnt = MAX_DEV_CNT;
+ unsigned dev_idx;
+
if ((p->flags & AVFMT_NOFILE)==0 || p->read_probe) {
goto next_format;
}
- info = &ff->dev_info[ff->dev_count];
- pj_bzero(info, sizeof(*info));
- pj_ansi_strncpy(info->base.name, "default",
- sizeof(info->base.name));
- pj_ansi_snprintf(info->base.driver, sizeof(info->base.driver),
- "%s (ffmpeg)", p->name);
- info->base.dir = PJMEDIA_DIR_CAPTURE;
- info->base.has_callback = PJ_FALSE;
-
- info->host_api = p;
-
#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
(defined(PJ_WIN64) && PJ_WIN64!=0)
- info->def_devname = "0";
+ if (pj_ansi_strcmp(p->name, "dshow") == 0) {
+ dshow_enum_devices(&dev_cnt, dev_names);
+ } else if (pj_ansi_strcmp(p->name, "vfwcap") == 0) {
+ dev_cnt = 1;
+ pj_ansi_snprintf(dev_names[0], MAX_DEV_NAME_LEN, "0");
+ } else {
+ dev_cnt = 0;
+ }
#elif defined(PJ_LINUX) && PJ_LINUX!=0
- info->def_devname = "/dev/video0";
+ dev_cnt = 1;
+ pj_ansi_snprintf(dev_names[0], MAX_DEV_NAME_LEN, "/dev/video0");
+#else
+ dev_cnt = 0;
#endif
- ctx = avformat_alloc_context();
- if (!ctx || avformat_open_input(&ctx, info->def_devname, p, NULL)!=0)
- goto next_format;
+ /* Iterate devices (only DirectShow devices for now) */
+ for (dev_idx = 0; dev_idx < dev_cnt && ff->dev_count < MAX_DEV_CNT;
+ ++dev_idx)
+ {
+ ffmpeg_dev_info *info;
+ AVFormatContext *ctx;
+ AVCodecContext *codec = NULL;
+ pjmedia_format_id fmt_id;
+ pj_str_t dev_name;
+ pj_status_t status;
+ unsigned i;
+
+ ctx = avformat_alloc_context();
+ if (!ctx || avformat_open_input(&ctx, dev_names[dev_idx], p, NULL)!=0)
+ continue;
+
+ for(i = 0; i < ctx->nb_streams; i++) {
+ if (ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ codec = ctx->streams[i]->codec;
+ break;
+ }
+ }
+ if (!codec) {
+ av_close_input_stream(ctx);
+ continue;
+ }
- for(i = 0; i < ctx->nb_streams; i++) {
- if (ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
- codec = ctx->streams[i]->codec;
- break;
+ status = PixelFormat_to_pjmedia_format_id(codec->pix_fmt, &fmt_id);
+ if (status != PJ_SUCCESS) {
+ av_close_input_stream(ctx);
+ continue;
}
- }
- if (!codec) {
- av_close_input_stream(ctx);
- goto next_format;
- }
- status = PixelFormat_to_pjmedia_format_id(codec->pix_fmt, &fmt_id);
- if (status != PJ_SUCCESS) {
- av_close_input_stream(ctx);
- goto next_format;
- }
+ info = &ff->dev_info[ff->dev_count++];
+ pj_bzero(info, sizeof(*info));
+ pj_ansi_strncpy(info->base.name, "default",
+ sizeof(info->base.name));
+ pj_ansi_snprintf(info->base.driver, sizeof(info->base.driver),
+ "ffmpeg %s", p->name);
+
+ pj_strdup2_with_null(ff->pool, &dev_name, dev_names[dev_idx]);
+ info->def_devname = dev_name.ptr;
+ info->base.dir = PJMEDIA_DIR_CAPTURE;
+ info->base.has_callback = PJ_FALSE;
+
+ info->host_api = p;
+
+ /* Set supported formats */
+ info->base.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+ info->base.fmt_cnt = 1;
+ for (i = 0; i < info->base.fmt_cnt; ++i) {
+ pjmedia_format *fmt = &info->base.fmt[i];
+ pjmedia_format_init_video(fmt, fmt_id,
+ codec->width, codec->height, 15, 1);
+ }
- /* Set supported formats */
- info->base.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
- info->base.fmt_cnt = 1;
- for (i = 0; i < info->base.fmt_cnt; ++i) {
- pjmedia_format *fmt = &info->base.fmt[i];
- pjmedia_format_init_video(fmt, fmt_id,
- codec->width, codec->height, 15, 1);
+ av_close_input_stream(ctx);
}
- av_close_input_stream(ctx);
-
- ff->dev_count++;
-
next_format:
- p = av_iformat_next(p);
+ p = av_input_video_device_next(p);
}
return PJ_SUCCESS;
@@ -455,11 +582,30 @@ static pj_status_t ffmpeg_factory_create_stream(
strm->pool = pool;
pj_memcpy(&strm->param, param, sizeof(*param));
+ /* Allocate frame buffer */
+ {
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_apply_fmt_param vafp;
+
+ vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
+ if (!vfi) goto on_error;
+
+ pj_bzero(&vafp, sizeof(vafp));
+ vafp.size = param->fmt.det.vid.size;
+ vfi->apply_fmt(vfi, &vafp);
+
+ strm->frame_buf = pj_pool_alloc(pool, vafp.framebytes);
+ }
+
/* Done */
strm->base.op = &stream_op;
*p_vid_strm = &strm->base;
return PJ_SUCCESS;
+
+on_error:
+ pj_pool_release(pool);
+ return PJMEDIA_EVID_INVCAP;
}
/* API: Get stream info. */
@@ -531,7 +677,7 @@ static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_dev_stream *s,
pjmedia_frame *frame)
{
ffmpeg_stream *strm = (ffmpeg_stream*)s;
- AVPacket p;
+ AVPacket p = {0};
int err;
err = av_read_frame(strm->ff_fmt_ctx, &p);
@@ -542,8 +688,10 @@ static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_dev_stream *s,
pj_bzero(frame, sizeof(*frame));
frame->type = PJMEDIA_FRAME_TYPE_VIDEO;
- frame->buf = p.data;
+ frame->buf = strm->frame_buf;
frame->size = p.size;
+ pj_memcpy(frame->buf, p.data, p.size);
+ av_free_packet(&p);
return PJ_SUCCESS;
}
diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp
index bd984649..2e94b25c 100644
--- a/pjsip/src/pjsua2/endpoint.cpp
+++ b/pjsip/src/pjsua2/endpoint.cpp
@@ -490,7 +490,7 @@ void Endpoint::logFunc(int level, const char *data, int len)
LogEntry entry;
entry.level = level;
entry.msg = string(data, len);
- entry.threadId = (long)pj_thread_this();
+ entry.threadId = (long)(size_t)pj_thread_this();
entry.threadName = string(pj_thread_get_name(pj_thread_this()));
ep.utilLogWrite(entry);