Ignore:
Timestamp:
Dec 28, 2016 3:40:07 AM (7 years ago)
Author:
nanang
Message:

Re #1900: More merged from trunk (r5512 mistakenly contains merged changes in third-party dir only).

Location:
pjproject/branches/projects/uwp
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • pjproject/branches/projects/uwp

  • pjproject/branches/projects/uwp/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c

    r4722 r5513  
    4040 
    4141#if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \ 
     42    defined(PJMEDIA_HAS_LIBAVDEVICE) && PJMEDIA_HAS_LIBAVDEVICE != 0 && \ 
    4243    defined(PJMEDIA_VIDEO_DEV_HAS_FFMPEG) && PJMEDIA_VIDEO_DEV_HAS_FFMPEG != 0 
    4344 
    4445 
    4546#define THIS_FILE               "ffmpeg.c" 
     47 
     48#define LIBAVFORMAT_VER_AT_LEAST(major,minor)  (LIBAVFORMAT_VERSION_MAJOR > major || \ 
     49                                               (LIBAVFORMAT_VERSION_MAJOR == major && \ 
     50                                                LIBAVFORMAT_VERSION_MINOR >= minor)) 
    4651 
    4752#include "../pjmedia/ffmpeg_util.h" 
    4853#include <libavdevice/avdevice.h> 
    4954#include <libavformat/avformat.h> 
     55#if LIBAVFORMAT_VER_AT_LEAST(53,2) 
     56# include <libavutil/pixdesc.h> 
     57#endif 
    5058 
    5159#define MAX_DEV_CNT     8 
     60 
     61#ifndef PJMEDIA_USE_OLD_FFMPEG 
     62#  define av_close_input_stream(ctx) avformat_close_input(&ctx) 
     63#endif 
     64 
    5265 
    5366typedef struct ffmpeg_dev_info 
     
    7790    pjmedia_vid_dev_param        param; 
    7891    AVFormatContext             *ff_fmt_ctx; 
     92    void                        *frame_buf; 
    7993} ffmpeg_stream; 
    8094 
     
    149163{ 
    150164    PJ_UNUSED_ARG(ptr); 
    151     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 
    152170    vfprintf(stdout, fmt, vl); 
    153171} 
     
    159177                                       const pjmedia_vid_dev_param *param) 
    160178{ 
     179#if LIBAVFORMAT_VER_AT_LEAST(53,2) 
     180    AVDictionary *format_opts = NULL; 
     181    char buf[128]; 
     182    enum AVPixelFormat av_fmt; 
     183#else 
    161184    AVFormatParameters fp; 
     185#endif 
    162186    pjmedia_video_format_detail *vfd; 
     187    pj_status_t status; 
    163188    int err; 
    164189 
     
    167192                     PJ_EINVAL); 
    168193 
     194    status = pjmedia_format_id_to_PixelFormat(param->fmt.id, &av_fmt); 
     195    if (status != PJ_SUCCESS) { 
     196        avformat_free_context(*ctx); 
     197        return status; 
     198    } 
     199 
    169200    vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE); 
    170201 
     
    172203    *ctx = avformat_alloc_context(); 
    173204 
     205#if LIBAVFORMAT_VER_AT_LEAST(53,2) 
     206    /* Init ffmpeg dictionary */ 
     207    /* 
     208    snprintf(buf, sizeof(buf), "%d/%d", vfd->fps.num, vfd->fps.denum); 
     209    av_dict_set(&format_opts, "framerate", buf, 0); 
     210    snprintf(buf, sizeof(buf), "%dx%d", vfd->size.w, vfd->size.h); 
     211    av_dict_set(&format_opts, "video_size", buf, 0); 
     212    av_dict_set(&format_opts, "pixel_format", av_get_pix_fmt_name(av_fmt), 0); 
     213    */ 
     214    /* Open capture stream */ 
     215    err = avformat_open_input(ctx, dev_name, ifmt, &format_opts); 
     216#else 
    174217    /* Init ffmpeg format param */ 
    175218    pj_bzero(&fp, sizeof(fp)); 
     
    177220    fp.width = vfd->size.w; 
    178221    fp.height = vfd->size.h; 
    179     fp.pix_fmt = PIX_FMT_BGR24; 
     222    fp.pix_fmt = av_fmt; 
    180223    fp.time_base.num = vfd->fps.denum; 
    181224    fp.time_base.den = vfd->fps.num; 
     
    183226    /* Open capture stream */ 
    184227    err = av_open_input_stream(ctx, NULL, dev_name, ifmt, &fp); 
     228#endif 
    185229    if (err < 0) { 
    186230        *ctx = NULL; /* ffmpeg freed its states on failure, do we must too */ 
     
    245289} 
    246290 
     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 
    247391/* API: refresh the list of devices */ 
    248392static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f) 
     
    250394    ffmpeg_factory *ff = (ffmpeg_factory*)f; 
    251395    AVInputFormat *p; 
    252     ffmpeg_dev_info *info; 
    253396 
    254397    av_log_set_callback(&print_ffmpeg_log); 
    255     av_log_set_level(AV_LOG_DEBUG); 
     398    av_log_set_level(AV_LOG_ERROR); 
    256399 
    257400    if (ff->dev_pool) { 
     
    260403    } 
    261404 
    262     /* TODO: this should enumerate devices, now it enumerates host APIs */ 
    263405    ff->dev_count = 0; 
    264406    ff->dev_pool = pj_pool_create(ff->pf, "ffmpeg_cap_dev", 500, 500, NULL); 
    265407 
    266     p = av_iformat_next(NULL); 
    267     while (p) { 
    268         if (p->flags & AVFMT_NOFILE) { 
    269             unsigned i; 
    270  
    271             info = &ff->dev_info[ff->dev_count++]; 
    272             pj_bzero(info, sizeof(*info)); 
    273             pj_ansi_strncpy(info->base.name, "default",  
    274                             sizeof(info->base.name)); 
    275             pj_ansi_snprintf(info->base.driver, sizeof(info->base.driver), 
    276                              "%s (ffmpeg)", p->name); 
    277             info->base.dir = PJMEDIA_DIR_CAPTURE; 
    278             info->base.has_callback = PJ_FALSE; 
    279  
    280             info->host_api = p; 
     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 
     415        if ((p->flags & AVFMT_NOFILE)==0 || p->read_probe) { 
     416            goto next_format; 
     417        } 
    281418 
    282419#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ 
    283420    (defined(PJ_WIN64) && PJ_WIN64!=0) 
    284             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        } 
    285429#elif defined(PJ_LINUX) && PJ_LINUX!=0 
    286             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; 
    287434#endif 
    288435 
    289             /* Set supported formats, currently hardcoded to RGB24 only */ 
    290             info->base.caps = PJMEDIA_VID_DEV_CAP_FORMAT; 
    291             info->base.fmt_cnt = 1; 
    292             for (i = 0; i < info->base.fmt_cnt; ++i) { 
    293                 pjmedia_format *fmt = &info->base.fmt[i]; 
    294  
    295                 fmt->id = PJMEDIA_FORMAT_RGB24; 
    296                 fmt->type = PJMEDIA_TYPE_VIDEO; 
    297                 fmt->detail_type = PJMEDIA_FORMAT_DETAIL_NONE; 
    298             } 
    299         } 
    300         p = av_iformat_next(p); 
     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                } 
     457            } 
     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); 
     493        } 
     494 
     495next_format: 
     496        p = av_input_video_device_next(p); 
    301497    } 
    302498 
     
    387583    pj_memcpy(&strm->param, param, sizeof(*param)); 
    388584 
     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 
    389600    /* Done */ 
    390601    strm->base.op = &stream_op; 
     
    392603 
    393604    return PJ_SUCCESS; 
     605 
     606on_error: 
     607    pj_pool_release(pool); 
     608    return PJMEDIA_EVID_INVCAP; 
    394609} 
    395610 
     
    463678{ 
    464679    ffmpeg_stream *strm = (ffmpeg_stream*)s; 
    465     AVPacket p; 
     680    AVPacket p = {0}; 
    466681    int err; 
    467682 
     
    474689    pj_bzero(frame, sizeof(*frame)); 
    475690    frame->type = PJMEDIA_FRAME_TYPE_VIDEO; 
    476     frame->buf = p.data; 
     691    frame->buf = strm->frame_buf; 
    477692    frame->size = p.size; 
     693    pj_memcpy(frame->buf, p.data, p.size); 
     694    av_free_packet(&p); 
    478695 
    479696    return PJ_SUCCESS; 
Note: See TracChangeset for help on using the changeset viewer.