使用三方摄像头,实现pjsip的视频通话功能

提要:

近期一直在做视频通话功能,主要基于pjsip来实现的,将这些过程记录下来,可能对做同类型工作的同学有所帮助!


实现思路,参考pjsip原来设备采集视频、编码并rtp组包发送的思路,再在原有流程中做修改!

主要关键点:

1、摄像头采集完成后已经是已编码的H264/H265的流,不需要再开启pjsip的编码/解码流程;

2、组包发送,H264的FU-A组包、PS封装发送;


首先梳理流程,具体包括下面几个点:

1. 摄像头设备适配(这里可以考虑多个数据源,包括文件、socket或者摄像头的数据源;)

2. 参考android_dev.c 实现一个ov5000_dev.c

/* $Id$ */
/*
 * Copyright (C) 2015 Teluu Inc. (http://www.teluu.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "util.h"
#include <pjmedia-videodev/videodev_imp.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/math.h>
#include <pj/os.h>

//#define ARM_LINUX
#define VIDEO_USE_SOCKET
 


extern void set_take_ps_packet(int enable_value);
int log_printf(int a,...)
{
    return 0;
}
#define PJMEDIA_VIDEO_DEV_HAS_OV5000 1
#if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \
    defined(PJMEDIA_VIDEO_DEV_HAS_OV5000) 
 

#define THIS_FILE		"ov5000_dev.c"

/* Default video params */
#define DEFAULT_CLOCK_RATE	90000
#define DEFAULT_WIDTH		352
#define DEFAULT_HEIGHT		288
#define DEFAULT_FPS		15
#define ALIGN16(x)		((((x)+15) >> 4) << 4)

/* Define whether we should maintain the aspect ratio when rotating the image.
 * For more details, please refer to util.h.
 */
#define MAINTAIN_ASPECT_RATIO 	PJ_TRUE

#define JNIEnv (void *)
/* Format map info */
typedef struct ov5000_fmt_map
{
    pjmedia_format_id   fmt_id;
    pj_uint32_t		ov5000_fmt_id;
} ov5000_fmt_map;


/* Format map.
 * Note: it seems that most of Ov5000 devices don't support I420, while
 * unfortunately, our converter (libyuv based) only support I420 & RGBA,
 * so in this case, we'd just pretend that we support I420 and we'll do
 * the NV21/YV12 -> I420 conversion here.
 */
static ov5000_fmt_map fmt_map[] =
{
    {PJMEDIA_FORMAT_NV21, 0x00000011},
    {PJMEDIA_FORMAT_YV12, 0x32315659},
    {PJMEDIA_FORMAT_I420, 0x00000023}, /* YUV_420_888 */
};


/* Device info */
typedef struct ov5000_dev_info
{
    pjmedia_vid_dev_info	 info;		/**< Base info         */
    unsigned			 dev_idx;	/**< Original dev ID   */
    pj_bool_t			 facing;	/**< Front/back camera?*/
    unsigned			 sup_size_cnt;	/**< # of supp'd size  */
    pjmedia_rect_size		*sup_size;	/**< Supported size    */
    unsigned			 sup_fps_cnt;	/**< # of supp'd FPS   */
    pjmedia_rect_size		*sup_fps;	/**< Supported FPS     */
    pj_bool_t			 has_yv12;	/**< Support YV12?     */
    pj_bool_t			 has_nv21;	/**< Support NV21?     */
    pj_bool_t			 forced_i420;	/**< Support I420 with
						     conversion		*/
} ov5000_dev_info;


/* Video factory */
typedef struct ov5000_factory
{
    pjmedia_vid_dev_factory	 base;		/**< Base factory      */
    pj_pool_t			*pool;		/**< Memory pool       */
    pj_pool_factory		*pf;		/**< Pool factory      */

    pj_pool_t			*dev_pool;	/**< Device list pool  */
    unsigned			 dev_count;	/**< Device count      */
    ov5000_dev_info		*dev_info;	/**< Device info list  */
} ov5000_factory;


