提要:
近期一直在做视频通话功能,主要基于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 }; /**************************************************************************** * OTHER stuff */ /* Use camera2 (since Ov5000 API level 21) */ #define USE_CAMERA2 0 #if USE_CAMERA2 #define PJ_CAMERA "PjCamera2" #define PJ_CAMERA_INFO "PjCameraInfo2" #else #define PJ_CAMERA "PjCamera" #define PJ_CAMERA_INFO "PjCameraInfo" #endif #define PJ_CLASS_PATH "org/pjsip/" #define PJ_CAMERA_CLASS_PATH PJ_CLASS_PATH PJ_CAMERA #define PJ_CAMERA_INFO_CLASS_PATH PJ_CLASS_PATH PJ_CAMERA_INFO static volatile ov5000_stream *vid_stream =NULL; static void OnGetFrame(uint8_t* data, int length, void* user_data); typedef enum gb28181_history_play_source_type { PJMEDIA_VID_S_PLAY = 0, //s=play? s=Download s=Playback PJMEDIA_VID_S_DOWNLOAD, PJMEDIA_VID_S_PLAYBACK, PJMEDIA_VID_S_OTHER, } ; static char gb28181_history_video_id[255]; static int gb28181_history_play_source_type = PJMEDIA_VID_S_PLAY;// void set_gb28181_history_play_source_type(int source_type, char *history_file_id){ gb28181_history_play_source_type = source_type; if (source_type != PJMEDIA_VID_S_PLAY){ memset(gb28181_history_video_id, 0x00, sizeof(gb28181_history_video_id)); strcpy(gb28181_history_video_id, history_file_id); } } static volatile int ov_stream_capture_end = 0; static volatile int ov5000_samplerate = 600000; static volatile int ov5000_framerate = 25; /**************************************************************************** * Helper functions */ static pjmedia_format_id ov5000_fmt_to_pj(pj_uint32_t fmt) { unsigned i; for (i = 0; i < PJ_ARRAY_SIZE(fmt_map); i++) { if (fmt_map[i].ov5000_fmt_id == fmt) return fmt_map[i].fmt_id; } return 0; } static pj_uint32_t pj_fmt_to_and(pjmedia_format_id fmt) { unsigned i; for (i = 0; i < PJ_ARRAY_SIZE(fmt_map); i++) { if (fmt_map[i].fmt_id == fmt) return fmt_map[i].ov5000_fmt_id; } return 0; } /**************************************************************************** * Factory operations */ int video_encode_callback_test_f(const unsigned char *_data, int _data_len) { #if 1 pj_thread_t *call_thread = NULL; //脳垄虏谩脢脗录镁 if(!pj_thread_is_registered()) { pj_thread_desc thread_desc; if (pj_thread_register("ov5000_capture_thread_func", thread_desc, &call_thread) == PJ_SUCCESS) { printf("pj_thread_register ok.\r\n"); } } #endif //PJ_LOG(4, (THIS_FILE, "video_encode_callback_test_f")); OnGetFrame2(_data, _data_len, vid_stream); } /* * Init ov5000_ video driver. */ pjmedia_vid_dev_factory* pjmedia_ov5000_factory(pj_pool_factory *pf) { ov5000_factory *f; pj_pool_t *pool; pool = pj_pool_create(pf, "ov5000_video", 512, 512, NULL); f = PJ_POOL_ZALLOC_T(pool, ov5000_factory); f->pf = pf; f->pool = pool; f->base.op = &factory_op; f->dev_pool = pj_pool_create(pf, "ov5000_video_dev", 512, 512, NULL); return &f->base; } /* API: init factory */ static pj_status_t ov5000_factory_init(pjmedia_vid_dev_factory *ff) { pj_status_t status; status = ov5000_factory_refresh(ff); if (status != PJ_SUCCESS) return status; PJ_LOG(4, (THIS_FILE, "ov5000 video src initialized with 1 device(s)")); return PJ_SUCCESS; } /* API: destroy factory */ static pj_status_t ov5000_factory_destroy(pjmedia_vid_dev_factory *ff) { ov5000_factory *f = (ov5000_factory*)ff; pj_pool_safe_release(&f->dev_pool); pj_pool_safe_release(&f->pool); return PJ_SUCCESS; } /* API: refresh the list of devices */ static pj_status_t ov5000_factory_refresh(pjmedia_vid_dev_factory *ff) { ov5000_factory *f = (ov5000_factory*)ff; pj_status_t status = PJ_SUCCESS; pj_bool_t with_attach, found_front = PJ_FALSE; int i, dev_count = 1; //need adaptor by , recreate camera? /* Clean up device info and pool */ f->dev_count = 1; pj_pool_reset(f->dev_pool); /* Start querying device info */ f->dev_info = (ov5000_dev_info*) pj_pool_calloc(f->dev_pool, dev_count, sizeof(ov5000_dev_info)); for (i = 0; i < dev_count; i++) { ov5000_dev_info *adi = &f->dev_info[i]; pjmedia_vid_dev_info *vdi = &adi->info; /* Set device ID, direction, and has_callback info */ adi->dev_idx = i; //adi->has_nv21 = 1; vdi->fmt_cnt = 1; vdi->id = f->dev_count; vdi->dir = PJMEDIA_DIR_CAPTURE; vdi->has_callback = PJ_TRUE; vdi->caps = PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW; adi->forced_i420 = 1; adi->sup_size_cnt = 1; /* Landscape video */ adi->sup_size = pj_pool_calloc(f->dev_pool, adi->sup_size_cnt, sizeof(adi->sup_size[0])); adi->sup_size[0].w = 640; adi->sup_size[0].h = 480; pjmedia_format_init_video(&vdi->fmt[vdi->fmt_cnt++], PJMEDIA_FORMAT_I420, adi->sup_size[0].w, adi->sup_size[0].h, DEFAULT_FPS, 1); /* Set driver & name info */ pj_ansi_strncpy(vdi->driver, "linux", sizeof(vdi->driver)); pj_ansi_strncpy(vdi->name, "ov5000", sizeof(vdi->name)); } return status; } /* API: get number of devices */ static unsigned ov5000_factory_get_dev_count(pjmedia_vid_dev_factory *ff) { ov5000_factory *f = (ov5000_factory*)ff; return f->dev_count; } /* API: get device info */ static pj_status_t ov5000_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info) { ov5000_factory *cf = (ov5000_factory*)f; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV); pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info)); return PJ_SUCCESS; } /* API: create default device parameter */ static pj_status_t ov5000_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param) { ov5000_factory *cf = (ov5000_factory*)f; ov5000_dev_info *di = &cf->dev_info[index]; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV); PJ_UNUSED_ARG(pool); pj_bzero(param, sizeof(*param)); param->dir = PJMEDIA_DIR_CAPTURE; param->cap_id = index; param->rend_id = PJMEDIA_VID_INVALID_DEV; param->flags = PJMEDIA_VID_DEV_CAP_FORMAT; param->clock_rate = DEFAULT_CLOCK_RATE; pj_memcpy(¶m->fmt, &di->info.fmt[0], sizeof(param->fmt)); return PJ_SUCCESS; } /* API: create stream */ static pj_status_t ov5000_factory_create_stream( pjmedia_vid_dev_factory *ff, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm) { ov5000_factory *f = (ov5000_factory*)ff; pj_pool_t *pool; ov5000_stream *strm; ov5000_dev_info *adi; const pjmedia_video_format_detail *vfd; const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; pj_uint32_t ov5000_fmt = 0; unsigned convert_to_i420 = 0; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO && param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO && param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); /* Camera2 supports only I420 for now */ #if USE_CAMERA2 if (param->fmt.id != PJMEDIA_FORMAT_I420) return PJMEDIA_EVID_BADFORMAT; #endif pj_bzero(&vafp, sizeof(vafp)); adi = &f->dev_info[param->cap_id]; vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE); vfi = pjmedia_get_video_format_info(NULL, param->fmt.id); if (param->fmt.id == PJMEDIA_FORMAT_I420 && adi->forced_i420) { convert_to_i420 = 0; } if (!vfi) return PJMEDIA_EVID_BADFORMAT; vafp.size = vfd->size; if (vfi->apply_fmt(vfi, &vafp) != PJ_SUCCESS) return PJMEDIA_EVID_BADFORMAT; /* Create and Initialize stream descriptor */ pool = pj_pool_create(f->pf, "ov5000-dev", 512, 512, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); strm = PJ_POOL_ZALLOC_T(pool, ov5000_stream); pj_memcpy(&strm->param, param, sizeof(*param)); strm->pool = pool; strm->factory = f; pj_memcpy(&strm->vid_cb, cb, sizeof(*cb)); strm->user_data = user_data; pj_memcpy(&strm->vafp, &vafp, sizeof(vafp)); strm->ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1); strm->sps_pps = (pj_uint8_t*) pj_pool_calloc(f->dev_pool, 56, sizeof(pj_uint8_t)); strm->recive_video_packet_count = 0; strm->sps_pps_len = 0; /* Allocate buffer for YV12 -> I420 conversion. * The camera2 is a bit tricky with format, for example it reports * for I420 support (and no NV21 support), however the incoming frame * buffers are actually in NV21 format (e.g: pixel stride is 2), so * we should always check and conversion buffer may be needed. */ #if 0 if (USE_CAMERA2 || convert_to_i420) { pj_assert(vfi->plane_cnt > 1); strm->convert_to_i420 = convert_to_i420; strm->convert_buf = pj_pool_alloc(pool, vafp.plane_bytes[1]); } /* Native preview */ if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW) { } strm->jcam = NULL; //need adaptor by , create camera? /* Video orientation. * If we send in portrait, we need to set up orientation converter * as well. */ if ((param->flags & PJMEDIA_VID_DEV_CAP_ORIENTATION) || (vfd->size.h > vfd->size.w)) { if (param->orient == PJMEDIA_ORIENT_UNKNOWN) param->orient = PJMEDIA_ORIENT_NATURAL; ov5000_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_ORIENTATION, ¶m->orient); } #endif// // add for adaptor. vid_stream = &strm->base; on_return: /* Success */ if (status == PJ_SUCCESS) { strm->base.op = &stream_op; *p_vid_strm = &strm->base; } return status; } /**************************************************************************** * Stream operations */ /* API: Get stream info. */ static pj_status_t ov5000_stream_get_param(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_param *pi) { ov5000_stream *strm = (ov5000_stream*)s; PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); pj_memcpy(pi, &strm->param, sizeof(*pi)); if (ov5000_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW, &pi->window) == PJ_SUCCESS) { pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW; } return PJ_SUCCESS; } /* API: get capability */ static pj_status_t ov5000_stream_get_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, void *pval) { ov5000_stream *strm = (ov5000_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) { //pjmedia_vid_dev_hwnd *wnd = (pjmedia_vid_dev_hwnd *)pval; //wnd->info.Ov5000.window = strm->window; //return PJ_SUCCESS; } return PJMEDIA_EVID_INVCAP; } /* API: set capability */ static pj_status_t ov5000_stream_set_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, const void *pval) { ov5000_stream *strm = (ov5000_stream*)s; pj_bool_t with_attach; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); switch (cap) { case PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW: { } break; case PJMEDIA_VID_DEV_CAP_SWITCH: { pjmedia_vid_dev_switch_param *p = (pjmedia_vid_dev_switch_param*) pval; ov5000_dev_info *adi; int res; /* Just return if current and target device are the same */ if (strm->param.cap_id == p->target_id) return PJ_SUCCESS; /* Verify target capture ID */ if (p->target_id < 0 || p->target_id >= strm->factory->dev_count) return PJ_EINVAL; /* Ok, let's do the switch */ adi = &strm->factory->dev_info[p->target_id]; PJ_LOG(4, (THIS_FILE, "Switching camera to %s..", adi->info.name)); break; } case PJMEDIA_VID_DEV_CAP_ORIENTATION: { pjmedia_orient orient = *(pjmedia_orient *)pval; pjmedia_orient eff_ori; ov5000_dev_info *adi; pj_assert(orient >= PJMEDIA_ORIENT_UNKNOWN && orient <= PJMEDIA_ORIENT_ROTATE_270DEG); if (orient == PJMEDIA_ORIENT_UNKNOWN) return PJ_EINVAL; pj_memcpy(&strm->param.orient, pval, sizeof(strm->param.orient)); if (!strm->conv.conv) { status = pjmedia_vid_dev_conv_create_converter( &strm->conv, strm->pool, &strm->param.fmt, strm->cam_size, strm->param.fmt.det.vid.size, PJ_TRUE, MAINTAIN_ASPECT_RATIO); if (status != PJ_SUCCESS) return status; } eff_ori = strm->param.orient; adi = &strm->factory->dev_info[strm->param.cap_id]; /* Normalize the orientation for back-facing camera */ if (!adi->facing) { if (eff_ori == PJMEDIA_ORIENT_ROTATE_90DEG) eff_ori = PJMEDIA_ORIENT_ROTATE_270DEG; else if (eff_ori == PJMEDIA_ORIENT_ROTATE_270DEG) eff_ori = PJMEDIA_ORIENT_ROTATE_90DEG; } pjmedia_vid_dev_conv_set_rotation(&strm->conv, eff_ori); PJ_LOG(4, (THIS_FILE, "Video capture orientation set to %d", strm->param.orient)); break; } default: status = PJMEDIA_EVID_INVCAP; break; } return status; } static int ov5000_capture_thread_func (void *arg) { struct ov5000_stream* stream = (struct ov5000_stream*) arg; pj_thread_t *call_thread = NULL; //脳垄虏谩脢脗录镁 if(!pj_thread_is_registered()) { pj_thread_desc thread_desc; if (pj_thread_register("ov5000_capture_thread_func", thread_desc, &call_thread) == PJ_SUCCESS) { printf("pj_thread_register ok.\r\n"); } } } typedef enum { NALU_TYPE_SLICE = 1, NALU_TYPE_DPA = 2, NALU_TYPE_DPB = 3, NALU_TYPE_DPC = 4, NALU_TYPE_IDR = 5, NALU_TYPE_SEI = 6, NALU_TYPE_SPS = 7, NALU_TYPE_PPS = 8, NALU_TYPE_AUD = 9, NALU_TYPE_EOSEQ = 10, NALU_TYPE_EOSTREAM = 11, NALU_TYPE_FILL = 12, } NaluType; typedef struct Nalu_ { char * packet; int length; NaluType type; }NALU; typedef enum { NALU_PRIPORITY_DISPOSABLE = 0, NALU_PRIORITY_LOW = 1, NALU_PRIORITY_HIGH = 2, NALU_PRIORITY_HIGHTEST = 3, a } NaluPriority; typedef struct { int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggest) unsigned len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU int max_size; //! Nalu Unit Buffer size int forbidden_bit; //! should be always FALSE int nal_reference_idc; //! NALU_PRIPORITY_xxxx int nal_unit_type; //! NALU_TYPE_xxxx char* buf; //! contains the first byte followed by the EBSP } NALU_t; static int info2 = 0, info3 = 0; static int FindStartCode2(unsigned char *Buf) { if (Buf[0] != 0 || Buf[1] != 0 || Buf[2] != 1) return 0; //0x00 0001 拢驴 else return 1; } static int FindStartCode3(unsigned char *Buf) { if (Buf[0] != 0 || Buf[1] != 0 || Buf[2] != 0 || Buf[3] != 1) return 0; //0x00 000001? else return 1; } static int GetAnnexbNALU(FILE *h264bitstream, NALU_t *nalu) { int pos = 0; int startCodeFound, rewind; //unsigned char *Buf; char Buf[50000];//tmp code if (h264bitstream == NULL || nalu == NULL){ return 0; } //if ((Buf = (unsigned char*)calloc(nalu->max_size, sizeof(char))) == NULL) //printf("GetAnnexbNALU: Could not allocate Buf memory\n"); nalu->startcodeprefix_len = 3; if (3 != fread(Buf, 1, 3, h264bitstream)) { free(Buf); return 0; } info2 = FindStartCode2(Buf); if (info2 != 1) { //虏禄脢脟0x000001 if (1 != fread(Buf + 3, 1, 1, h264bitstream)) { free(Buf); return -1; } info3 = FindStartCode3(Buf); if (info3 != 1) { //虏禄脢脟0x00 000001? free(Buf); return -1; } else { pos = 4; nalu->startcodeprefix_len = 4; } } else { pos = 3; nalu->startcodeprefix_len = 3; } startCodeFound = 0; info2 = 0; info3 = 0; while (!startCodeFound) { if (feof(h264bitstream)) { if ((pos - 1) < nalu->startcodeprefix_len){ printf("GetAnnexbNALU: faile, pos:%d, nalu->startcodeprefix_len:%d", pos, nalu->startcodeprefix_len); return pos - 1; } nalu->len = (pos - 1) - nalu->startcodeprefix_len; memcpy(nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len); nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit nalu->nal_reference_idc = nalu->buf[0] & 0x60; //2 bit nalu->nal_unit_type = (nalu->buf[0]) & 0x1f; //5 bit //free(Buf); return pos - 1; } if (pos > (sizeof(Buf)-1)){ printf("error GetAnnexbNALU: faile, pos:%d, nalu->startcodeprefix_len:%d", pos, nalu->startcodeprefix_len); break; } Buf[pos++] = fgetc(h264bitstream); info3 = FindStartCode3(&Buf[pos - 4]); if (info3 != 1) info2 = FindStartCode2(&Buf[pos - 3]); startCodeFound = (info2 == 1 || info3 == 1); } // Here we have found another start code (and read length of startcode bytes more than we should // have, hence ,go back in the file) rewind = ((info3 == 1) ? -4 : -3); if (0 != fseek(h264bitstream, rewind, SEEK_CUR)) { //free(Buf); printf("GetAnnexbNALU:Cannot fseek in the bit stream file"); } // Here the Start code, the complete NALU, and the next start code is in the Buf // The size of Buf is pos , pos+rewind are the number of bytes excluding the next // start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code nalu->len = (pos + rewind) - nalu->startcodeprefix_len; memcpy(nalu->buf, Buf, nalu->len+ nalu->startcodeprefix_len); nalu->forbidden_bit = nalu->buf[nalu->startcodeprefix_len] & 0x80; // 1 bit nalu->nal_reference_idc = nalu->buf[nalu->startcodeprefix_len] & 0x60; // 2 bit nalu->nal_unit_type = nalu->buf[nalu->startcodeprefix_len] & 0x1f; // 5 bit //nalu->len = (pos + rewind) - nalu->startcodeprefix_len; //memcpy(nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len); //nalu->forbidden_bit = nalu->buf[0] & 0x80; // 1 bit //nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit //nalu->nal_unit_type = nalu->buf[0] & 0x1f; // 5 bit //free(Buf); return (pos + rewind); } static int ov5000_read_h264file_thread_func (void *arg) { struct ov5000_stream* stream = (struct ov5000_stream*) arg; pj_thread_t *call_thread = NULL; if(!pj_thread_is_registered()) { pj_thread_desc thread_desc; if (pj_thread_register("ov5000_read_h264file_thread_func", thread_desc, &call_thread) == PJ_SUCCESS) { printf("pj_thread_register ok.\r\n"); } } pj_thread_sleep(100);//send_packet_interval ov_stream_capture_end = 0; int time_base = 90000; int fps = 24; int send_packet_interval = 1000 / fps; int interval = time_base / fps; long pts = 0; char filename[256]; set_take_ps_packet(1); sprintf(filename, "/home//work/broadcast_app/app_linux/thirds_libs_src/pjproject-2.12.1/AW_VirviEncoder.H264"); FILE *fd = fopen(filename, "rb"); if (fd == -1){ return -1; } int buffersize = 500000; FILE *myout = stdout; NALU_t *n; n = (NALU_t *)calloc(1, sizeof(NALU_t)); if (n == NULL) { fclose(fd); printf("Alloc NALU Error"); return -1; } n->max_size = buffersize; n->buf = (char *)calloc(buffersize, sizeof(char)); if (n->buf == NULL) { fclose(fd); free(n); printf("AllocNALU:n->buf"); return -1; } int data_offset = 0; int nal_num = 0; printf("-----+-------- NALU Table ------+---------+\n"); printf(" NUM | POS | IDC | TYPE | LEN |\n"); printf("-----+---------+--------+-------+---------+\n"); //read h264 file to rtp port. do { int data_lenth = GetAnnexbNALU(fd, n); char type_str[20] = { 0 }; switch (n->nal_unit_type) { case NALU_TYPE_SLICE: sprintf(type_str, "SLICE"); break; case NALU_TYPE_DPA: sprintf(type_str, "DPA"); break; case NALU_TYPE_DPB: sprintf(type_str, "DPB"); break; case NALU_TYPE_DPC: sprintf(type_str, "DPC"); break; case NALU_TYPE_IDR: sprintf(type_str, "IDR"); break; case NALU_TYPE_SEI: sprintf(type_str, "SEI"); break; case NALU_TYPE_SPS: sprintf(type_str, "SPS"); break; case NALU_TYPE_PPS: sprintf(type_str, "PPS"); break; case NALU_TYPE_AUD: sprintf(type_str, "AUD"); break; case NALU_TYPE_EOSEQ: sprintf(type_str, "EOSEQ"); break; case NALU_TYPE_EOSTREAM: sprintf(type_str, "EOSTREAM"); break; case NALU_TYPE_FILL: sprintf(type_str, "FILL"); break; } char idc_str[20] = { 0 }; switch (n->nal_reference_idc >> 5) { case NALU_PRIPORITY_DISPOSABLE: sprintf(idc_str, "DISPOS"); break; case NALU_PRIORITY_LOW: sprintf(idc_str, "LOW"); break; case NALU_PRIORITY_HIGH: sprintf(idc_str, "HIGH"); break; case NALU_PRIORITY_HIGHTEST: sprintf(idc_str, "HIGHTEST"); break; } fprintf(myout, "%5d| %8d| %7s| %6s| %8d| %d|\n", nal_num, data_offset, idc_str, type_str, n->len, data_lenth); if (data_lenth == 0){ break; } //脠隆脪禄脰隆 OnGetFrame2(n->buf, data_lenth, stream ); data_offset = data_offset + data_lenth; nal_num++; /* Sleep to allow log messages to flush */ //sleep(1); if (feof(fd)){ fseek(fd, 0, SEEK_SET); } pj_thread_sleep(10);//send_packet_interval } while(stream->is_running); ov_stream_capture_end = 1; PJ_LOG(4,(THIS_FILE, "2---ov5000_read_h264file_thread_func-run end.")); free(n->buf); free(n); fclose((FILE*)fd); } void set_frame_rate(int rate, int samplerate){ ov5000_framerate = rate; ov5000_samplerate = samplerate; } #ifdef VIDEO_USE_SOCKET typedef void (*get_video_frame_t)(char *data, int len); get_video_frame_t g_video_frame_callback = NULL; void set_get_video_frame_callback(get_video_frame_t function){ g_video_frame_callback=function; } static void start_connect_local_tcpserver(void *param ){ struct ov5000_stream* stream = (struct ov5000_stream*) param; pj_thread_t *call_thread = NULL; if(!pj_thread_is_registered()) { pj_thread_desc thread_desc; if (pj_thread_register("ov5000_socket_capture_thread_func", thread_desc, &call_thread) == PJ_SUCCESS) { printf("ov5000_socket_capture_thread_func ok.\r\n"); } } int server_socket; struct sockaddr_in server_addr; unsigned char flag; unsigned int length = 0; unsigned int data_length = 0; unsigned char *data = NULL; ssize_t bytes_received; // 创建socket server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket == -1) { perror("socket"); return; } int rcv_size = 204800; /*发送缓冲区大小 */ int optlen = sizeof(rcv_size); setsockopt(server_socket , SOL_SOCKET , SO_RCVBUF , &rcv_size , optlen); // 设置服务器地址和端口 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); server_addr.sin_port = htons(11603); // 连接到服务器 if (connect(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("connect"); return; } int MAX_RECV_FRAME_SIZE = 150*1024; data= (char *)malloc(MAX_RECV_FRAME_SIZE);// if (data== NULL){ return ; } while(stream->is_running){ // 接收数据块 ssize_t bytes_received = recv(server_socket, &flag, 1, 0); if (bytes_received <= 0) { // 服务器断开连接或接收错误 close(server_socket); break; } bytes_received = recv(server_socket, &length, 4, 0); if (bytes_received <= 0) { // 服务器断开连接或接收错误 close(server_socket); break; } int old_length = length; length = ntohl(length); if (length < 0 || length > 5000000){ PJ_LOG(4, (THIS_FILE, "!!!!!! invalid data, length:%d,old_length:%d\r\n", length, old_length)); continue; } if (length >= MAX_RECV_FRAME_SIZE) { PJ_LOG(4, (THIS_FILE, "length:%d\r\n", length)); data = realloc(data, length ); } bytes_received = recv(server_socket, data, length, 0); if (bytes_received <= 0) { // 服务器断开连接或接收错误 close(server_socket); break; } // //handle_data(flag, length, data); // flag : 0 ppsinfo; 1: I Frame; 2: P Frame OnGetFrame2(data, length, stream); } free(data); // 关闭socket close(server_socket); PJ_LOG(4, (THIS_FILE, "start_connect_local_tcpserver end\r\n")); } #endif /* API: Start stream. */ static pj_status_t ov5000_stream_start(pjmedia_vid_dev_stream *s) { ov5000_stream *stream = (ov5000_stream*)s; int res; pj_status_t status = PJ_SUCCESS; PJ_LOG(4, (THIS_FILE, "Starting Ov5000 camera stream, gb28181_history_play_source_type:%d", gb28181_history_play_source_type)); stream->is_running = PJ_TRUE; if (gb28181_history_play_source_type != PJMEDIA_VID_S_PLAY){ status = pj_thread_create (stream->pool, "ov5000_file_read", ov5000_read_h264file_thread_func, stream, 0, //ZERO, 0, &stream->ca_thread); if (status != PJ_SUCCESS) { stream->is_running = PJ_FALSE; return status; } return; } #ifdef VIDEO_USE_SOCKET status = pj_thread_create (stream->pool, "ov5000_capture", start_connect_local_tcpserver, stream, 0, //ZERO, 0, &stream->ca_thread); if (status != PJ_SUCCESS) { stream->is_running = PJ_FALSE; return status; } #endif system("echo 1 > /sys/class/gpio/gpio226/value"); /* Call PjCamera::Start() method */ on_return: return status; } /* API: Stop stream. */ static pj_status_t ov5000_stream_stop(pjmedia_vid_dev_stream *s) { ov5000_stream *strm = (ov5000_stream*)s; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL); PJ_LOG(4, (THIS_FILE, "*Stopping Ov5000 camera stream, gb28181_history_play_source_type:%d", gb28181_history_play_source_type)); strm->is_running = PJ_FALSE; system("echo 0 > /sys/class/gpio/gpio226/value"); pj_thread_sleep(10);//send_packet_interval //ov_stream_capture_end = 0; if (gb28181_history_play_source_type != PJMEDIA_VID_S_PLAY){ int wait_times = 0; while(!ov_stream_capture_end){ pj_thread_sleep(10); PJ_LOG(4, (THIS_FILE, "*Stopping Ov5000 wait:%d", wait_times++)); } return; } PJ_LOG(4, (THIS_FILE, "Stop Ov5000 camera stream end.")); return status; } /* API: Destroy stream. */ static pj_status_t ov5000_stream_destroy(pjmedia_vid_dev_stream *s) { ov5000_stream *strm = (ov5000_stream*)s; pj_bool_t with_attach; if (strm == NULL){ return PJ_SUCCESS; } //PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL); //pjmedia_vid_dev_conv_destroy_converter(&strm->conv); if (strm->pool){ pj_pool_release(strm->pool); } PJ_LOG(4, (THIS_FILE, "Ov5000 camera stream destroyed")); return PJ_SUCCESS; } PJ_INLINE(void) strip_padding(void *dst, void *src, int w, int h, int stride) { int i; for (i = 0; i < h; ++i) { pj_memmove(dst, src, w); src += stride; dst += w; } } static void printf_data(uint8_t *data, int size) { static char resp_str[512]; memset(resp_str, 0x0, 512); if (size > (512 / 2)) { size = 512 / 2; } int index = 0; for(int i = 0; i < size; i++) { sprintf(resp_str + index, "%02x ", data[i]); index = strlen(resp_str); if (index > 512) { break; } } PJ_LOG(4,(THIS_FILE,"data %d:%s\r\n", size, resp_str)); } static void OnGetFrame2(uint8_t* data, int length, void* user_data) { ov5000_stream *strm = (ov5000_stream*)(intptr_t)user_data; pjmedia_frame f; pj_uint8_t *Y, *U, *V; pj_status_t status; void *frame_buf, *data_buf; if (strm == NULL|| !strm->vid_cb.capture_cb || length < 0){ return; } strm->frame_ts.u64 += strm->ts_inc; #if 0 if (strm->thread_initialized == 0 || !pj_thread_is_registered()) { pj_status_t status; pj_bzero(strm->thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("ov5000_cam", strm->thread_desc, &strm->ca_thread); if (status != PJ_SUCCESS) return; strm->thread_initialized = 1; PJ_LOG(5,(THIS_FILE, "Ov5000 camera thread registered")); } #endif// f.type = PJMEDIA_FRAME_TYPE_VIDEO; f.size = length; f.timestamp.u64 = strm->frame_ts.u64; f.buf = data_buf = data;// (*env)->GetByteArrayElements(env, data, 0); //PJ_LOG(4,(THIS_FILE, "2---OnGetFrame2-length:%d",length)); uint8_t found_pps = 0; uint8_t send_pps = 0; if ( length < 56 && (*data == 0x00 && *(data+1)==0x00 && *(data+2) == 0x00 && *(data+3) == 0x01 && *(data+4) == 0x67)){ //printf_data(data, length); //save SPS/PPS pj_memcpy(strm->sps_pps, data, length); strm->sps_pps_len = length; #if 0 uint8_t *p_sps_data = data+5; int i = 0; while(i < length-5){ //pps if ((*(p_sps_data+i) == 0x00) && *(p_sps_data+i +1)==0x00 && *(p_sps_data+i +2) == 0x00 && *(p_sps_data+i +3) == 0x01 && *(p_sps_data+i +4) == 0x68){ //pps found_pps = 1; strm->sps_pps_len = i+5;//26-8 strm->pps_len = length - strm->sps_pps_len;//8 break; } i++; } #else strm->pps_len = 0; #endif//no need split pps //PJ_LOG(4,(THIS_FILE, "2--get sps len:%d,found_pps:%d, sps_len:%d, pps_len:%d",length, found_pps, strm->sps_pps_len, strm->pps_len)); } strm->recive_video_packet_count++; #if 1 if ((*data == 0x00 && *(data+1)==0x00 && *(data+2) == 0x01 && *(data+3) == 0x65) || (*data == 0x00 && *(data+1)==0x00 && *(data+2) == 0x00 && *(data+3) == 0x01 && *(data+4) == 0x65)){ //PJ_LOG(4,(THIS_FILE, "1--send sps len:%d",length)); send_pps = 1; } if (found_pps){ return; } if (send_pps){ //sps f.buf = data_buf = strm->sps_pps; f.size = strm->sps_pps_len; (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &f); //pps if (strm->pps_len > 0){ f.buf = data_buf = strm->sps_pps + strm->sps_pps_len; f.size = strm->pps_len; (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &f); } } f.size = length; f.buf = data_buf = data; #endif// //maybe get the encoded data ,no need encoded, direct to rtp packetlization (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &f); //PJ_LOG(4,(THIS_FILE, "2---OnGetFrame2-length:%d end",length)); } #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