Changeset 3402


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.

Location:
pjproject/branches/projects/2.0-dev
Files:
13 edited

Legend:

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

    r3392 r3402  
    5050}; 
    5151 
     52/** 
     53 * AVI stream data type. 
     54 */ 
    5255typedef pjmedia_port pjmedia_avi_stream; 
    5356 
     57/** 
     58 * Opaque data type for AVI streams. AVI streams is a collection of 
     59 * zero or more AVI stream. 
     60 */ 
    5461typedef struct pjmedia_avi_streams pjmedia_avi_streams; 
    5562 
     
    7279                                  pjmedia_avi_streams **p_streams); 
    7380 
    74 PJ_DECL(pj_uint8_t) 
     81/** 
     82 * Get the number of AVI stream. 
     83 * 
     84 * @param streams       The AVI streams. 
     85 * 
     86 * @return              The number of AVI stream. 
     87 */ 
     88PJ_DECL(unsigned) 
    7589pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams); 
    7690 
     91/** 
     92 * Return the idx-th stream of the AVI streams. 
     93 * 
     94 * @param streams       The AVI streams. 
     95 * @param idx           The stream index. 
     96 * 
     97 * @return              The AVI stream or NULL if it does not exist. 
     98 */ 
    7799PJ_DECL(pjmedia_avi_stream *) 
    78100pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams, 
    79                                pj_uint8_t idx); 
     101                               unsigned idx); 
    80102 
     103/** 
     104 * Return an AVI stream with a certain media type from the AVI streams. 
     105 * 
     106 * @param streams       The AVI streams. 
     107 * @param start_idx     The starting index. 
     108 * @param media_type    The media type of the stream. 
     109 * 
     110 * @return              The AVI stream or NULL if it does not exist. 
     111 */ 
    81112PJ_DECL(pjmedia_avi_stream *) 
    82113pjmedia_avi_streams_get_stream_by_media(pjmedia_avi_streams *streams, 
    83                                         pj_uint8_t start_idx, 
     114                                        unsigned start_idx, 
    84115                                        pjmedia_type media_type); 
    85116 
     117/** 
     118 * Return the media port of an AVI stream. 
     119 * 
     120 * @param stream        The AVI stream. 
     121 * 
     122 * @return              The media port. 
     123 */ 
    86124PJ_INLINE(pjmedia_port *) 
    87125pjmedia_avi_stream_get_port(pjmedia_avi_stream *stream) 
     
    93131 * Get the data length, in bytes. 
    94132 * 
    95  * @param port          The AVI stream. 
     133 * @param stream        The AVI stream. 
    96134 * 
    97135 * @return              The length of the data, in bytes. Upon error it will 
     
    107145 * registered for each AVI stream. 
    108146 * 
    109  * @param port          The AVI stream. 
     147 * @param stream        The AVI stream. 
    110148 * @param user_data     User data to be specified in the callback 
    111149 * @param cb            Callback to be called. If the callback returns non- 
  • pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/clock.h

    r3392 r3402  
    7979 
    8080PJ_BEGIN_DECL 
     81 
     82/** 
     83 * Media clock source. 
     84 */ 
     85typedef struct pjmedia_clock_src 
     86{ 
     87    pjmedia_type    media_type;     /**< Media type.                */ 
     88    unsigned        clock_rate;     /**< Clock rate.                */ 
     89    unsigned        ptime_usec;     /**< Frame interval (in usec).  */ 
     90    /** 
     91     * The timestamp field holds an increasing value in samples and its 
     92     * value is expected to be increased by clock_rate samples per second. 
     93     */ 
     94    pj_timestamp    timestamp; 
     95    /** 
     96     * Timestamp's last update. The last_update field contains a value in 
     97     * ticks, and it is expected to be increased by pj_get_timestamp_freq() 
     98     * ticks per second. 
     99     */ 
     100    pj_timestamp    last_update; 
     101} pjmedia_clock_src; 
     102 
     103/** 
     104 * This is an auxiliary function to initialize the media clock source. 
     105 * 
     106 * @param clocksrc          The clock source to be initialized. 
     107 * @param media_type        The media type. 
     108 * @param clock_rate        The clock rate. 
     109 * @param ptime_usec        Media frame interval (in usec). 
     110 * 
     111 * @return                  PJ_SUCCESS on success. 
     112 */ 
     113PJ_DECL(pj_status_t) pjmedia_clock_src_init( pjmedia_clock_src *clocksrc, 
     114                                             pjmedia_type media_type, 
     115                                             unsigned clock_rate, 
     116                                             unsigned ptime_usec ); 
     117 
     118/** 
     119 * This function updates the clock source's timestamp. Application should 
     120 * use this function instead of updating the timestamp directly since this 
     121 * function will also update the last_update field of the clock source. 
     122 * 
     123 * @param clocksrc          The clock source to be updated. 
     124 * @param timestamp         The new timestamp, can be NULL if the current 
     125 *                          timestamp does not change (in this case it 
     126 *                          will only update the last_update field). 
     127 * 
     128 * @return                  PJ_SUCCESS on success. 
     129 */ 
     130PJ_DECL(pj_status_t) pjmedia_clock_src_update( pjmedia_clock_src *clocksrc, 
     131                                               const pj_timestamp *timestamp ); 
     132 
     133/** 
     134 * This function gets the clock source's current timestamp. Application 
     135 * should use this function instead of accessing the timestamp directly 
     136 * since this function will calculate the predicted timestamp for current 
     137 * time, based on the values of timestamp, last_update, and clock_rate. 
     138 * 
     139 * @param clocksrc          The clock source. 
     140 * @param timestamp         Argument to receive the current timestamp 
     141 * 
     142 * @return                  PJ_SUCCESS on success. 
     143 */ 
     144PJ_DECL(pj_status_t) 
     145pjmedia_clock_src_get_current_timestamp( const pjmedia_clock_src *clocksrc, 
     146                                         pj_timestamp *timestamp); 
     147 
     148/** 
     149 * This function gets the clock source's time in msec. 
     150 * 
     151 * @param clocksrc          The clock source. 
     152 * 
     153 * @return                  The clock source's time (in msec). 
     154 */ 
     155PJ_DECL(pj_uint32_t) 
     156pjmedia_clock_src_get_time_msec( const pjmedia_clock_src *clocksrc ); 
    81157 
    82158 
  • pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/config.h

    r3392 r3402  
    967967#endif 
    968968 
     969/** 
     970 * Specify the maximum time difference (in ms) for synchronization between 
     971 * two medias. If the synchronization media source is ahead of time 
     972 * greater than this duration, it is considered to make a very large jump 
     973 * and the synchronization will be reset. 
     974 * 
     975 * Default: 20000 
     976 */ 
     977#ifndef PJMEDIA_CLOCK_SYNC_MAX_SYNC_MSEC 
     978#   define PJMEDIA_CLOCK_SYNC_MAX_SYNC_MSEC         20000 
     979#endif 
     980 
     981/** 
     982 * Specify the maximum duration (in ms) for resynchronization. When a media 
     983 * is late to another media it is supposed to be synchronized to, it is 
     984 * guaranteed to be synchronized again after this duration. While if the 
     985 * media is ahead/early by t ms, it is guaranteed to be synchronized after 
     986 * t + this duration. This timing only applies if there is no additional 
     987 * resynchronization required during the specified duration. 
     988 * 
     989 * Default: 2000 
     990 */ 
     991#ifndef PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION 
     992#   define PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION 2000 
     993#endif 
     994 
    969995 
    970996/** 
  • pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/port.h

    r3392 r3402  
    2525 * @brief Port interface declaration 
    2626 */ 
     27#include <pjmedia/clock.h> 
    2728#include <pjmedia/format.h> 
    2829#include <pjmedia/frame.h> 
     
    373374 
    374375    /** 
     376     * Get clock source. 
     377     * This should only be called by #pjmedia_port_get_clock_src(). 
     378     */ 
     379    pjmedia_clock_src* (*get_clock_src)(struct pjmedia_port *this_port, 
     380                                        pjmedia_dir dir); 
     381 
     382    /** 
    375383     * Sink interface.  
    376384     * This should only be called by #pjmedia_port_put_frame(). 
     
    399407 * 
    400408 * @param info              The port info to be initialized. 
    401  * @param pool              Pool to allocate memory from. 
    402409 * @param name              Port name. 
    403410 * @param signature         Port signature. 
     
    437444 
    438445/** 
     446 * Get a clock source from the port. 
     447 * 
     448 * @param port      The media port. 
     449 * @param dir       Media port's direction. 
     450 * 
     451 * @return          The clock source or NULL if clock source is not present 
     452 *                  in the port. 
     453 */ 
     454PJ_DECL(pjmedia_clock_src *) pjmedia_port_get_clock_src( pjmedia_port *port, 
     455                                                         pjmedia_dir dir ); 
     456 
     457 
     458/** 
    439459 * Get a frame from the port (and subsequent downstream ports). 
    440460 * 
  • pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/sound_port.h

    r2506 r3402  
    2626 */ 
    2727#include <pjmedia-audiodev/audiodev.h> 
     28#include <pjmedia/clock.h> 
    2829#include <pjmedia/port.h> 
    2930 
     
    243244 
    244245/** 
     246 * Get a clock source from the sound port. 
     247 * 
     248 * @param snd_port  The sound port. 
     249 * @param dir       Sound port's direction. 
     250 * 
     251 * @return          The clock source. 
     252 */ 
     253PJ_DECL(pjmedia_clock_src *) 
     254pjmedia_snd_port_get_clock_src( pjmedia_snd_port *snd_port, 
     255                                pjmedia_dir dir ); 
     256 
     257 
     258/** 
    245259 * Connect a port to the sound device port. If the sound device port has a 
    246260 * sound recorder device, then this will start periodic function call to 
  • pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/videoport.h

    r3392 r3402  
    126126 
    127127/** 
     128 * Get a clock source from the video port. 
     129 * 
     130 * @param vid_port  The video port. 
     131 * @param dir       Video port's direction. 
     132 * 
     133 * @return          The clock source. 
     134 */ 
     135PJ_DECL(pjmedia_clock_src *) 
     136pjmedia_vid_port_get_clock_src( pjmedia_vid_port *vid_port, 
     137                                pjmedia_dir dir ); 
     138 
     139/** 
     140 * Set a clock source for the video port. 
     141 * 
     142 * @param vid_port  The video port. 
     143 * @param dir       Video port's direction. 
     144 * @param clocksrc  The clock source. 
     145 * 
     146 * @return          PJ_SUCCESS on success or the appropriate error code. 
     147 */ 
     148PJ_DECL(pj_status_t) 
     149pjmedia_vid_port_set_clock_src( pjmedia_vid_port *vid_port, 
     150                                pjmedia_dir dir, 
     151                                pjmedia_clock_src *clocksrc ); 
     152 
     153/** 
    128154 * Connect the video port to a downstream (slave) media port. This operation 
    129155 * is only valid for video ports created with active interface selected. 
  • pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia-videodev/dshow_dev.c

    r3392 r3402  
    4141 
    4242#define THIS_FILE               "dshow_dev.c" 
     43#define DEFAULT_CLOCK_RATE      90000 
    4344#define DEFAULT_WIDTH           640 
    4445#define DEFAULT_HEIGHT          480 
     
    394395 
    395396    /* Set the device capabilities here */ 
    396     param->clock_rate = 9000; 
    397     param->frame_rate.num = 14; 
     397    param->clock_rate = DEFAULT_CLOCK_RATE; 
     398    param->frame_rate.num = DEFAULT_FPS; 
    398399    param->frame_rate.denum = 1; 
    399400    param->flags = PJMEDIA_VID_DEV_CAP_FORMAT; 
  • pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/avi_player.c

    r3392 r3402  
    8787struct pjmedia_avi_streams 
    8888{ 
    89     pj_uint8_t      num_streams; 
     89    unsigned        num_streams; 
    9090    pjmedia_port  **streams; 
    9191}; 
     
    9494{ 
    9595    pjmedia_port     base; 
    96     pj_uint8_t       stream_id; 
     96    unsigned         stream_id; 
    9797    unsigned         options; 
    9898    pj_uint16_t      bits_per_sample; 
     
    172172    struct avi_reader_port *fport[PJMEDIA_AVI_MAX_NUM_STREAMS]; 
    173173    pj_off_t pos; 
    174     pj_uint8_t i, nstr = 0; 
     174    unsigned i, nstr = 0; 
    175175    pj_status_t status = PJ_SUCCESS; 
    176176 
     
    468468} 
    469469 
    470 PJ_DEF(pj_uint8_t) 
     470PJ_DEF(unsigned) 
    471471pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams) 
    472472{ 
     
    477477PJ_DEF(pjmedia_avi_stream *) 
    478478pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams, 
    479                                pj_uint8_t idx) 
     479                               unsigned idx) 
    480480{ 
    481481    pj_assert(streams); 
     
    486486PJ_DEF(pjmedia_avi_stream *) 
    487487pjmedia_avi_streams_get_stream_by_media(pjmedia_avi_streams *streams, 
    488                                         pj_uint8_t start_idx, 
     488                                        unsigned start_idx, 
    489489                                        pjmedia_type media_type) 
    490490{ 
    491     pj_uint8_t i; 
     491    unsigned i; 
    492492 
    493493    pj_assert(streams); 
     
    594594        pjmedia_avi_subchunk ch = {0, 0}; 
    595595        char *cid; 
    596         pj_uint8_t stream_id; 
     596        unsigned stream_id; 
    597597 
    598598        /* We need to read data from the file past the chunk boundary */ 
  • pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/clock_thread.c

    r3392 r3402  
    2424#include <pj/os.h> 
    2525#include <pj/pool.h> 
     26#include <pj/string.h> 
     27#include <pj/compat/high_precision.h> 
     28 
     29/* API: Init clock source */ 
     30PJ_DEF(pj_status_t) pjmedia_clock_src_init( pjmedia_clock_src *clocksrc, 
     31                                            pjmedia_type media_type, 
     32                                            unsigned clock_rate, 
     33                                            unsigned ptime_usec ) 
     34{ 
     35    PJ_ASSERT_RETURN(clocksrc, PJ_EINVAL); 
     36 
     37    clocksrc->media_type = media_type; 
     38    clocksrc->clock_rate = clock_rate; 
     39    clocksrc->ptime_usec = ptime_usec; 
     40    pj_set_timestamp32(&clocksrc->timestamp, 0, 0); 
     41    pj_get_timestamp(&clocksrc->last_update); 
     42 
     43    return PJ_SUCCESS; 
     44} 
     45 
     46/* API: Update clock source */ 
     47PJ_DECL(pj_status_t) pjmedia_clock_src_update( pjmedia_clock_src *clocksrc, 
     48                                               const pj_timestamp *timestamp ) 
     49{ 
     50    PJ_ASSERT_RETURN(clocksrc, PJ_EINVAL); 
     51 
     52    if (timestamp) 
     53        pj_memcpy(&clocksrc->timestamp, timestamp, sizeof(pj_timestamp)); 
     54    pj_get_timestamp(&clocksrc->last_update); 
     55 
     56    return PJ_SUCCESS; 
     57} 
     58 
     59/* API: Get clock source's current timestamp */ 
     60PJ_DEF(pj_status_t) 
     61pjmedia_clock_src_get_current_timestamp( const pjmedia_clock_src *clocksrc, 
     62                                         pj_timestamp *timestamp) 
     63{ 
     64    pj_timestamp now; 
     65    unsigned elapsed_ms; 
     66     
     67    PJ_ASSERT_RETURN(clocksrc && timestamp, PJ_EINVAL); 
     68 
     69    pj_get_timestamp(&now); 
     70    elapsed_ms = pj_elapsed_msec(&clocksrc->last_update, &now); 
     71    pj_memcpy(timestamp, &clocksrc->timestamp, sizeof(pj_timestamp)); 
     72    pj_add_timestamp32(timestamp, elapsed_ms * clocksrc->clock_rate / 1000); 
     73 
     74    return PJ_SUCCESS; 
     75} 
     76 
     77/* API: Get clock source's time (in ms) */ 
     78PJ_DEF(pj_uint32_t) 
     79pjmedia_clock_src_get_time_msec( const pjmedia_clock_src *clocksrc ) 
     80{ 
     81    pj_timestamp ts; 
     82 
     83    pjmedia_clock_src_get_current_timestamp(clocksrc, &ts); 
     84 
     85#if PJ_HAS_INT64 
     86    if (ts.u64 > 0x3FFFFFFFFFFFFFUL) 
     87        return (pj_uint32_t)(ts.u64 / clocksrc->clock_rate * 1000); 
     88    else 
     89        return (pj_uint32_t)(ts.u64 * 1000 / clocksrc->clock_rate); 
     90#elif PJ_HAS_FLOATING_POINT 
     91    return (pj_uint32_t)((1.0 * ts.u32.hi * 0xFFFFFFFFUL + ts.u32.lo) 
     92                         * 1000.0 / clocksrc->clock_rate); 
     93#else 
     94    if (ts.u32.lo > 0x3FFFFFUL) 
     95        return (pj_uint32_t)(0xFFFFFFFFUL / clocksrc->clock_rate * ts.u32.hi  
     96                             * 1000UL + ts.u32.lo / clocksrc->clock_rate * 
     97                             1000UL); 
     98    else 
     99        return (pj_uint32_t)(0xFFFFFFFFUL / clocksrc->clock_rate * ts.u32.hi  
     100                             * 1000UL + ts.u32.lo * 1000UL / 
     101                             clocksrc->clock_rate); 
     102#endif 
     103} 
    26104 
    27105 
  • pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/port.c

    r3392 r3402  
    7676 
    7777/** 
     78 * Get a clock source from the port. 
     79 */ 
     80PJ_DEF(pjmedia_clock_src *) pjmedia_port_get_clock_src( pjmedia_port *port, 
     81                                                        pjmedia_dir dir ) 
     82{ 
     83    if (port && port->get_clock_src) 
     84        return port->get_clock_src(port, dir); 
     85    else 
     86        return NULL; 
     87} 
     88 
     89/** 
    7890 * Get a frame from the port (and subsequent downstream ports). 
    7991 */ 
  • pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/sound_port.c

    r3392 r3402  
    4545    pjmedia_port        *port; 
    4646 
     47    pjmedia_clock_src    cap_clocksrc, 
     48                         play_clocksrc; 
     49 
    4750    unsigned             clock_rate; 
    4851    unsigned             channel_count; 
     
    7073    pj_status_t status; 
    7174 
     75    pjmedia_clock_src_update(&snd_port->play_clocksrc, &frame->timestamp); 
     76 
    7277    port = snd_port->port; 
    7378    if (port == NULL) 
     
    127132    pjmedia_port *port; 
    128133 
     134    pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp); 
     135 
    129136    port = snd_port->port; 
    130137    if (port == NULL) 
     
    137144 
    138145    pjmedia_port_put_frame(port, frame); 
     146 
    139147 
    140148    return PJ_SUCCESS; 
     
    418426    pjmedia_snd_port *snd_port; 
    419427    pj_status_t status; 
     428    unsigned ptime_usec; 
    420429 
    421430    PJ_ASSERT_RETURN(pool && prm && p_port, PJ_EINVAL); 
     
    433442    snd_port->bits_per_sample = prm->bits_per_sample; 
    434443    pj_memcpy(&snd_port->aud_param, prm, sizeof(*prm)); 
     444 
     445    ptime_usec = prm->samples_per_frame * 1000 / prm->channel_count / 
     446                 prm->clock_rate * 1000; 
     447    pjmedia_clock_src_init(&snd_port->cap_clocksrc, PJMEDIA_TYPE_AUDIO, 
     448                           snd_port->clock_rate, ptime_usec); 
     449    pjmedia_clock_src_init(&snd_port->play_clocksrc, PJMEDIA_TYPE_AUDIO, 
     450                           snd_port->clock_rate, ptime_usec); 
    435451     
    436452    /* Start sound device immediately. 
     
    633649 
    634650/* 
     651 * Get clock source. 
     652 */ 
     653PJ_DEF(pjmedia_clock_src *) 
     654pjmedia_snd_port_get_clock_src( pjmedia_snd_port *snd_port, 
     655                                pjmedia_dir dir ) 
     656{ 
     657    return (dir == PJMEDIA_DIR_CAPTURE? &snd_port->cap_clocksrc: 
     658            &snd_port->play_clocksrc); 
     659} 
     660 
     661 
     662/* 
    635663 * Connect a port. 
    636664 */ 
  • 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); 
  • pjproject/branches/projects/2.0-dev/pjsip-apps/src/samples/aviplay.c

    r3401 r3402  
    9191{ 
    9292    pjmedia_vid_port   *vid_port; 
    93     pjmedia_aud_stream *aud_stream; 
     93    pjmedia_snd_port   *snd_port; 
    9494    pj_bool_t           is_running; 
    9595    pj_bool_t           is_quitting; 
     
    105105    pjmedia_converter   *conv; 
    106106} codec_port_data_t; 
    107  
    108 static pj_status_t avi_play_cb(void *user_data, pjmedia_frame *frame) 
    109 { 
    110     return pjmedia_port_get_frame((pjmedia_port*)user_data, frame); 
    111 } 
    112107 
    113108static pj_status_t avi_event_cb(pjmedia_vid_stream *stream, 
     
    126121        if (ap->is_running) { 
    127122            pjmedia_vid_port_stop(ap->vid_port); 
    128             if (ap->aud_stream) 
    129                 pjmedia_aud_stream_stop(ap->aud_stream); 
     123            if (ap->snd_port) 
     124                pjmedia_aud_stream_stop( 
     125                    pjmedia_snd_port_get_snd_stream(ap->snd_port)); 
    130126        } else { 
    131127            pjmedia_vid_port_start(ap->vid_port); 
    132             if (ap->aud_stream) 
    133                 pjmedia_aud_stream_start(ap->aud_stream); 
     128            if (ap->snd_port) 
     129                pjmedia_aud_stream_start( 
     130                    pjmedia_snd_port_get_snd_stream(ap->snd_port)); 
    134131        } 
    135132        ap->is_running = !ap->is_running; 
     
    187184    pjmedia_vid_port *renderer=NULL; 
    188185    pjmedia_vid_port_param param; 
    189     pjmedia_aud_param aparam; 
    190186    const pjmedia_video_format_info *vfi; 
    191187    pjmedia_video_format_detail *vfd; 
    192     pjmedia_audio_format_detail *afd; 
    193     pjmedia_aud_stream *strm = NULL; 
     188    pjmedia_snd_port *snd_port = NULL; 
    194189    pj_status_t status; 
    195190    int rc = 0; 
     
    376371     
    377372    if (aud_port) { 
    378         status = pjmedia_aud_dev_default_param( 
    379                      PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV, 
    380                      &aparam); 
     373        /* Create sound player port. */ 
     374        status = pjmedia_snd_port_create_player(  
     375                 pool,                              /* pool                 */ 
     376                 -1,                                /* use default dev.     */ 
     377                 PJMEDIA_PIA_SRATE(&aud_port->info),/* clock rate.          */ 
     378                 PJMEDIA_PIA_CCNT(&aud_port->info), /* # of channels.       */ 
     379                 PJMEDIA_PIA_SPF(&aud_port->info),  /* samples per frame.   */ 
     380                 PJMEDIA_PIA_BITS(&aud_port->info), /* bits per sample.     */ 
     381                 0,                                 /* options              */ 
     382                 &snd_port                          /* returned port        */ 
     383                 ); 
    381384        if (status != PJ_SUCCESS) { 
    382385            rc = 310; goto on_return; 
    383386        } 
    384387         
    385         aparam.dir = PJMEDIA_DIR_PLAYBACK; 
    386         afd = pjmedia_format_get_audio_format_detail(&aud_port->info.fmt, 
    387                                                      PJ_TRUE); 
    388         aparam.clock_rate = afd->clock_rate; 
    389         aparam.channel_count = afd->channel_count; 
    390         aparam.bits_per_sample = afd->bits_per_sample; 
    391         aparam.samples_per_frame = afd->frame_time_usec * aparam.clock_rate * 
    392                                    aparam.channel_count / 1000000; 
    393          
    394         status = pjmedia_aud_stream_create(&aparam, NULL, &avi_play_cb, 
    395                                            aud_port, 
    396                                            &strm); 
    397         if (status != PJ_SUCCESS) { 
    398             rc = 320; goto on_return; 
    399         } 
    400          
    401         /* Start audio streaming.. */ 
    402         status = pjmedia_aud_stream_start(strm); 
     388        /* Connect file port to the sound player. 
     389         * Stream playing will commence immediately. 
     390         */ 
     391        status = pjmedia_snd_port_connect(snd_port, aud_port); 
    403392        if (status != PJ_SUCCESS) { 
    404393            rc = 330; goto on_return; 
     
    411400        pj_bzero(&cb, sizeof(cb)); 
    412401        cb.on_event_cb = avi_event_cb; 
    413         avi_port.aud_stream = strm; 
     402        avi_port.snd_port = snd_port; 
    414403        avi_port.vid_port = renderer; 
    415404        avi_port.is_running = PJ_TRUE; 
    416405        pjmedia_vid_port_set_cb(renderer, &cb, &avi_port); 
     406 
     407        if (snd_port) { 
     408            /* Synchronize video rendering and audio playback */ 
     409            pjmedia_vid_port_set_clock_src( 
     410                renderer, PJMEDIA_DIR_RENDER, 
     411                pjmedia_snd_port_get_clock_src( 
     412                    snd_port, PJMEDIA_DIR_PLAYBACK)); 
     413        } 
     414                                               
    417415         
    418416        /* Start video streaming.. */ 
     
    428426 
    429427on_return: 
    430     if (strm) { 
    431         pjmedia_aud_stream_stop(strm); 
    432         pjmedia_aud_stream_destroy(strm); 
     428    if (snd_port) { 
     429        pjmedia_snd_port_disconnect(snd_port); 
     430        /* Without this sleep, Windows/DirectSound will repeteadly 
     431         * play the last frame during destroy. 
     432         */ 
     433        pj_thread_sleep(100); 
     434        pjmedia_snd_port_destroy(snd_port); 
    433435    } 
    434436    if (renderer) 
    435437        pjmedia_vid_port_destroy(renderer); 
     438    if (aud_port) 
     439        pjmedia_port_destroy(aud_port); 
    436440    if (vid_port) 
    437441        pjmedia_port_destroy(vid_port); 
Note: See TracChangeset for help on using the changeset viewer.