/* Video stream. */
typedef struct ov5000_stream
{
    pjmedia_vid_dev_stream  base;		/**< Base stream       */
    pjmedia_vid_dev_param   param;		/**< Settings	       */
    pj_pool_t		   *pool;		/**< Memory pool       */
    ov5000_factory		   *factory;            /**< Factory           */
    
    pjmedia_vid_dev_cb	    vid_cb;		/**< Stream callback   */
    void		   *user_data;          /**< Application data  */
    pj_bool_t		    is_running;		/**< Stream running?   */
    
    void*		    jcam;		/**< PjCamera instance */

    pj_timestamp            frame_ts;		/**< Current timestamp */
    unsigned                ts_inc;		/**< Timestamp interval*/
    unsigned		    convert_to_i420;	/**< Need to convert to I420?
						     0: no
						     1: from NV21
						     2: from YV12	*/
    
    /** Capture thread info */
    pj_bool_t		    thread_initialized;
    pj_thread_desc	    thread_desc;
    pj_thread_t		   *thread;

    /** NV21/YV12 -> I420 Conversion buffer  */
    pj_uint8_t		   *convert_buf;
    pjmedia_rect_size	    cam_size;
    
    /** Converter to rotate frame  */
    pjmedia_vid_dev_conv    conv;

    //
    pj_uint8_t *sps_pps;
	int sps_pps_p_index;
	int pps_len;
	int sps_pps_len;
	int recive_video_packet_count;
    
    
    /** Frame format param for NV21/YV12 -> I420 conversion */
    pjmedia_video_apply_fmt_param vafp;

    //capture thread.
    pj_thread_t		*ca_thread;
} ov5000_stream;


/* Prototypes */
static pj_status_t ov5000_factory_init(pjmedia_vid_dev_factory *f);
static pj_status_t ov5000_factory_destroy(pjmedia_vid_dev_factory *f);
static pj_status_t ov5000_factory_refresh(pjmedia_vid_dev_factory *f); 
static unsigned    ov5000_factory_get_dev_count(pjmedia_vid_dev_factory *f);
static pj_status_t ov5000_factory_get_dev_info(pjmedia_vid_dev_factory *f,
					    unsigned index,
					    pjmedia_vid_dev_info *info);
static pj_status_t ov5000_factory_default_param(pj_pool_t *pool,
                                             pjmedia_vid_dev_factory *f,
					     unsigned index,
					     pjmedia_vid_dev_param *param);
static pj_status_t ov5000_factory_create_stream(
					pjmedia_vid_dev_factory *f,
					pjmedia_vid_dev_param *param,
					const pjmedia_vid_dev_cb *cb,
					void *user_data,
					pjmedia_vid_dev_stream **p_vid_strm);


static pj_status_t ov5000_stream_get_param(pjmedia_vid_dev_stream *strm,
                                        pjmedia_vid_dev_param *param);
static pj_status_t ov5000_stream_get_cap(pjmedia_vid_dev_stream *strm,
                                      pjmedia_vid_dev_cap cap,
                                      void *value);
static pj_status_t ov5000_stream_set_cap(pjmedia_vid_dev_stream *strm,
                                      pjmedia_vid_dev_cap cap,
                                      const void *value);
static pj_status_t ov5000_stream_start(pjmedia_vid_dev_stream *strm);
static pj_status_t ov5000_stream_stop(pjmedia_vid_dev_stream *strm);
static pj_status_t ov5000_stream_destroy(pjmedia_vid_dev_stream *strm);
static void  OnGetFrame2(uint8_t* data, int length, void* user_data);


/* Operations */
static pjmedia_vid_dev_factory_op factory_op =
{
    &ov5000_factory_init,
    &ov5000_factory_destroy,
    &ov5000_factory_get_dev_count,
    &ov5000_factory_get_dev_info,
    &ov5000_factory_default_param,
    &ov5000_factory_create_stream,
    &ov5000_factory_refresh
};

static pjmedia_vid_dev_stream_op stream_op =
{
    &ov5000_stream_get_param,
    &ov5000_stream_get_cap,
    &ov5000_stream_set_cap,
    &ov5000_stream_start,
    NULL,
    NULL,
    &ov5000_stream_stop,
    &ov5000_stream_destroy
};

 
#endif	/* PJMEDIA_VIDEO_DEV_HAS_Ov5000 */


