Changeset 5026


Ignore:
Timestamp:
Mar 26, 2015 4:14:20 AM (4 years ago)
Author:
riza
Message:

Re #1831: Implement algorithm in video port to find the best video format between the requested and supported format.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/src/pjmedia/vid_port.c

    r5012 r5026  
    2626#include <pj/pool.h> 
    2727 
    28  
    2928#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) 
    3029 
     
    3231#define SIGNATURE       PJMEDIA_SIG_VID_PORT 
    3332#define THIS_FILE       "vid_port.c" 
     33 
     34 
     35/** 
     36 * Enable this to trace the format matching process. 
     37 */ 
     38#if 0 
     39#  define TRACE_FIND_FMT(args)      PJ_LOG(5,args) 
     40#else 
     41#  define TRACE_FIND_FMT(args) 
     42#endif 
     43 
     44/** 
     45 * We use nearest width and aspect ratio to find match between the requested  
     46 * format and the supported format. Specify this to determine the array size  
     47 * of the supported formats with the nearest width. From this array, we will  
     48 * find the one with lowest diff_ratio. Setting this to 1 will thus skip  
     49 * the aspect ratio calculation.  
     50 */ 
     51#ifndef PJMEDIA_VID_PORT_MATCH_WIDTH_ARRAY_SIZE 
     52#   define PJMEDIA_VID_PORT_MATCH_WIDTH_ARRAY_SIZE 3 
     53#endif 
    3454 
    3555typedef struct vid_pasv_port vid_pasv_port; 
     
    4060    ROLE_ACTIVE, 
    4161    ROLE_PASSIVE 
     62}; 
     63 
     64enum fmt_match 
     65{ 
     66    FMT_MATCH, 
     67    FMT_SAME_COLOR_SPACE, 
     68    FMT_DIFF_COLOR_SPACE 
    4269}; 
    4370 
     
    89116}; 
    90117 
     118struct fmt_prop  
     119{ 
     120    pj_uint32_t id; 
     121    pjmedia_rect_size size; 
     122    pjmedia_ratio fps; 
     123}; 
     124 
    91125static pj_status_t vidstream_cap_cb(pjmedia_vid_dev_stream *stream, 
    92126                                    void *user_data, 
     
    182216 
    183217    return PJ_SUCCESS; 
     218}          
     219 
     220static pj_uint32_t match_format_id(pj_uint32_t req_id, 
     221                                   pj_uint32_t sup_id) 
     222{ 
     223    const pjmedia_video_format_info *req_fmt_info, *sup_fmt_info; 
     224 
     225    if (req_id == sup_id) 
     226        return FMT_MATCH; 
     227 
     228    req_fmt_info = pjmedia_get_video_format_info(  
     229                                        pjmedia_video_format_mgr_instance(), 
     230                                        req_id); 
     231 
     232    sup_fmt_info = pjmedia_get_video_format_info(  
     233                                        pjmedia_video_format_mgr_instance(), 
     234                                        sup_id); 
     235 
     236    if (req_fmt_info->color_model == sup_fmt_info->color_model) { 
     237        return FMT_SAME_COLOR_SPACE; 
     238    } 
     239 
     240    return FMT_DIFF_COLOR_SPACE; 
     241} 
     242 
     243static pj_uint32_t get_match_format_id(pj_uint32_t req_fmt_id, 
     244                                       pjmedia_vid_dev_info *di) 
     245{ 
     246    unsigned i = 0, match_idx = 0, match_fmt = FMT_DIFF_COLOR_SPACE+1; 
     247 
     248    /* Find the matching format. If no exact match is found, find  
     249     * the supported format with the same color space. If no match is found, 
     250     * use the first supported format on the list. 
     251     */ 
     252    for (i; i < di->fmt_cnt; ++i) { 
     253        unsigned tmp_fmt = match_format_id(req_fmt_id, di->fmt[i].id); 
     254 
     255        if (match_fmt == FMT_MATCH) 
     256            return req_fmt_id; 
     257 
     258        if (tmp_fmt < match_fmt) { 
     259            match_idx = i; 
     260            match_fmt = tmp_fmt; 
     261        } 
     262    } 
     263    return di->fmt[match_idx].id; 
     264} 
     265 
     266/** 
     267 * Find the closest supported format from the specific requested format. 
     268 * The algo is to find a supported size with the matching format id, width and 
     269 * lowest diff_ratio. 
     270 * --- 
     271 * For format id matching, the priority is: 
     272 * 1. Find exact match 
     273 * 2. Find format with the same color space 
     274 * 3. Use the first supported format.  
     275 * --- 
     276 * For ratio matching: 
     277 * Find the lowest difference of the aspect ratio between the requested and 
     278 * the supported format. 
     279 */ 
     280static struct fmt_prop find_closest_fmt(pj_uint32_t req_fmt_id, 
     281                                        pjmedia_rect_size *req_fmt_size, 
     282                                        pjmedia_ratio *req_fmt_fps, 
     283                                        pjmedia_vid_dev_info *di) 
     284{ 
     285    unsigned i, match_idx = 0; 
     286    pj_uint32_t match_fmt_id;      
     287    float req_ratio, min_diff_ratio = 0.0;     
     288    struct fmt_prop ret_prop; 
     289    pj_bool_t found_exact_match = PJ_FALSE; 
     290 
     291    #define     GET_DIFF(x, y)  ((x) > (y)? (x-y) : (y-x)) 
     292     
     293    /* This will contain the supported format with lowest width difference */ 
     294    pjmedia_rect_size nearest_width[PJMEDIA_VID_PORT_MATCH_WIDTH_ARRAY_SIZE]; 
     295 
     296    /* Initialize the list. */ 
     297    for (i=0;i<PJMEDIA_VID_PORT_MATCH_WIDTH_ARRAY_SIZE;++i) { 
     298        nearest_width[i].w = 0xFFFFFFFF; 
     299        nearest_width[i].h = 0; 
     300    } 
     301 
     302    /* Get the matching format id. We assume each format will support all  
     303     * image size.  
     304     */ 
     305    match_fmt_id = get_match_format_id(req_fmt_id, di); 
     306     
     307    /* Search from the supported format, the smallest diff width. Stop the  
     308     * search if exact match is found. 
     309     */ 
     310    for (i=0;i<di->fmt_cnt;++i) { 
     311        pjmedia_video_format_detail *vfd; 
     312        unsigned diff_width1, diff_width2; 
     313 
     314        /* Ignore supported format with different format id. */ 
     315        if (di->fmt[i].id != match_fmt_id) 
     316            continue; 
     317 
     318        vfd = pjmedia_format_get_video_format_detail(&di->fmt[i], PJ_TRUE); 
     319 
     320        /* Exact match found. */ 
     321        if ((vfd->size.w == req_fmt_size->w) &&  
     322            (vfd->size.h == req_fmt_size->h))  
     323        { 
     324            nearest_width[0] = vfd->size; 
     325            found_exact_match = PJ_TRUE; 
     326            break; 
     327        } 
     328 
     329        diff_width1 =  GET_DIFF(vfd->size.w, req_fmt_size->w); 
     330        diff_width2 =  GET_DIFF(nearest_width[0].w, req_fmt_size->w); 
     331 
     332        /* Fill the nearest width list. */ 
     333        if (diff_width1 <= diff_width2) { 
     334            int k = 1; 
     335            pjmedia_rect_size tmp_size = vfd->size;          
     336 
     337            while(((GET_DIFF(tmp_size.w, req_fmt_size->w) < 
     338                   (GET_DIFF(nearest_width[k].w, req_fmt_size->w))) &&  
     339                  (k < PJ_ARRAY_SIZE(nearest_width))))           
     340            { 
     341                nearest_width[k-1] = nearest_width[k]; 
     342                ++k; 
     343            } 
     344            nearest_width[k-1] = tmp_size; 
     345        }                
     346    } 
     347    /* No need to calculate ratio if exact match is found. */ 
     348    if (!found_exact_match) { 
     349        /* We have the list of supported format with nearest width. Now get the  
     350         * best ratio. 
     351         */ 
     352        req_ratio = (float)req_fmt_size->w / (float)req_fmt_size->h; 
     353        for (i=0;i<PJ_ARRAY_SIZE(nearest_width);++i) { 
     354            float sup_ratio, diff_ratio; 
     355 
     356            if (nearest_width[i].w == 0xFFFFFFFF) 
     357                continue; 
     358 
     359            sup_ratio = (float)nearest_width[i].w / (float)nearest_width[i].h; 
     360 
     361            diff_ratio = GET_DIFF(sup_ratio, req_ratio); 
     362 
     363            if ((i==0) || (diff_ratio <= min_diff_ratio)) { 
     364                match_idx = i; 
     365                min_diff_ratio = diff_ratio; 
     366            } 
     367        } 
     368    } 
     369    ret_prop.id = match_fmt_id; 
     370    ret_prop.size = nearest_width[match_idx]; 
     371    ret_prop.fps = *req_fmt_fps; 
     372    return ret_prop; 
     373} 
     374 
     375/** 
     376 * This is to test the algo to find the closest fmt 
     377 */ 
     378static void test_find_closest_fmt(pjmedia_vid_dev_info *di) 
     379 
     380    unsigned i, j, k; 
     381    char fmt_name[5]; 
     382 
     383    pjmedia_rect_size find_size[] = { 
     384        {720, 480}, 
     385        {352, 288}, 
     386        {400, 300}, 
     387        {1600, 900}, 
     388        {255, 352}, 
     389        {500, 500}, 
     390    }; 
     391 
     392    pjmedia_ratio find_fps[] = { 
     393        {1, 1}, 
     394        {10, 1}, 
     395        {15, 1}, 
     396        {30, 1}, 
     397    }; 
     398 
     399    pj_uint32_t find_id[] = { 
     400        PJMEDIA_FORMAT_RGB24, 
     401        PJMEDIA_FORMAT_RGBA, 
     402        PJMEDIA_FORMAT_AYUV, 
     403        PJMEDIA_FORMAT_YUY2, 
     404        PJMEDIA_FORMAT_I420 
     405    }; 
     406 
     407    TRACE_FIND_FMT((THIS_FILE, "Supported format = ")); 
     408    for (i = 0; i < di->fmt_cnt; i++) { 
     409        //pjmedia_video_format_detail *vid_fd =  
     410        //    pjmedia_format_get_video_format_detail(&di->fmt[i], PJ_TRUE); 
     411 
     412        pjmedia_fourcc_name(di->fmt[i].id, fmt_name); 
     413 
     414        TRACE_FIND_FMT((THIS_FILE, "id:%s size:%d*%d fps:%d/%d",  
     415                        fmt_name, 
     416                        vid_fd->size.w, 
     417                        vid_fd->size.h, 
     418                        vid_fd->fps.num, 
     419                        vid_fd->fps.denum)); 
     420    } 
     421     
     422    for (i = 0; i < PJ_ARRAY_SIZE(find_id); i++) { 
     423 
     424        for (j = 0; j < PJ_ARRAY_SIZE(find_fps); j++) { 
     425         
     426            for (k = 0; k < PJ_ARRAY_SIZE(find_size); k++) { 
     427                struct fmt_prop match_prop; 
     428 
     429                pjmedia_fourcc_name(find_id[i], fmt_name); 
     430 
     431                TRACE_FIND_FMT((THIS_FILE, "Trying to find closest match " 
     432                                           "id:%s size:%dx%d fps:%d/%d",  
     433                                fmt_name, 
     434                                find_size[k].w, 
     435                                find_size[k].h, 
     436                                find_fps[j].num, 
     437                                find_fps[j].denum)); 
     438                 
     439                match_prop = find_closest_fmt(find_id[i], 
     440                                              &find_size[k], 
     441                                              &find_fps[j], 
     442                                              di); 
     443 
     444                if ((match_prop.id == find_id[i]) &&  
     445                    (match_prop.size.w == find_size[k].w) && 
     446                    (match_prop.size.h == find_size[k].h) && 
     447                    (match_prop.fps.num / match_prop.fps.denum ==  
     448                     find_fps[j].num * find_fps[j].denum))  
     449                { 
     450                    TRACE_FIND_FMT((THIS_FILE, "Exact Match found!!")); 
     451                } else { 
     452                    pjmedia_fourcc_name(match_prop.id, fmt_name); 
     453                    TRACE_FIND_FMT((THIS_FILE, "Closest format = "\ 
     454                                                "id:%s size:%dx%d fps:%d/%d",  
     455                                    fmt_name, 
     456                                    match_prop.size.w, 
     457                                    match_prop.size.h,  
     458                                    match_prop.fps.num, 
     459                                    match_prop.fps.denum));                  
     460                } 
     461            } 
     462        } 
     463    } 
    184464} 
    185465 
     
    189469{ 
    190470    pjmedia_vid_port *vp; 
    191     const pjmedia_video_format_detail *vfd; 
     471    pjmedia_video_format_detail *vfd; 
    192472    char dev_name[64]; 
    193473    char fmt_name[5]; 
     
    198478    pjmedia_vid_dev_param vparam; 
    199479    pjmedia_vid_dev_info di; 
    200     unsigned i; 
    201480 
    202481    PJ_ASSERT_RETURN(pool && prm && p_vid_port, PJ_EINVAL); 
     
    234513                     di.name, di.driver); 
    235514 
    236     for (i = 0; i < di.fmt_cnt; ++i) { 
    237         if (prm->vidparam.fmt.id == di.fmt[i].id) 
    238             break; 
    239     } 
    240  
    241     if (i == di.fmt_cnt) { 
    242         /* The device has no no matching format. Pick one from 
    243          * the supported formats, and later use converter to 
    244          * convert it to the required format. 
    245          */ 
    246         pj_assert(di.fmt_cnt != 0); 
    247         vparam.fmt.id = di.fmt[0].id; 
     515    if (di.dir == PJMEDIA_DIR_RENDER) { 
     516        /* Find the matching format. If no exact match is found, find  
     517         * the supported format with the same color space. If no match is found, 
     518         * use the first supported format on the list. 
     519         */ 
     520        pj_assert(di.fmt_cnt != 0); 
     521        vparam.fmt.id = get_match_format_id(prm->vidparam.fmt.id, &di); 
     522    } else { 
     523        struct fmt_prop match_prop; 
     524 
     525        if (di.fmt_cnt == 0) { 
     526            status = PJMEDIA_EVID_SYSERR; 
     527            PJ_PERROR(4,(THIS_FILE, status, "Device has no supported format")); 
     528            return status; 
     529        } 
     530 
     531#if 0 
     532        test_find_closest_fmt(&di); 
     533#endif 
     534 
     535        pjmedia_fourcc_name(vparam.fmt.id, fmt_name); 
     536        PJ_LOG(4,(THIS_FILE, 
     537                  "Finding best match for %s(%s) format=%s, size=%dx%d "\ 
     538                  "@%d:%d fps", 
     539                  dev_name, vid_dir_name(prm->vidparam.dir), fmt_name, 
     540                  vfd->size.w, vfd->size.h, vfd->fps.num, vfd->fps.denum)); 
     541 
     542        match_prop = find_closest_fmt(prm->vidparam.fmt.id,  
     543                                      &vfd->size,                             
     544                                      &vfd->fps,  
     545                                      &di); 
     546 
     547        if ((match_prop.id != prm->vidparam.fmt.id) ||  
     548            (match_prop.size.w != vfd->size.w) || 
     549            (match_prop.size.h != vfd->size.h)) 
     550        { 
     551            vparam.fmt.id = match_prop.id; 
     552            vparam.fmt.det.vid.size = match_prop.size; 
     553            vfd->size = match_prop.size; 
     554        } 
    248555    } 
    249556 
Note: See TracChangeset for help on using the changeset viewer.