提要:
近期一直在做视频通话功能,主要基于pjsip来实现的,将这些过程记录下来,可能对做同类型工作的同学有所帮助!
实现思路,参考pjsip原来设备采集视频、编码并rtp组包发送的思路,再在原有流程中做修改!
主要关键点:
1、摄像头采集完成后已经是已编码的H264/H265的流,不需要再开启pjsip的编码/解码流程;
2、组包发送,H264的FU-A组包、PS封装发送;
首先梳理流程,具体包括下面几个点:
1. 摄像头设备适配(这里可以考虑多个数据源,包括文件、socket或者摄像头的数据源;)
2. 参考android_dev.c 实现一个ov5000_dev.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | /* $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方法;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | //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方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | 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 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | /* 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 |
其他修改:
1 2 3 4 5 6 7 | vid_port_destroy #if PJMEDIA_VIDEO_DEV_HAS_OV5000 //free ringbuffer ringfree(); //add end. #endif |
-------------------广告线---------------
项目、合作,欢迎勾搭,邮箱:promall@qq.com
本文为呱牛笔记原创文章,转载无需和我联系,但请注明来自呱牛笔记 ,it3q.com