3. ov5000_dev摄像头采集的数据最终会回调到strm->vid_cb.capture_cb ,这个回调方法是video_port.c中的vidstream_cap_cb方法;

static void copy_frame_to_buffer(pjmedia_vid_port *vp,
                                 pjmedia_frame *frame)
{
	if (frame == NULL){
		return;
	}
    pj_mutex_lock(vp->frm_mutex);
	#if PJMEDIA_VIDEO_DEV_HAS_OV5000
	 //PJ_LOG(4, (THIS_FILE, "-1--copy_frame_to_buffer: frame.size:%d, %d len", frame->size, vp->frm_buf->size));
		#if 1
        if (vp->frm_buf == NULL){
    		pj_mutex_unlock(vp->frm_mutex);
			return;
		}

		ringput(frame->buf, frame->size, 0);
		vp->frm_buf->size = frame->size;
		    vp->frm_buf->type = frame->type;
		    vp->frm_buf->timestamp = frame->timestamp;
		    vp->frm_buf->bit_info = frame->bit_info;
		#else
		//direct put frame?
    		pjmedia_frame_copy(vp->frm_buf, frame);
		
		#endif
	#endif//
    pj_mutex_unlock(vp->frm_mutex);
}


另外,vid_port中的frm_buf缓存数据太少,数据帧存在明显的跳帧现象,所以参考之前的fifobuffer思路,实现了一个fifobuffer;

//add for ring buffer
#if PJMEDIA_VIDEO_DEV_HAS_OV5000
pthread_mutex_t ring_mutex;

struct ringbuf {
    unsigned char *buffer;
	int frame_type;
    int size;
};
static int addring (int i);
static int ringget(struct ringbuf *getinfo);
static void ringput(unsigned char *buffer,int size,int encode_type);
static void ringfree();
static void ringmalloc(int size);
static void ringreset();


#define NMAX 10//30
#define RING_BUFFER_SIZE 145000//50000
 
static volatile  int iput = 0; /*   */
static volatile  int iget = 0; /* */
static volatile  int n = 0; /* */
#define USE_MALLOC_MEM
#ifndef USE_MALLOC_MEM
static uint8_t mem_buffer[RING_BUFFER_SIZE*NMAX];
#endif
static volatile struct ringbuf ringfifo[NMAX]; 
static volatile int init_flag = 0;

static void ringmalloc(int size)
{
    int i;
	#ifdef USE_MALLOC_MEM
	//
    pthread_mutex_init(&ring_mutex, 0);
	if (init_flag){ 
	    return;
	}
    for(i =0; i<NMAX; i++)
    {  
        ringfifo[i].buffer = malloc(size);
        ringfifo[i].size = 0;
        ringfifo[i].frame_type = 0;
       // printf("FIFO INFO:idx:%d,len:%d,ptr:%x\n",i,ringfifo[i].size,(int)(ringfifo[i].buffer));
    }
	init_flag = 1;
	#else
    for(i =0; i<NMAX; i++)
    { 
        ringfifo[i].buffer = &mem_buffer[i*RING_BUFFER_SIZE];
        ringfifo[i].size = 0;
        ringfifo[i].frame_type = 0;
       // printf("FIFO INFO:idx:%d,len:%d,ptr:%x\n",i,ringfifo[i].size,(int)(ringfifo[i].buffer));
    }
	#endif
    iput = 0; /* ?隆陇D??o3???娄脤?娄脤隆脌?隆茫隆陇?篓篓????? */
    iget = 0; /* ?o3???娄脤?娄脤隆脌?隆茫篓篓?3????? */
    n = 0; /* ?隆陇D??o3????D娄脤??a??隆脕篓鹿篓潞y篓垄? */
}
/**************************************************************************************************
**
**
**
**************************************************************************************************/
static void ringreset()
{

    pthread_mutex_lock(&ring_mutex);
    iput = 0; /* ?隆陇D??o3???娄脤?娄脤隆脌?隆茫隆陇?篓篓????? */
    iget = 0; /* ?o3???娄脤?娄脤隆脌?隆茫篓篓?3????? */
    n = 0; /* ?隆陇D??o3????D娄脤??a??隆脕篓鹿篓潞y篓垄? */ 
    pthread_mutex_unlock(&ring_mutex);
}
/**************************************************************************************************
**
**
**
**************************************************************************************************/
static void ringfree(void)
{
    int i;
    printf("begin free mem\n");
    for(i =0; i<NMAX; i++)
    {
       // printf("FREE FIFO INFO:idx:%d,len:%d,ptr:%x\n",i,ringfifo[i].size,(int)(ringfifo[i].buffer));
	#ifdef USE_MALLOC_MEM
       free(ringfifo[i].buffer);
	 ringfifo[i].buffer = NULL;
	#endif//#ifdef USE_MALLOC_MEM
        ringfifo[i].size = 0;
    }
	init_flag = 0;
    //pthread_mutex_destroy(&ring_mutex);
}
/**************************************************************************************************
**
**
**
**************************************************************************************************/
static int addring(int i)
{
    return (i+1) == NMAX ? 0 : i+1;
}

