Ignore:
Timestamp:
Apr 12, 2011 6:24:19 PM (13 years ago)
Author:
nanang
Message:

Re #1219:

  • Implemented validation of H.264 level in codec param.
  • Update H.264 packetization setting to always send single NAL unit, for better compatibility.
  • Update H.264 SDP custom negotiation to be permissive.
Location:
pjproject/branches/projects/2.0-dev/pjmedia
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/vid_codec_util.h

    r3500 r3526  
    140140 
    141141 
     142/** 
     143 * Parse and apply the encoding and decoding SDP fmtp of H.264 in the 
     144 * specified codec parameter. This will validate size and fps to conform 
     145 * to H.264 level specified in SDP fmtp "profile-level-id". 
     146 * 
     147 * @param param         The codec parameter. 
     148 * 
     149 * @return              PJ_SUCCESS on success. 
     150 */ 
     151PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp( 
     152                                pjmedia_vid_codec_param *param); 
     153 
     154 
    142155PJ_END_DECL 
    143156 
  • pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c

    r3522 r3526  
    261261              {{"QCIF",4},  {"1",1}}, } }, 
    262262    }, 
    263 /* 
    264263    { 
    265264        {PJMEDIA_FORMAT_H263, PJMEDIA_RTP_PT_H263, {"H263",4}}, 
     
    269268              {{"QCIF",4},  {"1",1}}, } }, 
    270269    }, 
    271 */ 
    272270    { 
    273271        {PJMEDIA_FORMAT_H263,   PJMEDIA_RTP_PT_H263,    {"H263",4}}, 
     
    313311    /* Create packetizer */ 
    314312    pktz_cfg.mtu = ff->param.enc_mtu; 
     313#if 0 
    315314    if (data->fmtp.packetization_mode == 0) 
    316315        pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL; 
     
    319318    else 
    320319        return PJ_ENOTSUP; 
     320#else 
     321    if (data->fmtp.packetization_mode!= 
     322                                PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL && 
     323        data->fmtp.packetization_mode!= 
     324                                PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED) 
     325    { 
     326        return PJ_ENOTSUP; 
     327    } 
     328    /* Better always send in single NAL mode for better compatibility */ 
     329    pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL; 
     330#endif 
    321331 
    322332    status = pjmedia_h264_packetizer_create(ff->pool, &pktz_cfg, &data->pktz); 
     333    if (status != PJ_SUCCESS) 
     334        return status; 
     335 
     336    /* Apply SDP fmtp to format in codec param */ 
     337    status = pjmedia_vid_codec_h264_apply_fmtp(&ff->param); 
    323338    if (status != PJ_SUCCESS) 
    324339        return status; 
     
    354369 
    355370        /* Apply profile level. */ 
    356         PJ_TODO(apply_h264_profile_level_in_pjmedia_vid_codec_param); 
    357371        ctx->level    = data->fmtp.level; 
    358372 
     
    12461260    int out_buf_len = output_buf_len; 
    12471261    int err; 
    1248     AVRational src_timebase; 
     1262    //AVRational src_timebase; 
    12491263    /* For some reasons (e.g: SSE/MMX usage), the avcodec_encode_video() must 
    12501264     * have stack aligned to 16 bytes. Let's try to be safe by preparing the 
  • pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/vid_codec_util.c

    r3500 r3526  
    2626 
    2727#define THIS_FILE   "vid_codec_util.c" 
     28 
     29/* If this is set to non-zero, H.264 custom negotiation will require 
     30 * "profile-level-id" and "packetization-mode" to be exact match to 
     31 * get a successful negotiation. Note that flexible answer (updating 
     32 * SDP answer to match remote offer) is always active regardless the 
     33 * value of this macro. 
     34 */ 
     35#define H264_STRICT_SDP_NEGO        0 
    2836 
    2937 
     
    421429 
    422430        /* Match them now */ 
     431#if H264_STRICT_SDP_NEGO 
    423432        if (a_fmtp.profile_idc != o_fmtp.profile_idc || 
    424433            a_fmtp.profile_iop != o_fmtp.profile_iop || 
     
    428437            return PJMEDIA_SDP_EFORMATNOTEQUAL; 
    429438        } 
     439#else 
     440        if (a_fmtp.profile_idc != o_fmtp.profile_idc) 
     441        { 
     442            return PJMEDIA_SDP_EFORMATNOTEQUAL; 
     443        } 
     444#endif 
    430445 
    431446        /* Update the answer */ 
     
    447462        } 
    448463    } else { 
     464#if H264_STRICT_SDP_NEGO 
    449465        /* Strict negotiation */ 
    450466        if (a_fmtp.profile_idc != o_fmtp.profile_idc || 
     
    455471            return PJMEDIA_SDP_EFORMATNOTEQUAL; 
    456472        } 
     473#else 
     474        /* Permissive negotiation */ 
     475        if (a_fmtp.profile_idc != o_fmtp.profile_idc) 
     476        { 
     477            return PJMEDIA_SDP_EFORMATNOTEQUAL; 
     478        } 
     479#endif 
    457480    } 
    458481 
    459482    return PJ_SUCCESS; 
    460483} 
     484 
     485 
     486/* Declaration of H.264 level info */ 
     487typedef struct h264_level_info_t 
     488{ 
     489    unsigned id;            /* Level id.                        */ 
     490    unsigned max_mbps;      /* Max macroblocks per second.      */ 
     491    unsigned max_mb;        /* Max macroblocks.                 */ 
     492    unsigned bitrate;       /* Max bitrate (kbps).              */ 
     493    unsigned def_w;         /* Default width.                   */ 
     494    unsigned def_h;         /* Default height.                  */ 
     495    unsigned def_fps;       /* Default fps.                     */ 
     496} h264_level_info_t; 
     497 
     498 
     499/* Get H.264 level info from specified level ID */ 
     500static pj_status_t get_h264_level_info(unsigned id, h264_level_info_t *level) 
     501{ 
     502    unsigned i; 
     503    const h264_level_info_t level_info[] = 
     504    { 
     505        { 10,   1485,    99,     64,  176,  144, 15 }, 
     506        { 9,    1485,    99,    128,  176,  144, 15 }, /*< level 1b */ 
     507        { 11,   3000,   396,    192,  320,  240, 10 }, 
     508        { 12,   6000,   396,    384,  352,  288, 15 }, 
     509        { 13,  11880,   396,    768,  352,  288, 15 }, 
     510        { 20,  11880,   396,   2000,  352,  288, 30 }, 
     511        { 21,  19800,   792,   4000,  352,  288, 30 }, 
     512        { 22,  20250,  1620,   4000,  352,  288, 30 }, 
     513        { 30,  40500,  1620,  10000,  720,  480, 30 }, 
     514        { 31, 108000,  3600,  14000, 1280,  720, 30 }, 
     515        { 32, 216000,  5120,  20000, 1280,  720, 30 }, 
     516        { 40, 245760,  8192,  20000, 1920, 1080, 30 }, 
     517        { 41, 245760,  8192,  50000, 1920, 1080, 30 }, 
     518        { 42, 522240,  8704,  50000, 1920, 1080, 30 }, 
     519        { 50, 589824, 22080, 135000, 1920, 1080, 30 }, 
     520        { 51, 983040, 36864, 240000, 1920, 1080, 30 }, 
     521    }; 
     522 
     523    for (i = 0; i < PJ_ARRAY_SIZE(level_info); ++i) { 
     524        if (level_info[i].id == id) { 
     525            *level = level_info[i]; 
     526            return PJ_SUCCESS; 
     527        } 
     528    } 
     529    return PJ_ENOTFOUND; 
     530} 
     531 
     532 
     533#define CALC_H264_MB_NUM(size) (((size.w+15)/16)*((size.h+15)/16)) 
     534#define CALC_H264_MBPS(size,fps) CALC_H264_MB_NUM(size)*fps.num/fps.denum 
     535 
     536 
     537PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp( 
     538                                pjmedia_vid_codec_param *param) 
     539{ 
     540    const unsigned default_fps = 30; 
     541 
     542    if (param->dir & PJMEDIA_DIR_ENCODING) { 
     543        pjmedia_vid_codec_h264_fmtp fmtp; 
     544        pjmedia_video_format_detail *vfd; 
     545        h264_level_info_t level_info; 
     546        pj_status_t status; 
     547 
     548        /* Get remote param */ 
     549        status = pjmedia_vid_codec_h264_parse_fmtp(&param->enc_fmtp, 
     550                                                   &fmtp); 
     551        if (status != PJ_SUCCESS) 
     552            return status; 
     553 
     554        status = get_h264_level_info(fmtp.level, &level_info); 
     555        if (status != PJ_SUCCESS) 
     556            return status; 
     557 
     558        /* Size and fps for encoding direction must conform to H.264 level 
     559         * specified by remote SDP fmtp. 
     560         */ 
     561        vfd = pjmedia_format_get_video_format_detail(&param->enc_fmt, 
     562                                                     PJ_TRUE); 
     563        if (vfd->size.w && vfd->size.h) { 
     564            unsigned mb, mbps; 
     565             
     566            if (vfd->fps.num == 0 || vfd->fps.denum == 0) { 
     567                vfd->fps.num = default_fps; 
     568                vfd->fps.denum = 1; 
     569            } 
     570            mb = CALC_H264_MB_NUM(vfd->size); 
     571            mbps = CALC_H264_MBPS(vfd->size, vfd->fps); 
     572            if (mb > level_info.max_mb || mbps > level_info.max_mbps) { 
     573                vfd->size.w = level_info.def_w; 
     574                vfd->size.h = level_info.def_h; 
     575                vfd->fps.num = level_info.def_fps; 
     576                vfd->fps.denum = 1; 
     577            } 
     578        } else { 
     579            vfd->size.w = level_info.def_w; 
     580            vfd->size.h = level_info.def_h; 
     581            vfd->fps.num = level_info.def_fps; 
     582            vfd->fps.denum = 1; 
     583        } 
     584    } 
     585 
     586    if (param->dir & PJMEDIA_DIR_DECODING) { 
     587        /* Here we just want to find the highest resolution possible from the 
     588         * fmtp and set it as the decoder param. 
     589         */ 
     590        pjmedia_vid_codec_h264_fmtp fmtp; 
     591        pjmedia_video_format_detail *vfd; 
     592        h264_level_info_t level_info; 
     593        pj_status_t status; 
     594         
     595        status = pjmedia_vid_codec_h264_parse_fmtp(&param->dec_fmtp, 
     596                                                   &fmtp); 
     597        if (status != PJ_SUCCESS) 
     598            return status; 
     599 
     600        status = get_h264_level_info(fmtp.level, &level_info); 
     601        if (status != PJ_SUCCESS) 
     602            return status; 
     603 
     604        vfd = pjmedia_format_get_video_format_detail(&param->dec_fmt, 
     605                                                     PJ_TRUE); 
     606 
     607        if (vfd->size.w * vfd->size.h < level_info.def_w * level_info.def_h) { 
     608            vfd->size.w = level_info.def_w; 
     609            vfd->size.h = level_info.def_h; 
     610        } 
     611 
     612        if (vfd->fps.num == 0 || vfd->fps.denum == 0) { 
     613            vfd->fps.num = default_fps; 
     614            vfd->fps.denum = 1; 
     615        } 
     616    } 
     617 
     618    return PJ_SUCCESS; 
     619} 
Note: See TracChangeset for help on using the changeset viewer.