Ignore:
Timestamp:
Sep 29, 2016 4:04:22 AM (8 years ago)
Author:
nanang
Message:

Misc (re #1945): Added feature of listing dshow device in ffmpeg video device (experimental).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c

    r5399 r5441  
    9090    pjmedia_vid_dev_param        param; 
    9191    AVFormatContext             *ff_fmt_ctx; 
     92    void                        *frame_buf; 
    9293} ffmpeg_stream; 
    9394 
     
    162163{ 
    163164    PJ_UNUSED_ARG(ptr); 
    164     PJ_UNUSED_ARG(level); 
     165 
     166    /* Custom callback needs to filter log level by itself */ 
     167    if (level > av_log_get_level()) 
     168        return; 
     169 
    165170    vfprintf(stdout, fmt, vl); 
    166171} 
     
    200205#if LIBAVFORMAT_VER_AT_LEAST(53,2) 
    201206    /* Init ffmpeg dictionary */ 
     207    /* 
    202208    snprintf(buf, sizeof(buf), "%d/%d", vfd->fps.num, vfd->fps.denum); 
    203209    av_dict_set(&format_opts, "framerate", buf, 0); 
     
    205211    av_dict_set(&format_opts, "video_size", buf, 0); 
    206212    av_dict_set(&format_opts, "pixel_format", av_get_pix_fmt_name(av_fmt), 0); 
    207  
     213    */ 
    208214    /* Open capture stream */ 
    209215    err = avformat_open_input(ctx, dev_name, ifmt, &format_opts); 
     
    283289} 
    284290 
     291 
     292#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ 
     293    (defined(PJ_WIN64) && PJ_WIN64!=0) 
     294 
     295#ifdef _MSC_VER 
     296#   pragma warning(push, 3) 
     297#endif 
     298 
     299#define COBJMACROS 
     300#include <DShow.h> 
     301#pragma comment(lib, "Strmiids.lib") 
     302 
     303#ifdef _MSC_VER 
     304#   pragma warning(pop) 
     305#endif 
     306 
     307#define MAX_DEV_NAME_LEN 80 
     308 
     309static pj_status_t dshow_enum_devices(unsigned *dev_cnt, 
     310                                      char dev_names[][MAX_DEV_NAME_LEN]) 
     311{ 
     312    unsigned max_cnt = *dev_cnt; 
     313    ICreateDevEnum *dev_enum = NULL; 
     314    IEnumMoniker *enum_cat = NULL; 
     315    IMoniker *moniker = NULL; 
     316    HRESULT hr; 
     317    ULONG fetched; 
     318    unsigned i = 0; 
     319 
     320    CoInitialize(0); 
     321 
     322    *dev_cnt = 0; 
     323    hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, 
     324                          CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum, 
     325                          (void**)&dev_enum); 
     326    if (FAILED(hr) || 
     327        ICreateDevEnum_CreateClassEnumerator(dev_enum, 
     328            &CLSID_VideoInputDeviceCategory, &enum_cat, 0) != S_OK)  
     329    { 
     330        PJ_LOG(4,(THIS_FILE, "Windows found no video input devices")); 
     331        if (dev_enum) 
     332            ICreateDevEnum_Release(dev_enum); 
     333 
     334        return PJ_SUCCESS; 
     335    } 
     336 
     337    while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK && 
     338           *dev_cnt < max_cnt) 
     339    { 
     340        (*dev_cnt)++; 
     341    } 
     342 
     343    if (*dev_cnt == 0) { 
     344        IEnumMoniker_Release(enum_cat); 
     345        ICreateDevEnum_Release(dev_enum); 
     346        return PJ_SUCCESS; 
     347    } 
     348 
     349    IEnumMoniker_Reset(enum_cat); 
     350    while (i < max_cnt && 
     351           IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) 
     352    { 
     353        IPropertyBag *prop_bag; 
     354 
     355        hr = IMoniker_BindToStorage(moniker, 0, 0, &IID_IPropertyBag, 
     356                                    (void**)&prop_bag); 
     357        if (SUCCEEDED(hr)) { 
     358            VARIANT var_name; 
     359 
     360            VariantInit(&var_name); 
     361            hr = IPropertyBag_Read(prop_bag, L"FriendlyName", &var_name, 
     362                                   NULL); 
     363            if (SUCCEEDED(hr) && var_name.bstrVal) { 
     364                char tmp[MAX_DEV_NAME_LEN] = {0}; 
     365                WideCharToMultiByte(CP_ACP, 0, var_name.bstrVal, 
     366                                    (int)wcslen(var_name.bstrVal), 
     367                                    tmp, MAX_DEV_NAME_LEN, NULL, NULL); 
     368                pj_ansi_snprintf(dev_names[i++], MAX_DEV_NAME_LEN, 
     369                                 "video=%s", tmp); 
     370            } 
     371            VariantClear(&var_name); 
     372            IPropertyBag_Release(prop_bag); 
     373        } 
     374        IMoniker_Release(moniker); 
     375    } 
     376 
     377    IEnumMoniker_Release(enum_cat); 
     378    ICreateDevEnum_Release(dev_enum); 
     379 
     380    PJ_LOG(4, (THIS_FILE, "DShow has %d devices:", *dev_cnt)); 
     381    for (i = 0; i < *dev_cnt; ++i) { 
     382        PJ_LOG(4, (THIS_FILE, " %d: %s", (i+1), dev_names[i])); 
     383    } 
     384 
     385    return PJ_SUCCESS; 
     386} 
     387 
     388#endif /* PJ_WIN32 or PJ_WIN64 */ 
     389 
     390 
    285391/* API: refresh the list of devices */ 
    286392static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f) 
     
    288394    ffmpeg_factory *ff = (ffmpeg_factory*)f; 
    289395    AVInputFormat *p; 
    290     ffmpeg_dev_info *info; 
    291396 
    292397    av_log_set_callback(&print_ffmpeg_log); 
    293     av_log_set_level(AV_LOG_DEBUG); 
     398    av_log_set_level(AV_LOG_ERROR); 
    294399 
    295400    if (ff->dev_pool) { 
     
    298403    } 
    299404 
    300     /* TODO: this should enumerate devices, now it enumerates host APIs */ 
    301405    ff->dev_count = 0; 
    302406    ff->dev_pool = pj_pool_create(ff->pf, "ffmpeg_cap_dev", 500, 500, NULL); 
    303407 
    304     p = av_iformat_next(NULL); 
    305     while (p) { 
    306         AVFormatContext *ctx; 
    307         AVCodecContext *codec = NULL; 
    308         pjmedia_format_id fmt_id; 
    309         pj_status_t status; 
    310         unsigned i; 
    311          
     408    /* Iterate host APIs */ 
     409    p = av_input_video_device_next(NULL); 
     410    while (p && ff->dev_count < MAX_DEV_CNT) { 
     411        char dev_names[MAX_DEV_CNT][MAX_DEV_NAME_LEN]; 
     412        unsigned dev_cnt = MAX_DEV_CNT; 
     413        unsigned dev_idx; 
     414 
    312415        if ((p->flags & AVFMT_NOFILE)==0 || p->read_probe) { 
    313416            goto next_format; 
    314417        } 
    315418 
    316         info = &ff->dev_info[ff->dev_count]; 
    317         pj_bzero(info, sizeof(*info)); 
    318         pj_ansi_strncpy(info->base.name, "default",  
    319                         sizeof(info->base.name)); 
    320         pj_ansi_snprintf(info->base.driver, sizeof(info->base.driver), 
    321                          "%s (ffmpeg)", p->name); 
    322         info->base.dir = PJMEDIA_DIR_CAPTURE; 
    323         info->base.has_callback = PJ_FALSE; 
    324  
    325         info->host_api = p; 
    326  
    327419#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ 
    328420    (defined(PJ_WIN64) && PJ_WIN64!=0) 
    329         info->def_devname = "0"; 
     421        if (pj_ansi_strcmp(p->name, "dshow") == 0) { 
     422            dshow_enum_devices(&dev_cnt, dev_names); 
     423        } else if (pj_ansi_strcmp(p->name, "vfwcap") == 0) { 
     424            dev_cnt = 1; 
     425            pj_ansi_snprintf(dev_names[0], MAX_DEV_NAME_LEN, "0"); 
     426        } else { 
     427            dev_cnt = 0; 
     428        } 
    330429#elif defined(PJ_LINUX) && PJ_LINUX!=0 
    331         info->def_devname = "/dev/video0"; 
     430        dev_cnt = 1; 
     431        pj_ansi_snprintf(dev_names[0], MAX_DEV_NAME_LEN, "/dev/video0"); 
     432#else 
     433        dev_cnt = 0; 
    332434#endif 
    333435 
    334         ctx = avformat_alloc_context(); 
    335         if (!ctx || avformat_open_input(&ctx, info->def_devname, p, NULL)!=0) 
    336             goto next_format; 
    337  
    338         for(i = 0; i < ctx->nb_streams; i++) { 
    339             if (ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { 
    340                 codec = ctx->streams[i]->codec; 
    341                 break; 
     436        /* Iterate devices (only DirectShow devices for now) */ 
     437        for (dev_idx = 0; dev_idx < dev_cnt && ff->dev_count < MAX_DEV_CNT; 
     438            ++dev_idx) 
     439        { 
     440            ffmpeg_dev_info *info; 
     441            AVFormatContext *ctx; 
     442            AVCodecContext *codec = NULL; 
     443            pjmedia_format_id fmt_id; 
     444            pj_str_t dev_name; 
     445            pj_status_t status; 
     446            unsigned i; 
     447             
     448            ctx = avformat_alloc_context(); 
     449            if (!ctx || avformat_open_input(&ctx, dev_names[dev_idx], p, NULL)!=0) 
     450                continue; 
     451 
     452            for(i = 0; i < ctx->nb_streams; i++) { 
     453                if (ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { 
     454                    codec = ctx->streams[i]->codec; 
     455                    break; 
     456                } 
    342457            } 
     458            if (!codec) { 
     459                av_close_input_stream(ctx); 
     460                continue; 
     461            } 
     462 
     463            status = PixelFormat_to_pjmedia_format_id(codec->pix_fmt, &fmt_id); 
     464            if (status != PJ_SUCCESS) { 
     465                av_close_input_stream(ctx); 
     466                continue; 
     467            } 
     468 
     469            info = &ff->dev_info[ff->dev_count++]; 
     470            pj_bzero(info, sizeof(*info)); 
     471            pj_ansi_strncpy(info->base.name, "default",  
     472                            sizeof(info->base.name)); 
     473            pj_ansi_snprintf(info->base.driver, sizeof(info->base.driver), 
     474                             "ffmpeg %s", p->name); 
     475             
     476            pj_strdup2_with_null(ff->pool, &dev_name, dev_names[dev_idx]); 
     477            info->def_devname = dev_name.ptr; 
     478            info->base.dir = PJMEDIA_DIR_CAPTURE; 
     479            info->base.has_callback = PJ_FALSE; 
     480 
     481            info->host_api = p; 
     482 
     483            /* Set supported formats */ 
     484            info->base.caps = PJMEDIA_VID_DEV_CAP_FORMAT; 
     485            info->base.fmt_cnt = 1; 
     486            for (i = 0; i < info->base.fmt_cnt; ++i) { 
     487                pjmedia_format *fmt = &info->base.fmt[i]; 
     488                pjmedia_format_init_video(fmt, fmt_id, 
     489                                          codec->width, codec->height, 15, 1); 
     490            } 
     491 
     492            av_close_input_stream(ctx); 
    343493        } 
    344         if (!codec) { 
    345             av_close_input_stream(ctx); 
    346             goto next_format; 
    347         } 
    348  
    349         status = PixelFormat_to_pjmedia_format_id(codec->pix_fmt, &fmt_id); 
    350         if (status != PJ_SUCCESS) { 
    351             av_close_input_stream(ctx); 
    352             goto next_format; 
    353         } 
    354  
    355         /* Set supported formats */ 
    356         info->base.caps = PJMEDIA_VID_DEV_CAP_FORMAT; 
    357         info->base.fmt_cnt = 1; 
    358         for (i = 0; i < info->base.fmt_cnt; ++i) { 
    359             pjmedia_format *fmt = &info->base.fmt[i]; 
    360             pjmedia_format_init_video(fmt, fmt_id, 
    361                                       codec->width, codec->height, 15, 1); 
    362         } 
    363  
    364         av_close_input_stream(ctx); 
    365  
    366         ff->dev_count++; 
    367494 
    368495next_format: 
    369         p = av_iformat_next(p); 
     496        p = av_input_video_device_next(p); 
    370497    } 
    371498 
     
    456583    pj_memcpy(&strm->param, param, sizeof(*param)); 
    457584 
     585    /* Allocate frame buffer */ 
     586    { 
     587        const pjmedia_video_format_info *vfi; 
     588        pjmedia_video_apply_fmt_param vafp; 
     589 
     590        vfi = pjmedia_get_video_format_info(NULL, param->fmt.id); 
     591        if (!vfi) goto on_error; 
     592 
     593        pj_bzero(&vafp, sizeof(vafp)); 
     594        vafp.size = param->fmt.det.vid.size; 
     595        vfi->apply_fmt(vfi, &vafp); 
     596 
     597        strm->frame_buf = pj_pool_alloc(pool, vafp.framebytes); 
     598    } 
     599 
    458600    /* Done */ 
    459601    strm->base.op = &stream_op; 
     
    461603 
    462604    return PJ_SUCCESS; 
     605 
     606on_error: 
     607    pj_pool_release(pool); 
     608    return PJMEDIA_EVID_INVCAP; 
    463609} 
    464610 
     
    532678{ 
    533679    ffmpeg_stream *strm = (ffmpeg_stream*)s; 
    534     AVPacket p; 
     680    AVPacket p = {0}; 
    535681    int err; 
    536682 
     
    543689    pj_bzero(frame, sizeof(*frame)); 
    544690    frame->type = PJMEDIA_FRAME_TYPE_VIDEO; 
    545     frame->buf = p.data; 
     691    frame->buf = strm->frame_buf; 
    546692    frame->size = p.size; 
     693    pj_memcpy(frame->buf, p.data, p.size); 
     694    av_free_packet(&p); 
    547695 
    548696    return PJ_SUCCESS; 
Note: See TracChangeset for help on using the changeset viewer.