/**************************************************************************************************
**
**
**
**************************************************************************************************/ 

static int ringget(struct ringbuf *getinfo)
{
    int Pos;
    if(n>0)
    {
    	pthread_mutex_lock(&ring_mutex);
        Pos = iget;
        iget = addring(iget);
        n--;
        getinfo->buffer = (ringfifo[Pos].buffer);
		if (getinfo->buffer == NULL){
    		pthread_mutex_unlock(&ring_mutex);
			return 0;
		}
        getinfo->frame_type = ringfifo[Pos].frame_type;
        getinfo->size = ringfifo[Pos].size;
    	pthread_mutex_unlock(&ring_mutex);
        //printf("Get FIFO INFO:idx:%d,len:%d,ptr:%x,type:%d\n",Pos,getinfo->size,(int)(getinfo->buffer),getinfo->frame_type);
        return ringfifo[Pos].size;
    }
    else
    {
        //printf("Buffer is empty\n");
        return 0;
    }
}
/**************************************************************************************************
**
**
**
**************************************************************************************************/ 
static void ringput(unsigned char *buffer,int size,int encode_type)
{

    if (size > RING_BUFFER_SIZE){
	    PJ_PERROR(4,(THIS_FILE, 0, "Error ringput, size:%d > %d", size, RING_BUFFER_SIZE));
		return;
    }
    if(n >= 0 && n<NMAX)
    {
    	pthread_mutex_lock(&ring_mutex);
		if (ringfifo[iput].buffer == NULL){
    		pthread_mutex_unlock(&ring_mutex);
			return;
		}
		if (size > RING_BUFFER_SIZE){
    			//pthread_mutex_unlock(&ring_mutex);
			//return;
			ringfifo[iput].buffer = realloc(ringfifo[iput].buffer, size);
		} 
        memcpy(ringfifo[iput].buffer,buffer,size);
        ringfifo[iput].size= size;
        ringfifo[iput].frame_type = encode_type;
        //printf("Put FIFO INFO:idx:%d,len:%d,ptr:%x,type:%d\n",iput,ringfifo[iput].size,(int)(ringfifo[iput].buffer),ringfifo[iput].frame_type);
        iput = addring(iput);
    	pthread_mutex_unlock(&ring_mutex);
        n++;
    }
    else
    {
        //  printf("Buffer is full\n");
    }
}
 
#endif
//add end.


