Ignore:
Timestamp:
Dec 30, 2010 4:31:16 PM (13 years ago)
Author:
ming
Message:

Re #1184: Adding pjmedia_clock_src for the purpose of audio and video synchronization and also to provide synchronization mechanism between two medias in general.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/videoport.c

    r3401 r3402  
    5050                        *dec_clock; 
    5151 
     52    pjmedia_clock_src    cap_clocksrc, 
     53                         rend_clocksrc; 
     54 
     55    struct sync_clock_src_t 
     56    { 
     57        pjmedia_clock_src   *sync_clocksrc; 
     58        pj_int32_t           sync_delta; 
     59        unsigned             max_sync_ticks; 
     60        unsigned             nsync_frame; 
     61        unsigned             nsync_progress; 
     62    } cap_sync_clocksrc, rend_sync_clocksrc; 
     63 
    5264    pjmedia_frame       *enc_frm_buf, 
    5365                        *dec_frm_buf; 
     
    99111    pj_bool_t need_frame_buf = PJ_FALSE; 
    100112    pj_status_t status; 
     113    unsigned ptime_usec; 
    101114 
    102115    PJ_ASSERT_RETURN(pool && prm && p_vid_port, PJ_EINVAL); 
     
    140153    vp->stream_role = di.has_callback ? ROLE_ACTIVE : ROLE_PASSIVE; 
    141154 
     155    ptime_usec = PJMEDIA_PTIME(&vfd->fps); 
     156    pjmedia_clock_src_init(&vp->cap_clocksrc, PJMEDIA_TYPE_VIDEO, 
     157                           prm->vidparam.clock_rate, ptime_usec); 
     158    pjmedia_clock_src_init(&vp->rend_clocksrc, PJMEDIA_TYPE_VIDEO, 
     159                           prm->vidparam.clock_rate, ptime_usec); 
     160    vp->cap_sync_clocksrc.max_sync_ticks =  
     161        PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION * 
     162        1000 / vp->cap_clocksrc.ptime_usec; 
     163    vp->rend_sync_clocksrc.max_sync_ticks =  
     164        PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION * 
     165        1000 / vp->rend_clocksrc.ptime_usec; 
     166 
    142167    /* Create the video stream */ 
    143168    pj_bzero(&vid_cb, sizeof(vid_cb)); 
     
    269294 
    270295 
     296 
     297PJ_DEF(pjmedia_clock_src *) 
     298pjmedia_vid_port_get_clock_src( pjmedia_vid_port *vid_port, 
     299                                pjmedia_dir dir ) 
     300{ 
     301    return (dir == PJMEDIA_DIR_CAPTURE? &vid_port->cap_clocksrc: 
     302            &vid_port->rend_clocksrc); 
     303} 
     304 
     305PJ_DECL(pj_status_t) 
     306pjmedia_vid_port_set_clock_src( pjmedia_vid_port *vid_port, 
     307                                pjmedia_dir dir, 
     308                                pjmedia_clock_src *clocksrc) 
     309{ 
     310    pjmedia_clock_src *vclocksrc; 
     311    struct sync_clock_src_t *sync_src; 
     312 
     313    PJ_ASSERT_RETURN(vid_port && clocksrc, PJ_EINVAL); 
     314 
     315    vclocksrc = (dir == PJMEDIA_DIR_CAPTURE? &vid_port->cap_clocksrc: 
     316                 &vid_port->rend_clocksrc); 
     317    sync_src = (dir == PJMEDIA_DIR_CAPTURE? &vid_port->cap_sync_clocksrc: 
     318                &vid_port->rend_sync_clocksrc); 
     319    sync_src->sync_clocksrc = clocksrc; 
     320    sync_src->sync_delta = pjmedia_clock_src_get_time_msec(vclocksrc) - 
     321                           pjmedia_clock_src_get_time_msec(clocksrc); 
     322 
     323    return PJ_SUCCESS; 
     324} 
     325 
     326 
    271327PJ_DEF(pj_status_t) pjmedia_vid_port_connect(pjmedia_vid_port *vp, 
    272328                                              pjmedia_port *port, 
     
    438494    pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data; 
    439495    pj_status_t status; 
     496    unsigned frame_ts = vp->rend_clocksrc.clock_rate / 1000 * 
     497                        vp->rend_clocksrc.ptime_usec / 1000; 
    440498 
    441499    pj_assert(vp->role==ROLE_ACTIVE && vp->stream_role==ROLE_PASSIVE); 
     
    446504        return; 
    447505 
     506    if (vp->rend_sync_clocksrc.sync_clocksrc) { 
     507        pjmedia_clock_src *src = vp->rend_sync_clocksrc.sync_clocksrc; 
     508        pj_int32_t diff; 
     509        unsigned nsync_frame; 
     510 
     511        /* Synchronization */ 
     512        /* Calculate the time difference (in ms) with the sync source */ 
     513        diff = pjmedia_clock_src_get_time_msec(&vp->rend_clocksrc) - 
     514               pjmedia_clock_src_get_time_msec(src) - 
     515               vp->rend_sync_clocksrc.sync_delta; 
     516 
     517        /* Check whether sync source made a large jump */ 
     518        if (diff < 0 && -diff > PJMEDIA_CLOCK_SYNC_MAX_SYNC_MSEC) { 
     519            pjmedia_clock_src_update(&vp->rend_clocksrc, NULL); 
     520            vp->rend_sync_clocksrc.sync_delta =  
     521                pjmedia_clock_src_get_time_msec(src) - 
     522                pjmedia_clock_src_get_time_msec(&vp->rend_clocksrc); 
     523            vp->rend_sync_clocksrc.nsync_frame = 0; 
     524            return; 
     525        } 
     526 
     527        /* Calculate the difference (in frames) with the sync source */ 
     528        nsync_frame = abs(diff) * 1000 / vp->rend_clocksrc.ptime_usec; 
     529        if (nsync_frame == 0) { 
     530            /* Nothing to sync */ 
     531            vp->rend_sync_clocksrc.nsync_frame = 0; 
     532        } else { 
     533            pj_int32_t init_sync_frame = nsync_frame; 
     534 
     535            /* Check whether it's a new sync or whether we need to reset 
     536             * the sync 
     537             */ 
     538            if (vp->rend_sync_clocksrc.nsync_frame == 0 || 
     539                (vp->rend_sync_clocksrc.nsync_frame > 0 && 
     540                 nsync_frame > vp->rend_sync_clocksrc.nsync_frame)) 
     541            { 
     542                vp->rend_sync_clocksrc.nsync_frame = nsync_frame; 
     543                vp->rend_sync_clocksrc.nsync_progress = 0; 
     544            } else { 
     545                init_sync_frame = vp->rend_sync_clocksrc.nsync_frame; 
     546            } 
     547 
     548            if (diff >= 0) { 
     549                unsigned skip_mod; 
     550 
     551                /* We are too fast */ 
     552                if (vp->rend_sync_clocksrc.max_sync_ticks > 0) { 
     553                    skip_mod = init_sync_frame /  
     554                               vp->rend_sync_clocksrc.max_sync_ticks + 2; 
     555                } else 
     556                    skip_mod = init_sync_frame + 2; 
     557 
     558                PJ_LOG(5, (THIS_FILE, "synchronization: early by %d ms", 
     559                           diff)); 
     560                /* We'll play a frame every skip_mod-th tick instead of 
     561                 * a complete pause 
     562                 */ 
     563                if (++vp->rend_sync_clocksrc.nsync_progress % skip_mod > 0) { 
     564                    pjmedia_clock_src_update(&vp->rend_clocksrc, NULL); 
     565                    return; 
     566                } 
     567            } else { 
     568                unsigned i, ndrop = init_sync_frame; 
     569 
     570                /* We are too late, drop the frame */ 
     571                if (vp->rend_sync_clocksrc.max_sync_ticks > 0) { 
     572                    ndrop /= vp->rend_sync_clocksrc.max_sync_ticks; 
     573                    ndrop++; 
     574                } 
     575                PJ_LOG(5, (THIS_FILE, "synchronization: late, " 
     576                                      "dropping %d frame(s)", ndrop)); 
     577 
     578                if (ndrop >= nsync_frame) { 
     579                    vp->rend_sync_clocksrc.nsync_frame = 0; 
     580                    ndrop = nsync_frame; 
     581                } else 
     582                    vp->rend_sync_clocksrc.nsync_progress += ndrop; 
     583                for (i = 0; i < ndrop; i++) { 
     584                    status = pjmedia_port_get_frame(vp->client_port, 
     585                                                    vp->dec_frm_buf); 
     586                    if (status != PJ_SUCCESS) { 
     587                        pjmedia_clock_src_update(&vp->rend_clocksrc, NULL); 
     588                        return; 
     589                    } 
     590                    pj_add_timestamp32(&vp->rend_clocksrc.timestamp, 
     591                                       frame_ts); 
     592                } 
     593            } 
     594        } 
     595    } 
     596 
    448597    status = pjmedia_port_get_frame(vp->client_port, vp->dec_frm_buf); 
    449     if (status != PJ_SUCCESS) 
     598    if (status != PJ_SUCCESS) { 
     599        pjmedia_clock_src_update(&vp->rend_clocksrc, NULL); 
    450600        return; 
     601    } 
     602    pj_add_timestamp32(&vp->rend_clocksrc.timestamp, frame_ts); 
     603    pjmedia_clock_src_update(&vp->rend_clocksrc, NULL); 
    451604 
    452605    status = pjmedia_vid_stream_put_frame(vp->strm, vp->dec_frm_buf); 
Note: See TracChangeset for help on using the changeset viewer.