vid_port.c中的get_frame_from_buffer做如下修改:
static pj_status_t get_frame_from_buffer(pjmedia_vid_port *vp,
                                         pjmedia_frame *frame)
{
    pj_status_t status = PJ_SUCCESS;

    pj_mutex_lock(vp->frm_mutex);
    if (vp->conv.conv)
        status = convert_frame(vp, vp->frm_buf, frame);
    else{
		
		//for bug
		//PJ_LOG(4, (THIS_FILE, "-1--get_frame_from_buffer: frame.size:%d, %d len", frame->size, vp->frm_buf->size));
	 
		#if PJMEDIA_VIDEO_DEV_HAS_OV5000
		struct ringbuf ringitem ;
		int itemlen  = ringget(&ringitem);
		if (itemlen > 0 && frame->buf != NULL){
			int copy_len = itemlen;
			if (itemlen > frame->size){
				copy_len = frame->size;	
			}
			memcpy(frame->buf, ringitem.buffer, copy_len);
			frame->size = copy_len;
		}else{
		    frame->size = 0;
	    		pj_mutex_unlock(vp->frm_mutex);
			return -1;
		}
		if (vp->frm_buf != NULL){
	       frame->type = vp->frm_buf->type;
	       frame->timestamp = vp->frm_buf->timestamp;
	       frame->bit_info = vp->frm_buf->bit_info;
		   //PJ_LOG(4, (THIS_FILE, "-2--get_frame_from_buffer: frame.size:%d, %d len, itemlen:%d", frame->size, vp->frm_buf->size, itemlen));
    
	    }
		#else
		int itemlen  = vp->frm_buf->size;
		pjmedia_frame_copy(frame, vp->frm_buf);
		#endif
		}
    pj_mutex_unlock(vp->frm_mutex);
    
    return status;
}



3. 视频帧获取驱动定时器适配,调试发现视频流卡顿明显,发现是驱动的定时器跑的太慢,涉及到vid_conf.c中的on_clock_tick方法。

    clock_param.clock_rate = 900000;//TS_CLOCK_RATE;   
    clock_param.usec_interval = 1000000 /1000;// vid_conf->opt.frame_rate;
    status = pjmedia_clock_create2(pool, &clock_param, 0, &on_clock_tick,
                                   vid_conf, &vid_conf->clock);
                                   
                                   
	#if PJMEDIA_VIDEO_DEV_HAS_OV5000//lyz@ no need converter

	vp->conv.conv_param.src.id = vp->conv.conv_param.dst.id;
	vp->conv.conv_param.src.det.vid.size.w = vp->conv.conv_param.dst.det.vid.size.w;
	vp->conv.conv_param.src.det.vid.size.h= vp->conv.conv_param.dst.det.vid.size.h;
	//vp->role = ROLE_ACTIVE;
    	//return PJ_SUCCESS;
	#endif
	
vconf_port结构体中增加
    pj_size_t		 get_buf_real_size;	/**< Data size for get_frame().   */
    pj_size_t		put_buf_real_size;	/**< Data size for put_frame().   */

on_clock_tick方法中
                status = pjmedia_port_get_frame(src->port, &frame);
                if (status != PJ_SUCCESS) {
                    PJ_PERROR(5, (THIS_FILE, status,
                                  "Failed to get frame from port %d [%s]!",
                                  src->idx, src->port->info.name.ptr));
                    src->got_frame = PJ_FALSE;
                } else {
			#if PJMEDIA_VIDEO_DEV_HAS_OV5000//just set got_frame by 
		       //PJ_PERROR(4, (THIS_FILE, status, "get frame from port %d  ,len:%d, src_buf_size:%d!", src->idx, frame.size, src->get_buf_size));
			src->got_frame = PJ_TRUE;
			src->get_buf_real_size = frame.size;
			#else
		    	src->got_frame = (frame.size == src->get_buf_size);
			#endif

                    /* There is a possibility that the source port's format has
                     * changed, but we haven't received the event yet.
                     */
                    cur_fmt = &src->format;
                    new_fmt = &src->port->info.fmt;
                    if (cmp_fps(cur_fmt, new_fmt) ||
                        cmp_size(cur_fmt, new_fmt))
                    {
                        op_param prm;
                        prm.update_port.port = src->idx;
                        op_update_port(vid_conf, &prm);
                    }
                }

                render_src_frame方法中做下面的修改
static pj_status_t render_src_frame(vconf_port *src, vconf_port *sink,
                                    unsigned transmitter_idx)
    if (sink->transmitter_cnt == 1 && (!rs || !rs->converter)) {
        /* The only transmitter and no conversion needed */
	    #if PJMEDIA_VIDEO_DEV_HAS_OV5000//just set got_frame  
		int get_buf_size = src->get_buf_real_size < sink->put_buf_size?src->get_buf_real_size:sink->put_buf_size;
		sink->put_buf_real_size = get_buf_size;
		#else
		/* The only transmitter and no conversion needed */
		if (src->get_buf_size != sink->put_buf_size)
		    return PJMEDIA_EVID_BADFORMAT;
		int get_buf_size = src->put_buf_size;
		#endif//
		pj_memcpy(sink->put_buf, src->get_buf, get_buf_size);
    } else if (rs && rs->converter) {


4、rtp h264 fu-a组包发送和回调,涉及的文件,vid_stream.c 的 put_frame 方法。

   /*
	 mark need modify later.
   */
   #if PJMEDIA_VIDEO_DEV_HAS_OV5000
   int rtp_per_packet_len = 1200;//1300;
   int i=0;
   int send_len = 0;
   int reserved_len = frame->size;
   int data_start_index = 0;

#if 1//SUPPORT_PS_ENPACKED
	char ps_header[PS_HDR_LEN];

	char ps_system_header[SYS_HDR_LEN];

	char ps_map_header[PSM_HDR_LEN];

	char pes_header[PES_HDR_LEN];
	char temp_frame[1024 * 128];
#endif//

   uint8_t nalu = 0;
   uint8_t *data = (uint8_t *)frame->buf;
	if ( *data == 0x00 && *(data+1)==0x00 && *(data+2) == 0x00 && *(data+3) == 0x01){
		nalu = *(data+4);
		data_start_index = 4;
		if (reserved_len > rtp_per_packet_len){
		    //fu-a
			data_start_index = 5;			
		}
	}else  if ( *data == 0x00 && *(data+1)==0x00 && *(data+2) == 0x01 ){
		nalu = *(data+3); 
		data_start_index = 3;
		if (reserved_len > rtp_per_packet_len){
		    //fu-a
			data_start_index = 4;			 
		}
	}else{
		nalu = *(data);
		data_start_index = 0;
	}
    
      int index = 0;
	if (ps_packet_flag){
		
		int time_base = 90000;
		int fps = 24;
		int send_packet_interval = 1000 / fps;

		int interval = time_base / fps;
		stream->pts += interval;
	       long pts = stream->pts;
		//ps封装
		if (nalu == 0x67 || nalu == 0x68 || nalu == 0x65){
			//I frame
                gb28181_make_ps_header(ps_header, pts);
                memcpy(temp_frame,ps_header,PS_HDR_LEN);
                index += PS_HDR_LEN;
                gb28181_make_sys_header(ps_system_header, 0x3f);

                memcpy(temp_frame+ index, ps_system_header, SYS_HDR_LEN);
                index += SYS_HDR_LEN;

                gb28181_make_psm_header(ps_map_header);

                memcpy(temp_frame + index, ps_map_header, PSM_HDR_LEN);
                index += PSM_HDR_LEN;
			
		}else{

                gb28181_make_ps_header(ps_header, pts);

                memcpy(temp_frame, ps_header, PS_HDR_LEN);
                index += PS_HDR_LEN;
		}
            //封装pes
            gb28181_make_pes_header(pes_header, 0xe0, reserved_len, pts, pts);

            memcpy(temp_frame+index, pes_header, PES_HDR_LEN);
            index += PES_HDR_LEN;

            memcpy(temp_frame + index, data, reserved_len);
            index += reserved_len;
			
            data = temp_frame;
	      reserved_len = index;
	      data_start_index = 0; 
	}else{
		//data_start_index = 0;
		reserved_len -= data_start_index;
       }
   
    while(1){
   	    send_len = rtp_per_packet_len;
		if (reserved_len < rtp_per_packet_len){
			send_len = reserved_len;
			has_more_data = PJ_FALSE;
		}else{
			has_more_data = PJ_TRUE;
		}
		
		status = pjmedia_rtp_encode_rtp(&channel->rtp,
		                                channel->pt,
	                                (has_more_data == PJ_FALSE ? 1 : 0),
		                                (int)send_len,
	 	                                rtp_ts_len,
		                                (const void**)&rtphdr,
		                                &rtphdrlen);
		if (status != PJ_SUCCESS) {
		    LOGERR_((channel->port.info.name.ptr, status,
			    "RTP encode_rtp() error"));
		    return status;
		}
		/* When the payload length is zero, we should not send anything,
		 * but proceed the rest normally.
		 */
		 int fu_a_index = 0;
		uint8_t *p_data = (uint8_t *)channel->buf;
		if (reserved_len > 0) {
			#if 1
			if (frame->size > rtp_per_packet_len){
				//fu-a
				if (total_sent == 0){
					//start
					p_data[sizeof(pjmedia_rtp_hdr)] =  (nalu & 0x60) | 28;
					// |S|E|R|  Type   |
					//S 1 E 0 R 0
					p_data[sizeof(pjmedia_rtp_hdr)+1] = (1 << 7) | (nalu & 0x1f);
					fu_a_index += 2;
				}else{
					if (has_more_data){
						//end
						p_data[sizeof(pjmedia_rtp_hdr)] = 28;
						// |S|E|R|  Type   |
						//S 0 E 0 R 0
						p_data[sizeof(pjmedia_rtp_hdr)+1] = (nalu & 0x1f);
						fu_a_index += 2;
					}else{							//end
						p_data[sizeof(pjmedia_rtp_hdr)] = 28;
						// |S|E|R|  Type   |
						//S 0 E 1 R 0
						p_data[sizeof(pjmedia_rtp_hdr)+1] = (1 << 6) | (nalu & 0x1f);
						fu_a_index += 2;
					}
				} 
				//send_len+=fu_a_index;
			}
			#endif//no -fu-a
		    /* Copy RTP header to the beginning of packet */
		    pj_memcpy(channel->buf, rtphdr, sizeof(pjmedia_rtp_hdr));
			//copy data
		    pj_memcpy(channel->buf + fu_a_index + sizeof(pjmedia_rtp_hdr), data +total_sent + data_start_index, send_len+fu_a_index);

		    if (stream->transport == NULL){
				break;
		    }
		    /* Send the RTP packet to the transport. */
		    status = pjmedia_transport_send_rtp(stream->transport,
							(char*)channel->buf,
							send_len +
							    sizeof(pjmedia_rtp_hdr) +fu_a_index);
		    if (status != PJ_SUCCESS) {
				if (stream->rtp_tx_err_cnt++ == 0) {
				    LOGERR_((channel->port.info.name.ptr, status,
					     "Error sending RTP"));
				}
				if (stream->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) {
				    stream->rtp_tx_err_cnt = 0;
				}
				break;
		    }
		    pjmedia_rtcp_tx_rtp(&stream->rtcp, (unsigned)send_len);
		    //total_sent += frame_out.size;
			pj_thread_sleep(2);//2ms
		    pkt_cnt++;
		}
			
		/* Next packets use same timestamp */
		rtp_ts_len = 0; 
		
		reserved_len -= send_len; 
		total_sent += send_len;
		if (reserved_len <= 0){
			break;
		}
   	}
       //PJ_PERROR(4,(THIS_FILE, status,  "put_frame len:%d,total_sent:%d", frame->size,total_sent));
	goto ov5000_end;
   #endif
   
  ov5000_end:
#if TRACE_RC
    /* Trace log for rate control */
    {
        pj_timestamp end_time;
        unsigned total_sleep;

        pj_get_timestamp(&end_time);
        total_sleep = pj_elapsed_msec(&initial_time, &end_time);
        PJ_LOG(5, (stream->name.ptr, "total pkt=%d size=%d sleep=%d",
                   pkt_cnt, total_sent, total_sleep));

        if (stream->tx_start.u64 == 0)
            stream->tx_start = initial_time;
        stream->tx_end = end_time;
        stream->rc_total_pkt += pkt_cnt;
        stream->rc_total_sleep += total_sleep;
        stream->rc_total_img++;
    }
#endif


其他修改:

vid_port_destroy

	#if PJMEDIA_VIDEO_DEV_HAS_OV5000
	//free ringbuffer  
	ringfree();
	//add end.
	#endif

呱牛笔记

本文为呱牛笔记原创文章,转载无需和我联系,但请注明来自呱牛笔记 ,it3q.com

请先登录后发表评论
  • 最新评论
  • 总共0条评论