树莓派sip视频电话-4:使用高清摄像头
2017-11-19 21:16
597 查看
很久没有更新树莓派上的sip视频电话程序了,最近入手了csi接口的摄像头,就完善一下程序.
可以配合freeswitch使用,可以实现视频会议功能.
主要问题:没有实现主动呼叫功能.
更新:1.使用csi 摄像头传输高清视频;2.实现音频播放;3.完善其他功能;4.使用了omxcam库(github上有)
程序没有做优化,有很多重复代码,主要是为了实现功能,有时间再优化.
1.audio_rtp_recv.c
2.audio_rtp_recv.h
4.audio_rtp_send.h
5.common.h
6.g711.c
7.g711codec.c
8.g711codec.h
9.omx_decode.c
10.omx_decode.h
11.queue.c
13.rtpavcsend.c (小日本写的,改了下)
15.sip.c 程序入口
16.video_rtp_recv.c
17.video_rtp_recv.h
18.video_rtp_send.c
19.video_rtp_send.h
20.makefile
还有omxcam文件夹中的文件github中可以找到
camera.c dump_omx.c h264.c omxcam.h utils.c
core.c error.c internal.h omxcam_version.h version.c
debug.c event.c jpeg.c still.c video.c
可以配合freeswitch使用,可以实现视频会议功能.
主要问题:没有实现主动呼叫功能.
更新:1.使用csi 摄像头传输高清视频;2.实现音频播放;3.完善其他功能;4.使用了omxcam库(github上有)
程序没有做优化,有很多重复代码,主要是为了实现功能,有时间再优化.
1.audio_rtp_recv.c
#define ALSA_PCM_NEW_HW_PARAMS_API #include <fcntl.h> #include <unistd.h> #include <alsa/asoundlib.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include "audio_rtp_recv.h" #include "g711codec.h" snd_pcm_t *handle; int audio_recv_init(){ int err; if ((err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) { printf("Playback open error: %s/n", snd_strerror(err)); exit(1); } if ((err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE,//SND_PCM_FORMAT_U8, SND_PCM_ACCESS_RW_INTERLEAVED,/* snd_pcm_readi/snd_pcm_writei access */ 1, //Channels 8000, //sample rate in Hz 1, //soft_resample 500000)) < 0) {////如果latency过小,会使得snd_pcm_writei()丢发声数据,产生short write现象 1000000=1sec printf("Playback open error: %s/n", snd_strerror(err)); exit(1); } return 0; } int audio_recv_close(){ snd_pcm_drain(handle); snd_pcm_close(handle); return 0; } void *audio_recv(void *AudioParam){ int rc; struct audio_param_recv *audiorecvparam=AudioParam; char recvbuffer[256]; char outbuffer[320]; int recv_len; int frames=160;//注意定义alsa中frames audio_recv_init(); while (audiorecvparam->recv_quit==1) { bzero(recvbuffer, sizeof(recvbuffer)); usleep(100); //防止cpu过高 recv_len = recv(audiorecvparam->audio_rtp_socket, recvbuffer, sizeof(recvbuffer), 0 ); if(recv_len<0) continue; //printf("audio recv_len=%d,seq=%d\n",recv_len,recvbuffer[2] << 8|recvbuffer[3] << 0); rc=G711u2PCM(&recvbuffer[12], outbuffer, 160, 0);//应该返回值为320 if(rc<0) fprintf(stderr,RED "[%s]:" NONE "G711u2PCM error:rc=%d\n",__FILE__,rc); //送到pcm解码播放 rc = snd_pcm_writei(handle, outbuffer, frames); if (rc == -EPIPE) { fprintf(stderr, RED "[%s]:" NONE "underrun occurred\n",__FILE__); snd_pcm_prepare(handle); } else if (rc < 0) { fprintf(stderr,RED "[%s]:" NONE "error from writei: %s\n",__FILE__,snd_strerror(rc)); } else if (rc != (int)frames) { fprintf(stderr,RED "[%s]:" NONE "short write, write %d frames,not %d\n",__FILE__, rc,frames); } } audio_recv_close(); close(audiorecvparam->audio_rtp_socket); return 0; } /* int fd = open ("recv.pcm", O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, 0666);//保存解码接收后的声音文件 if (pwrite (fd, outbuffer, rc, 0) == -1) fprintf (stderr, "error: pwrite\n");//写入到文件用于测试 close (fd);//关闭句柄 */
2.audio_rtp_recv.h
#define NONE "\033[m" #define RED "\033[1;31m" #define GREEN "\033[1;32m" #define BLUE "\033[1;34m" typedef struct audio_param_recv { int audio_rtp_socket; char *audio_hw; char *dest_ip ; int dest_port; int local_port; int recv_quit; } audio_param_recv; void *audio_recv(void *AudioParam) ;3.audio_rtp_send.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <memory.h> #include <netdb.h> #include <time.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <alsa/asoundlib.h> #include <math.h> #include "g711codec.h" #include "audio_rtp_send.h" #define BUFFERSIZE 4096 #define PERIOD_SIZE 1024 #define PERIODS 2 #define SAMPLE_RATE 8000 #define CHANNELS 1 #define FSIZE 2*CHANNELS #define ALSA_PCM_NEW_HW_PARAMS_API void *audio_send(void *AudioSendParam) { struct audio_param_send *audiosendparam=AudioSendParam; //char *audio_hw, char *dest_ip, int dest_port fprintf(stderr,GREEN"[%s]:"NONE"param:audio_hw=%s,dest_ip=%s,dest_port=%d\n",__FILE__,audiosendparam->audio_hw,audiosendparam->dest_ip,audiosendparam->dest_port); int rc; //return code. int size; snd_pcm_t *handle; snd_pcm_hw_params_t *params; unsigned int val; int dir; snd_pcm_uframes_t frames; char *buffer; int err; /* Open PCM device for recording (capture). */ err = snd_pcm_open(&handle, audiosendparam->audio_hw , SND_PCM_STREAM_CAPTURE, 0); if (err < 0) { fprintf(stderr,RED "[%s@%s,%d]:" NONE "unable to open pcm device: %s\n",__func__, __FILE__, __LINE__,snd_strerror(err)); exit(1); } /* Allocate a hardware parameters object. */ snd_pcm_hw_params_alloca(¶ms); /* Fill it in with default values. */ err=snd_pcm_hw_params_any(handle, params); if (err < 0) { fprintf(stderr, RED "[%s@%s,%d]:" NONE "Can not configure this PCM device: %s\n",__func__, __FILE__, __LINE__,snd_strerror(err)); exit(1); } /* Set the desired hardware parameters. */ /* Interleaved mode */ err=snd_pcm_hw_params_set_access(handle, params,SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { fprintf(stderr, RED "[%s@%s,%d]:" NONE "Failed to set PCM device to interleaved: %s\n", __func__, __FILE__, __LINE__,snd_strerror(err)); exit(1); } /* Signed 16-bit little-endian format */ //err=snd_pcm_hw_params_set_format(handle, params,SND_PCM_FORMAT_MU_LAW); err=snd_pcm_hw_params_set_format(handle, params,SND_PCM_FORMAT_S16_LE); if (err < 0) { fprintf(stderr,RED "[%s@%s,%d]:" NONE "Failed to set PCM device to 16-bit signed PCM: %s\n", __func__, __FILE__, __LINE__,snd_strerror(err)); exit(1); } /* One channels (mono) */ err=snd_pcm_hw_params_set_channels(handle, params, CHANNELS); if (err < 0) { fprintf(stderr,RED "[%s@%s,%d]:" NONE "Failed to set PCM device to mono: %s\n",__func__, __FILE__, __LINE__,snd_strerror(err)); exit(1); } /* 8000 bits/second sampling rate (CD quality) */ val = 8000;//这里修改为8000 //val = 44100; err=snd_pcm_hw_params_set_rate_near(handle, params,&val, &dir); if (err < 0) { fprintf(stderr, RED "[%s@%s,%d]:" NONE "Failed to set PCM device to sample rate =%d: %s\n",__func__, __FILE__, __LINE__,val,snd_strerror(err)); exit(1); } /* Set period size to 32 frames. */ // Set buffer time 500000. unsigned int buffer_time,period_time; snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0); if ( buffer_time >500000) buffer_time = 80000;//这里可修改 period_time = buffer_time/4;//这里可修改 size = frames * FSIZE; err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, 0); if (err < 0) { fprintf(stderr, RED "[%s@%s,%d]:" NONE "Failed to set PCM device to buffer time =%d: %s\n", __func__, __FILE__, __LINE__,buffer_time,snd_strerror(err)); exit(1); } err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, 0); if (err < 0) { fprintf(stderr, RED "[%s@%s,%d]:" NONE "Failed to set PCM device to period time =%d: %s\n",__func__, __FILE__, __LINE__,period_time,snd_strerror(err)); exit(1); } err = snd_pcm_hw_params(handle, params); if (err < 0) { fprintf(stderr,RED "[%s@%s,%d]:" NONE "unable to set hw parameters: %s\n",__func__, __FILE__, __LINE__,snd_strerror(err)); exit(1); } /* Use a buffer large enough to hold one period */ snd_pcm_hw_params_get_period_size(params,&frames, &dir); size = frames * FSIZE; /* 2 bytes/sample, 1 channels *///这里应该=320 FSIZE=2 frames=160 buffer = (char *) malloc(size); fprintf(stderr,GREEN "[%s@%s]:" NONE "read buffer size = %d\n",__func__, __FILE__,size); fprintf(stderr,GREEN "[%s@%s]:" NONE "period size = %d frames\n",__func__, __FILE__,(int)frames); /*print alsa config parameter*/ snd_pcm_hw_params_get_period_time(params,&val, &dir); fprintf(stderr,GREEN "[%s@%s]:" NONE "period time is: %d\n",__func__, __FILE__,val); snd_pcm_hw_params_get_buffer_time(params, &val, &dir); fprintf(stderr,GREEN "[%s@%s]:" NONE "buffer time = %d us\n",__func__, __FILE__,val); snd_pcm_hw_params_get_buffer_size(params, (snd_pcm_uframes_t *) &val); fprintf(stderr,GREEN "[%s@%s]:" NONE "buffer size = %d frames\n",__func__, __FILE__,val); snd_pcm_hw_params_get_periods(params, &val, &dir); fprintf(stderr,GREEN "[%s@%s]:" NONE "periods per buffer = %d frames\n",__func__, __FILE__,val); int M_bit=1; char sendbuf[1500]; memset(sendbuf,0,1500); unsigned short seq_num = 0; RTP_FIXED_HEADER *rtp_hdr; unsigned int timestamp_increse = 0,ts_current = 0; timestamp_increse = 160; while (audiosendparam->send_quit==1) { //1.采集 rc = snd_pcm_readi(handle, buffer, frames);//采集音频数据 if (rc == -EPIPE) { fprintf(stderr, RED "[%s@%s,%d]:overrun occurred\n",__func__, __FILE__, __LINE__); err=snd_pcm_prepare(handle); if( err <0){ fprintf(stderr, RED "[%s@%s,%d]:Failed to recover form overrun : %s\n",__func__, __FILE__, __LINE__, snd_strerror(err)); exit(1); } } else if (rc < 0) { fprintf(stderr,RED "[%s@%s,%d]:" NONE "error from read: %s\n",__func__, __FILE__, __LINE__,snd_strerror(rc)); exit(1); } else if (rc != (int)frames) { fprintf(stderr, RED "[%s@%s,%d]:" NONE "short read, read %d frames\n", __func__, __FILE__, __LINE__,rc); } //2.编码 rc = PCM2G711u( (char *)buffer, (char *)&sendbuf[12], size, 0 );//pcm转g711a if(rc<0) fprintf(stderr,RED "[%s@%s,%d]:" NONE "PCM2G711u error:rc=%d\n",__func__, __FILE__, __LINE__,rc); //3.打包 rtp_hdr =(RTP_FIXED_HEADER*)&sendbuf[0]; rtp_hdr->payload = 0; //负载类型号, rtp_hdr->version = 2; //版本号,此版本固定为2 if(1 == M_bit) { rtp_hdr->marker = 1; //标志位,由具体协议规定其值。 M_bit = 0; } else{ rtp_hdr->marker = 0; //标志位,由具体协议规定其值。 } rtp_hdr->ssrc = htonl(10); //随机指定为10,并且在本RTP会话中全局唯一 rtp_hdr->seq_no = htons(seq_num ++);//rtp包序号 ts_current = ts_current+timestamp_increse; rtp_hdr->timestamp=htonl(ts_current);//rtp传输时间戳,增量为timestamp_increse=160 //4.发送 rc = send( audiosendparam->audio_rtp_socket, sendbuf, rc+12, 0 );//开始发送rtp包,+12是rtp的包头+g711荷载 if(rc<0) { //对方呼叫结束产生错误 //fprintf(stderr , RED "[%s@%s,%d]:" NONE "net send error=%d\n", __func__, __FILE__, __LINE__,rc); break; } memset(sendbuf,0,1500);//清空sendbuf;此时会将上次的时间戳清空,因此需要ts_current来保存上次的时间戳值 } //shutdown(rtp_socket,2); close(audiosendparam->audio_rtp_socket); snd_pcm_drain(handle); snd_pcm_close(handle); free(buffer); return NULL; }
4.audio_rtp_send.h
#define NONE "\033[m" #define RED "\033[1;31m" #define GREEN "\033[1;32m" #define BLUE "\033[1;34m" typedef struct { /** byte 0 */ unsigned char csrc_len:4; /** expect 0 */ unsigned char extension:1; /** expect 1, see RTP_OP below */ unsigned char padding:1; /** expect 0 */ unsigned char version:2; /** expect 2 */ /** byte 1 */ unsigned char payload:7; /** stream type */ unsigned char marker:1; /** when send the first framer,set it */ /** bytes 2, 3 */ unsigned short seq_no; /** bytes 4-7 */ unsigned long timestamp; /** bytes 8-11 */ unsigned long ssrc; /** stream number is used here. */ } RTP_FIXED_HEADER; typedef struct audio_param_send { int audio_rtp_socket; char *audio_hw; char *dest_ip ; int dest_port; int local_port; int recv_quit; int send_quit; } audio_param_send; void *audio_send(void *AudioSendParam) ;
5.common.h
#define NONE "\033[m" #define RED "\033[1;31m" #define GREEN "\033[1;32m" #define BLUE "\033[1;34m" #define OMX_INIT_STRUCTURE(a) \ memset(&(a), 0, sizeof(a)); \ (a).nSize = sizeof(a); \ (a).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \ (a).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \ (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \ (a).nVersion.s.nStep = OMX_VERSION_STEP #define BOOL int #define TRUE 1 #define FALSE 0 typedef struct rect { int x1; int y1; int x2; int y2; }rect; typedef struct rtp_param { char *dest_ip ; int dest_port; int local_port; int thread_exit; } rtp_param; typedef struct dec_param { int alpha; struct rect videorect; int thread_exit; int m_settings_changed; } dec_param; static inline OMX_TICKS ToOMXTime(int64_t pts) { OMX_TICKS ticks; ticks.nLowPart = pts; ticks.nHighPart = pts >> 32; return ticks; } static inline int64_t FromOMXTime(OMX_TICKS ticks) { int64_t pts = ticks.nLowPart | ((uint64_t)(ticks.nHighPart) << 32); return pts; }
6.g711.c
#include <stdio.h> #include "g711codec.h" /* * function: convert PCM audio format to g711 alaw/ulaw.(zqj) * InAudioData: PCM data prepared for encoding to g711 alaw/ulaw. * OutAudioData: encoded g711 alaw/ulaw. * DataLen: PCM data size. * reserve: reserved param, no use. */ /*alaw*/ int PCM2G711a( char *InAudioData, char *OutAudioData, int DataLen, int reserve ) { //check params. if( (NULL == InAudioData) && (NULL == OutAudioData) && (0 == DataLen) ) { printf("Error, empty data or transmit failed, exit !\n"); return -1; } //printf("DataLen = %d, %s, %d\n", DataLen, __func__, __LINE__); int Retaen = 0; //printf("G711a encode start......\n"); Retaen = g711a_encode( (unsigned char *)OutAudioData, (short*)InAudioData, DataLen/2 ); //printf("Retaen = %d, %s, %d\n", Retaen, __func__, __LINE__); return Retaen; //index successfully encoded data len. } /*ulaw*/ int PCM2G711u( char *InAudioData, char *OutAudioData, int DataLen, int reserve ) { //check params. if( (NULL == InAudioData) && (NULL == OutAudioData) && (0 == DataLen) ) { printf("Error, empty data or transmit failed, exit !\n"); return -1; } //printf("DataLen = %d, %s, %d\n", DataLen, __func__, __LINE__); int Retuen = 0; //printf("G711u encode start......\n"); Retuen = g711u_encode( (unsigned char *)OutAudioData, (short*)InAudioData, DataLen/2 ); //printf("Retuen = %d, %s, %d\n", Retuen, __func__, __LINE__); return Retuen; } /* * function: convert g711 alaw audio format to PCM.(zqj) * InAudioData: g711 alaw data prepared for encoding to PCM. * OutAudioData: encoded PCM audio data. * DataLen: g711a data size. * reserve: reserved param, no use. */ /*alaw*/ int G711a2PCM( char *InAudioData, char *OutAudioData, int DataLen, int reserve ) { //check param. if( (NULL == InAudioData) && (NULL == OutAudioData) && (0 == DataLen) ) { printf("Error, empty data or transmit failed, exit !\n"); return -1; } printf("DataLen = %d, %s, %d\n", DataLen, __func__, __LINE__); int Retade = 0; printf("G711a decode start......\n"); Retade = g711a_decode( (short*)OutAudioData, (unsigned char *)InAudioData, DataLen ); printf("Retade = %d, %s, %d\n", Retade, __func__, __LINE__); return Retade; //index successfully decoded data len. } /*ulaw*/ int G711u2PCM( char *InAudioData, char *OutAudioData, int DataLen, int reserve ) { //check param. if( (NULL == InAudioData) && (NULL == OutAudioData) && (0 == DataLen) ) { printf("Error, empty data or transmit failed, exit !\n"); return -1; } //printf("DataLen = %d, %s, %d\n", DataLen, __func__, __LINE__); int Retude = 0; //printf("G711u decode start......\n"); Retude = g711u_decode( (short*)OutAudioData, (unsigned char *)InAudioData, DataLen ); //printf("Retude = %d, %s, %d\n", Retude, __func__, __LINE__); return Retude; }
7.g711codec.c
#include "g711codec.h" static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; static int search(int val, short *table, int size) { int i; for (i = 0; i < size; i++) { if (val <= *table++) return (i); } return (size); } /* * alaw2linear() - Convert an A-law value to 16-bit linear PCM * */ static int alaw2linear( unsigned char a_val ) { int t; int seg; a_val ^= 0x55; t = (a_val & QUANT_MASK) << 4; seg = ( (unsigned)a_val & SEG_MASK ) >> SEG_SHIFT; switch (seg) { case 0: t += 8; break; case 1: t += 0x108; break; default: t += 0x108; t <<= seg - 1; } return ((a_val & SIGN_BIT) ? t : -t); } /* * ulaw2linear() - Convert a u-law value to 16-bit linear PCM * * First, a biased linear code is derived from the code word. An unbiased * output can then be obtained by subtracting 33 from the biased code. * * Note that this function expects to be passed the complement of the * original code word. This is in keeping with ISDN conventions. */ static int ulaw2linear(unsigned char u_val) { int t; /* Complement to obtain normal u-law value. */ u_val = ~u_val; /* * Extract and bias the quantization bits. Then * shift up by the segment number and subtract out the bias. */ t = ((u_val & QUANT_MASK) << 3) + BIAS; t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); } /* * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law * */ unsigned char linear2alaw(int pcm_val) /* 2's complement (16-bit range) */ { int mask; int seg; unsigned char aval; if (pcm_val >= 0) { mask = 0xD5; /* sign (7th) bit = 1 */ } else { mask = 0x55; /* sign bit = 0 */ pcm_val = -pcm_val - 8; } /* Convert the scaled magnitude to segment number. */ seg = search(pcm_val, seg_end, 8); /* Combine the sign, segment, and quantization bits. */ if (seg >= 8) /* out of range, return maximum value. */ return (0x7F ^ mask); else { aval = seg << SEG_SHIFT; if (seg < 2) aval |= (pcm_val >> 4) & QUANT_MASK; else aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; return (aval ^ mask); } } /* * linear2ulaw() - Convert a linear PCM value to u-law * */ unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */ { int mask; int seg; unsigned char uval; /* Get the sign and the magnitude of the value. */ if (pcm_val < 0) { pcm_val = BIAS - pcm_val; mask = 0x7F; } else { pcm_val += BIAS; mask = 0xFF; } /* Convert the scaled magnitude to segment number. */ seg = search(pcm_val, seg_end, 8); /* * Combine the sign, segment, quantization bits; * and complement the code word. */ if (seg >= 8) /* out of range, return maximum value. */ return (0x7F ^ mask); else { uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); return (uval ^ mask); } } int g711a_decode( short amp[], const unsigned char g711a_data[], int g711a_bytes ) { int i; int samples; unsigned char code; int sl; for ( samples = i = 0; ; ) { if (i >= g711a_bytes) break; code = g711a_data[i++]; sl = alaw2linear( code ); amp[samples++] = (short) sl; } return samples*2; } int g711u_decode(short amp[], const unsigned char g711u_data[], int g711u_bytes) { int i; int samples; unsigned char code; int sl; for (samples = i = 0;;) { if (i >= g711u_bytes) break; code = g711u_data[i++]; sl = ulaw2linear(code); amp[samples++] = (short) sl; } return samples*2; } int g711a_encode(unsigned char g711_data[], const short amp[], int len) { int i; for (i = 0; i < len; i++) { g711_data[i] = linear2alaw(amp[i]); } return len; } int g711u_encode(unsigned char g711_data[], const short amp[], int len) { int i; for (i = 0; i < len; i++) { g711_data[i] = linear2ulaw(amp[i]); } return len; }
8.g711codec.h
/* * G711 encode decode HEADER. */ #ifndef __G711CODEC_H__ #define __G711CODEC_H__ /* * u-law, A-law and linear PCM conversions. */ #define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ #define QUANT_MASK (0xf) /* Quantization field mask. */ #define NSEGS (8) /* Number of A-law segments. */ #define SEG_SHIFT (4) /* Left shift for segment number. */ #define SEG_MASK (0x70) /* Segment field mask. */ #define BIAS (0x84) /* Bias for linear code. */ int PCM2G711a( char *InAudioData, char *OutAudioData, int DataLen, int reserve ); int PCM2G711u( char *InAudioData, char *OutAudioData, int DataLen, int reserve ); int G711a2PCM( char *InAudioData, char *OutAudioData, int DataLen, int reserve ); int G711u2PCM( char *InAudioData, char *OutAudioData, int DataLen, int reserve ); int g711a_decode(short amp[], const unsigned char g711a_data[], int g711a_bytes); int g711u_decode(short amp[], const unsigned char g711u_data[], int g711u_bytes); int g711a_encode(unsigned char g711_data[], const short amp[], int len); int g711u_encode(unsigned char g711_data[], const short amp[], int len); #endif /* g711codec.h */
9.omx_decode.c
// Copyright 2015-2016 Ansersion // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include <stdio.h> #include <stdlib.h> #include <string.h> #include <malloc.h> #include "bcm_host.h" #include "ilclient.h" #include "common.h" COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *video_render = NULL, *video_clock = NULL; COMPONENT_T *list[5]; TUNNEL_T tunnel[4]; ILCLIENT_T *client; int status = 0; unsigned int data_len = 0; char *base_sps, *base_pps; int debug = 1; //设置播放速度 int SetSpeed(int speed){ //speed=1000;正常速度 OMX_TIME_CONFIG_SCALETYPE scaleType; OMX_INIT_STRUCTURE(scaleType); scaleType.xScale = (speed << 16) / 1000;//speed=0为暂停 if(OMX_SetConfig(ILC_GET_HANDLE(video_clock),OMX_IndexConfigTimeScale, &scaleType)!= OMX_ErrorNone) printf("[clock]OMX_IndexConfigTimeScale error\n"); return 0; } //设置视频透明度 int SetAlpha(int alpha){ //if (debug) printf("set alpha:[%d]\n",alpha); OMX_CONFIG_DISPLAYREGIONTYPE configDisplay; OMX_INIT_STRUCTURE(configDisplay); configDisplay.nPortIndex =90; configDisplay.set = (OMX_DISPLAYSETTYPE)(OMX_DISPLAY_SET_ALPHA ); configDisplay.alpha = alpha; if(OMX_SetConfig(ILC_GET_HANDLE(video_render),OMX_IndexConfigDisplayRegion, &configDisplay) != OMX_ErrorNone) printf("[video_render]OMX_IndexConfigDisplayRegion error\n"); return 0; } //设置窗口位置 int SetRect(int x1,int y1,int x2,int y2){ //if (debug) printf("set rect:[%d,%d,%d,%d]\n",x1,y1,x2,y2); OMX_CONFIG_DISPLAYREGIONTYPE configDisplay; OMX_INIT_STRUCTURE(configDisplay); configDisplay.nPortIndex =90; configDisplay.fullscreen = OMX_FALSE; configDisplay.noaspect = OMX_TRUE; configDisplay.set = (OMX_DISPLAYSETTYPE)(OMX_DISPLAY_SET_DEST_RECT|OMX_DISPLAY_SET_SRC_RECT|OMX_DISPLAY_SET_FULLSCREEN|OMX_DISPLAY_SET_NOASPECT); configDisplay.dest_rect.x_offset = x1; configDisplay.dest_rect.y_offset = y1; configDisplay.dest_rect.width = x2; configDisplay.dest_rect.height = y2; /* //其他设置 configDisplay.set = (OMX_DISPLAYSETTYPE)(OMX_DISPLAY_SET_ALPHA | OMX_DISPLAY_SET_TRANSFORM | OMX_DISPLAY_SET_LAYER | OMX_DISPLAY_SET_NUM); configDisplay.alpha = 200; configDisplay.num = 0; configDisplay.layer = 1; configDisplay.transform =0;//0正常 1镜像 2旋转180 configDisplay.fullscreen = OMX_FALSE; configDisplay.noaspect = OMX_TRUE; configDisplay.set = (OMX_DISPLAYSETTYPE)(OMX_DISPLAY_SET_DEST_RECT|OMX_DISPLAY_SET_SRC_RECT|OMX_DISPLAY_SET_FULLSCREEN|OMX_DISPLAY_SET_NOASPECT); configDisplay.dest_rect.x_offset =200; configDisplay.dest_rect.y_offset = 0; configDisplay.dest_rect.width = 640; configDisplay.dest_rect.height = 480; configDisplay.src_rect.x_offset =1920; configDisplay.src_rect.y_offset =0; configDisplay.src_rect.width = 1920; configDisplay.src_rect.height =1080; */ if(OMX_SetConfig(ILC_GET_HANDLE(video_render),OMX_IndexConfigDisplayRegion, &configDisplay) != OMX_ErrorNone) printf("[video_render]OMX_IndexConfigDisplayRegion error\n"); return 0; } int omx_init(){ bcm_host_init(); memset(list, 0, sizeof(list)); memset(tunnel, 0, sizeof(tunnel)); if((client = ilclient_init()) == NULL){ status = -21; printf("ilclient_init error\n"); return status; } if(OMX_Init() != OMX_ErrorNone){ status = -21; printf("OMX_Init error\n"); ilclient_destroy(client); return status; } // create video_decode if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) status = -14; list[0] = video_decode; // create video_render if(status == 0 && ilclient_create_component(client, &video_render, "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0) status = -14; list[1] = video_render; // create clock if(status == 0 && ilclient_create_component(client, &video_clock, "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0) status = -14; list[2] = video_clock; OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; memset(&cstate, 0, sizeof(cstate)); cstate.nSize = sizeof(cstate); cstate.nVersion.nVersion = OMX_VERSION; cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; cstate.nWaitMask = 1; if(video_clock != NULL && OMX_SetParameter(ILC_GET_HANDLE(video_clock), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) status = -13; // create video_scheduler if(status == 0 && ilclient_create_component(client, &video_scheduler, "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0) status = -14; list[3] = video_scheduler; set_tunnel(tunnel, video_decode, 131, video_scheduler, 10); set_tunnel(tunnel+1, video_scheduler, 11, video_render, 90); set_tunnel(tunnel+2, video_clock, 80, video_scheduler, 12); // setup clock tunnel first if(status == 0 && ilclient_setup_tunnel(tunnel+2, 0, 0) != 0) status = -15; else ilclient_change_component_state(video_clock, OMX_StateExecuting); if(status == 0) ilclient_change_component_state(video_decode, OMX_StateIdle); OMX_VIDEO_PARAM_PORTFORMATTYPE format; memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); format.nVersion.nVersion = OMX_VERSION; format.nPortIndex = 130; format.eCompressionFormat = OMX_VIDEO_CodingAVC; format.xFramerate = 30 * (1<<16); if(OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) != OMX_ErrorNone ){ status = -21; } //---------------------------------------------------------------- OMX_PARAM_PORTDEFINITIONTYPE portParam; OMX_INIT_STRUCTURE(portParam); portParam.nPortIndex = 130; if(OMX_GetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &portParam)!= OMX_ErrorNone) printf("OMX_GetParameter OMX_IndexParamPortDefinition error!\n"); portParam.nBufferSize=100*1024;//默认81920,不要轻易改动 //portParam.nBufferCountMin=2; //portParam.nBufferCountActual=100; //portParam.format.video.nFrameWidth = 1920; //portParam.format.video.nFrameHeight = 1080; if(OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &portParam)!= OMX_ErrorNone) printf("OMX_SetParameter OMX_IndexParamPortDefinition error\n!"); if(OMX_GetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &portParam)!= OMX_ErrorNone) printf("OMX_GetParameter OMX_IndexParamPortDefinition error\n!"); printf("portParam.nBufferSize=%d\n",portParam.nBufferSize); //---------------------------------------------------------------- //有效帧开始 OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE concanParam; OMX_INIT_STRUCTURE(concanParam); concanParam.bStartWithValidFrame = OMX_FALSE;//OMX_FALSE OMX_TRUE if(OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamBrcmVideoDecodeErrorConcealment, &concanParam)!= OMX_ErrorNone) printf("OMX_SetParameter OMX_IndexParamBrcmVideoDecodeErrorConcealment error!\n"); //request portsettingschanged on aspect ratio change OMX_CONFIG_REQUESTCALLBACKTYPE notifications; OMX_INIT_STRUCTURE(notifications); notifications.nPortIndex = 131;//OutputPort notifications.nIndex = OMX_IndexParamBrcmPixelAspectRatio; notifications.bEnable = OMX_TRUE; if (OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexConfigRequestCallback, ¬ifications) != OMX_ErrorNone) printf("[video_decode]OMX_SetParameter OMX_IndexConfigRequestCallback error!\n"); //---------------------------------------------------------- OMX_CONFIG_BOOLEANTYPE timeStampMode; OMX_INIT_STRUCTURE(timeStampMode); timeStampMode.bEnabled = OMX_TRUE; if (OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamBrcmVideoTimestampFifo, &timeStampMode) != OMX_ErrorNone) printf("[video_decode]OMX_SetParameter OMX_IndexParamBrcmVideoTimestampFifo error\n!"); //---------------------------------------------------------- if(ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) != 0){ status = -22; } if(status==0){ ilclient_change_component_state(video_decode, OMX_StateExecuting); } printf("omx init succefull\n"); return status; } float pts,recpts; unsigned long start_timestamp; int first_packet = 1; int omx_decode(unsigned char *videobuffer,int videobuffer_len,unsigned long timestamp){ //printf("[omx_decode]videobuffer_len=%d\n",videobuffer_len); usleep(0);//防止cpu占用100%,不知道原因 if(status == 0){ //pts获取 OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp; OMX_INIT_STRUCTURE(timeStamp); timeStamp.nPortIndex =80;//OMX_IndexConfigTimeCurrentMediaTime if(OMX_GetConfig(ILC_GET_HANDLE(video_clock),OMX_IndexConfigTimeCurrentMediaTime, &timeStamp)== OMX_ErrorNone){ pts = (double)FromOMXTime(timeStamp.nTimestamp); //if (debug)printf("pts:%.2f [%.0f]--recpts:%.2f [%.0f]\n", (double)pts* 1e-6, (double)pts,(double)recpts* 1e-6, (double)recpts); } OMX_BUFFERHEADERTYPE *buf; int port_settings_changed = 0;//每次为0可以在切换码流时候再次port_settings_changed if((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL){ if(buf->nAllocLen<videobuffer_len) printf("buf-nAllocLen=%d,videobuffer_len=%d\n",buf->nAllocLen,videobuffer_len); memcpy(buf->pBuffer,videobuffer,videobuffer_len); //free(videobuffer);//对应的是video_rtp_recv.c中的in_buffer=(unsigned char *)malloc(outbuffer_len); buf->nFilledLen = videobuffer_len; buf->nOffset = 0; recpts=((double)timestamp-(double)start_timestamp)/90000;//注意计算方式不一样 float video_fifo=recpts-(double)pts* 1e-6; //printf("[pts]:encode[%.2f]-decode[%.2f]=[%.2f]\n", recpts,(double)pts* 1e-6, video_fifo); //调节速度 /* if(video_fifo<0.19){ SetSpeed(995); }else if(video_fifo>0.21){ SetSpeed(1010); } */ //------------------------------------------------------------------------------- if(port_settings_changed == 0 &&(videobuffer_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0)){ printf("-------------***-port_settings_changed--***---------------\n"); first_packet = 1;//***在视频切换或改变的时候,解码器的时间戳继续按原来的走,还不知道怎么把它初始化为0(pts的值) port_settings_changed = 1; if(ilclient_setup_tunnel(tunnel, 0, 0) != 0) status = -7; ilclient_change_component_state(video_scheduler, OMX_StateExecuting); if(ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0) status = -12; ilclient_change_component_state(video_render, OMX_StateExecuting); } if(first_packet){ /* //设置开始时间戳,从rtp包中获取到时间戳,但是不正确,采取时间戳相减 OMX_TIME_CONFIG_TIMESTAMPTYPE sClientTimeStamp; OMX_INIT_STRUCTURE(sClientTimeStamp); if(OMX_GetConfig(ILC_GET_HANDLE(video_clock), OMX_IndexConfigTimeCurrentWallTime, &sClientTimeStamp)!=OMX_ErrorNone) printf("OMX_GetConfig OMX_IndexConfigTimeClientStartTime error\n!"); sClientTimeStamp.nPortIndex=80; sClientTimeStamp.nTimestamp=ToOMXTime(timestamp);//设置开始时间戳 if( OMX_SetConfig(ILC_GET_HANDLE(video_clock), OMX_IndexConfigTimeClientStartTime, &sClientTimeStamp)!=OMX_ErrorNone) printf("OMX_SetConfig OMX_IndexConfigTimeClientStartTime error\n!"); */ start_timestamp=timestamp; //if (debug)printf("start_timestamp=%d\n",timestamp); buf->nFlags = OMX_BUFFERFLAG_STARTTIME; //buf->nTimeStamp=pts; first_packet = 0; } else{ buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; //buf->nTimeStamp.nLowPart=0; //buf->nTimeStamp.nHighPart=0; } if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) status = -6; //ilclient_wait_for_event(video_render, OMX_EventBufferFlag, 90, 0, OMX_BUFFERFLAG_EOS, 0,ILCLIENT_BUFFER_FLAG_EOS, 10000); //ilclient_flush_tunnels(tunnel, 0); } } return status; } int omx_deinit(){ pts=0; recpts=0; start_timestamp=0; first_packet = 1;//一定要注意,否则第二次呼入不解码,不显示图像 ilclient_disable_tunnel(tunnel); ilclient_disable_tunnel(tunnel+1); ilclient_disable_tunnel(tunnel+2); ilclient_disable_port_buffers(video_decode, 130, NULL, NULL, NULL); ilclient_teardown_tunnels(tunnel); ilclient_state_transition(list, OMX_StateIdle); ilclient_state_transition(list, OMX_StateLoaded); ilclient_cleanup_components(list); OMX_Deinit(); ilclient_destroy(client); return 0; }
10.omx_decode.h
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "bcm_host.h" #include "ilclient.h" #include "common.h" int omx_init(); int omx_decode(unsigned char *videobuffer,int videobuffer_len,unsigned long timestamp); int omx_deinit(); int SetAlpha(int alpha); int SetSpeed(int speed); int SetRect(int x1,int y1,int x2,int y2);
11.queue.c
#include <stdio.h> #include <stdlib.h> #include "queue.h" //------------------cache-start-------------------- /************************************************* Function: InitQueue Description: 初始化,构造空队列 Input: 队列指针 CircleQueue *queue Output: Return: 成功返回OK Others: 空队列 queue->front = queue->rear = 0 *************************************************/ int InitQueue(CircleQueue *queue) { queue->front = queue->rear = 0; queue->count = 0; return OK; } //判断队列为空和满 //1、使用计数器count,队列为空和满时,front都等于rear //2、少用一个元素的空间,约定队列满时:(rear+1)%QUEUESIZE=front,为空时front=rear //rear指向队尾元素的下一个位置,始终为空;队列的长度为(rear-front+QUEUESIZE)%QUEUESIZE /************************************************* Function: IsQueueEmpty Description: 队列是否为空 Input: 队列指针 CircleQueue *queue Output: Return: 为空返回TRUE,否则返回FALSE Others: *************************************************/ int IsQueueEmpty(CircleQueue *queue) { if(queue->count == 0) return TRUE; else return FALSE; } /************************************************* Function: IsQueueFull Description: 队列是否为满 Input: 队列指针 CircleQueue *queue Output: Return: 为满返回TRUE,否则返回FALSE Others: *************************************************/ int IsQueueFull(CircleQueue *queue) { if(queue->count == QUEUESIZE) return TRUE; else return FALSE; } /************************************************* Function: EnQueue Description: 入队 Input: 队列指针 CircleQueue *queue 数据元素 ElemType e Output: Return: 成功返回OK,失败返回ERROR Others: *************************************************/ int EnQueue(CircleQueue *queue, ElemType e) { //验证队列是否已满 if(queue->count == QUEUESIZE) { printf("The queue is full"); return ERROR; } //入队 queue->data[queue->rear] = e; //对尾指针后移 queue->rear = (queue->rear + 1) % QUEUESIZE; //更新队列长度 queue->count++; //printf("e.data=%p\n",*(e.data)); return OK; } /************************************************* Function: DeQueue Description: 出队 Input: 队列指针 CircleQueue *queue Output: Return: 成功返回数据元素,失败程序退出 Others: *************************************************/ ElemType DeQueue(CircleQueue *queue) { //判断队列是否为空 if(queue->count == 0) { printf("The queue is empty!"); exit(EXIT_FAILURE); } //保存返回值 ElemType e = queue->data[queue->front]; //free(queue->data[queue->front].data); //更新队头指针 queue->front = (queue->front + 1) % QUEUESIZE; //更新队列长度 queue->count--; return e; } /************************************************* Function: GetHead Description: 取队头元素 Input: 队列指针 CircleQueue *queue Output: Return: 成功返回数据元素,否则程序退出 Others: *************************************************/ ElemType GetHead(CircleQueue *queue) { //判断队列是否为空 if(queue->count == 0) { printf("The queue is empty!"); exit(EXIT_FAILURE); } return queue->data[queue->front]; } /************************************************* Function: ClearQueue Description: 清空队列 Input: 队列指针 CircleQueue *queue Output: Return: 成功返回OK Others: *************************************************/ int ClearQueue(CircleQueue *queue ) { queue->front = queue->rear = 0; queue->count = 0; return OK; } /************************************************* Function: GetLength Description: 取得队列的长度 Input: 队列指针 CircleQueue *queue Output: Return: 返回队列的长度 Others: *************************************************/ int GetLength(CircleQueue *queue) { return queue->count; }12.queue.h
#include <stdio.h> #include <stdlib.h> #include <stdint.h> //------------------cache-start-------------------- //定义函数结果状态码 #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 //定义循环队列空间大小 #define QUEUESIZE 100 typedef struct _frame { //unsigned char *data;//存储队列元素 uint8_t *data; //int len;//队列头指针 size_t len; unsigned long pts; unsigned long start_pts; //unsigned long dts; uint32_t timestamp; uint16_t seqnum; int64_t start_timestamp; }frame; //frame //定义数据类型 typedef frame ElemType ; //循环队列存储结构 typedef struct _CircleQueue { ElemType data[QUEUESIZE];//存储队列元素 int front;//队列头指针 int rear;//队列尾指针 int count;//队列元素个数 }CircleQueue; int InitQueue(CircleQueue *queue) ; int IsQueueEmpty(CircleQueue *queue) ; int IsQueueFull(CircleQueue *queue) ; int EnQueue(CircleQueue *queue, ElemType e) ; ElemType DeQueue(CircleQueue *queue) ; ElemType GetHead(CircleQueue *queue) ; int ClearQueue(CircleQueue *queue ) ; int GetLength(CircleQueue *queue) ; //---------------cache-end------------
13.rtpavcsend.c (小日本写的,改了下)
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include "rtpavcsend.h" #pragma pack(1) typedef struct rtphead_t { unsigned char head1; unsigned char ptype; unsigned short seqno; // sequence number unsigned long timestamp; unsigned long ssrc; } RtpHead; #pragma pack() int rtpopen(RtpSend** pctx_out, unsigned long ssrc, int ptype, int sock, struct sockaddr_in *peer) { RtpSend* ctx = (RtpSend*)malloc(sizeof(RtpSend)); memset(ctx, 0, sizeof(RtpSend)); memcpy(&ctx->peer, peer, sizeof(struct sockaddr_in)); ctx->seqno = 1; ctx->ssrc = ssrc; ctx->ptype = ptype; ctx->sock = sock; *pctx_out = ctx; return 0; } static int rtpsend_nal(RtpSend* ctx, const unsigned char* data, int datalen, unsigned long timestamp) { unsigned char nal_unit_type; unsigned char* payload; unsigned char buff[4096]; int single_NAL = 1; RtpHead *head = (RtpHead*)buff; head->head1 = 0x80; head->ptype = ctx->ptype; head->seqno = htons(ctx->seqno); head->timestamp = htonl(timestamp); head->ssrc = htonl(ctx->ssrc); payload = (unsigned char*)(head+1); nal_unit_type = data[0] & 0x1f; // NAL unit type switch (nal_unit_type) { case 7://SPS case 8://PPS case 6://SEI case 9://AUD //printf("nal_unit_type=%d\n",nal_unit_type); head->ptype |= 0x80;//by aphero!小日本编写的有点错误,找了半天,才发现sps pps 中的maker标志为不正确,暂时在这里修改 break; default: if (datalen > 1300) single_NAL = 0; // 1300 break; } if (single_NAL) { memcpy(payload, data, datalen); if (sendto(ctx->sock, buff, datalen+sizeof(RtpHead), 0, (struct sockaddr*)&ctx->peer, sizeof(ctx->peer)) < 0) return -1; ctx->seqno++; return 0; } payload[0] = ((data[0]&0x60) | (28/*FU-A*/&0x1f)); // FUindicator: nal_ref_idc NALtype=28 payload[1] = (data[0]&0x1f); // FUheader: data++; datalen--; payload[1] |= 0x80; // while (datalen > 0) { int len = datalen < 1300 ? datalen : 1300; memcpy(payload+2, data, len); if (len == datalen) { payload[1] |= 0x40; head->ptype |= 0x80; } if (sendto(ctx->sock, buff, sizeof(RtpHead)+2+len, 0, (struct sockaddr*)&ctx->peer, sizeof(ctx->peer)) < 0) return -1; ctx->seqno++; head->seqno = htons(ctx->seqno); payload[1] &= 0x7f; data += len; datalen -= len; } return 0; } void rtpclose(RtpSend* ctx) { close(ctx->sock); free(ctx); } // RTP unsigned long long last_ts64 =0; static unsigned long make_timestamp() { struct timeval tv; gettimeofday(&tv, NULL); unsigned long long ts64 = (unsigned long long)tv.tv_sec * 90000 + tv.tv_usec*90/1000; //printf("timestamp=%lld\n",ts64-last_ts64); last_ts64=ts64; return (unsigned long)(ts64 & 0xffffffff); } int AvcAnalyzeAndSend(RtpSend* ctx, const unsigned char* data, int datalen) { const unsigned char _startcode[] = {0,0,1}; const unsigned long ts = make_timestamp(); int nal; int i; int begin; if (ctx->pending_len == 0) { for (i=0; i<datalen-sizeof(_startcode); i++) { if (memcmp(data+i, _startcode, sizeof(_startcode)) == 0) { i += sizeof(_startcode); memcpy(ctx->pending_buff, data+i, datalen-i); data = ctx->pending_buff; datalen = ctx->pending_len = datalen-i; begin = 0; break; } } if (ctx->pending_len == 0) return 0; } else { begin = ctx->pending_len - sizeof(_startcode)+1; if (begin < 0) begin = 0; memcpy(ctx->pending_buff + ctx->pending_len, data, datalen); data = ctx->pending_buff; datalen = ctx->pending_len = ctx->pending_len + datalen; } nal = 0; for (i=begin; i<datalen-sizeof(_startcode); i++) { // startcode if (memcmp(&data[i], _startcode, sizeof(_startcode)) != 0) continue; else { // startcode int pre0 = 0; while (nal < i-pre0 && data[i-pre0-1] == 0) pre0++; const int nallen = i-nal - pre0; if (nallen > 0) rtpsend_nal(ctx, &data[nal], nallen, ts); } nal = i + sizeof(_startcode); i = nal-1; } if (nal > 0 && datalen - nal > 0) { memmove(&ctx->pending_buff[0], &data[nal], datalen-nal); ctx->pending_len = datalen - nal; } return 0; }14.rtpavcsend.h
#ifndef _RTPAVCSEND_H #define _RTPAVCSEND_H #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> typedef struct RtpSend_t { int sock; struct sockaddr_in peer; unsigned short seqno; unsigned char ptype; unsigned long ssrc; unsigned char pending_buff[1024*640]; int pending_len; } RtpSend; //============================= int rtpopen(RtpSend** pctx_out, unsigned long ssrc, int ptype, int sock, struct sockaddr_in *peer); void rtpclose(RtpSend* ctx); //============================= int AvcAnalyzeAndSend(RtpSend* ctx, const unsigned char* data, int datalen); #endif /* _RTPAVCSEND_H */
15.sip.c 程序入口
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/types.h> #include <pthread.h> #include <osip2/osip_mt.h> #include <eXosip2/eXosip.h> #include "audio_rtp_send.h" #include "audio_rtp_recv.h" #include "video_rtp_send.h" #include "video_rtp_recv.h" eXosip_event_t *je; osip_message_t *reg = NULL; osip_message_t *invite = NULL; osip_message_t *ack = NULL; osip_message_t *info = NULL; osip_message_t *message = NULL; osip_message_t *answer = NULL; sdp_message_t *remote_sdp = NULL; sdp_connection_t * con_req = NULL; sdp_media_t * md_audio_req = NULL; sdp_media_t * md_video_req = NULL; struct osip_thread *event_thread; struct osip_thread *audio_thread_send; struct osip_thread *audio_thread_recv; struct osip_thread *video_thread_send; struct osip_thread *video_thread_recv; int call_id, dialog_id,calling ; int i,flag; int quit_flag = 1; int id; char command; char tmp[4096]; char localip[128]; //下面是需要预定义的参数 char *identity = "sip:1009@192.168.1.114"; char *registerer = "sip:192.168.1.114"; char *source_call = "sip:1009@192.168.1.114"; char *proxy="sip:192.168.1.114"; char *dest_call ="sip:192.168.1.133:5062";// "sip:1001@192.168.1.114"; char *fromuser="sip:1009@192.168.1.114"; //"sip:1008@192.168.1.114"; char *userid="1009"; char *passwd="12345"; struct audio_param_send audiosendparam={0/*audio_rtp_socket*/,"plughw:1,0"/*"hw:1,0"*/,NULL,0,54000,1,1};//音频线程初始化默认值,树莓派下usb声卡hw:1,0 struct audio_param_recv audiorecvparam={0/*audio_rtp_socket*/,"hw:1,0",NULL,0,54000,1};//音频线程初始化默认值,树莓派下usb声卡hw:1,0 struct video_param_send videosendparam={0/*video_rtp_socket*/,"/dev/video0",NULL,0,54002,1280,720,30,4000,1,1,1,1};//视频线程初始化默认值 struct video_param_recv videorecvparam={0/*video_rtp_socket*/,"/dev/video0",NULL,0,54002,640,480,15,8000,1,1,1,1};//视频线程初始化默认值 int open_audio_socket(){ //音频socket int audio_rtp_socket; struct sockaddr_in server; int len = sizeof(server); server.sin_family = AF_INET; server.sin_port = htons(audiosendparam.dest_port); server.sin_addr.s_addr = inet_addr(audiosendparam.dest_ip); audio_rtp_socket = socket(AF_INET,SOCK_DGRAM|SOCK_NONBLOCK,0); //设置超时 struct timeval timeout={1,0};//1s //int ret=setsockopt(sock_fd,SOL_SOCKET,SO_SNDTIMEO,(const char*)&timeout,sizeof(timeout)); if(setsockopt(audio_rtp_socket,SOL_SOCKET,SO_RCVTIMEO,(const char*)&timeout,sizeof(timeout))<0){ printf("setsockopt timeout fail"); return -1; } //端口复用 int flag=1; if( setsockopt(audio_rtp_socket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) == -1) { fprintf(stderr, RED "[%s@%s,%d]:"NONE"socket setsockopt error\n", __func__, __FILE__, __LINE__); return -1; } //绑定本地端口 struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(audiosendparam.local_port); ///监听端口 local.sin_addr.s_addr=INADDR_ANY; ///本机 if(bind(audio_rtp_socket,(struct sockaddr*)&local,sizeof(local))==-1) { fprintf(stderr, RED "[%s@%s,%d]:"NONE"udp port bind error\n",__func__, __FILE__, __LINE__); return -1; } connect(audio_rtp_socket, (struct sockaddr *)&server, len); audiosendparam.audio_rtp_socket=audiorecvparam.audio_rtp_socket=audio_rtp_socket; fprintf(stderr,GREEN"[%s]:"NONE"open_audio_socket!\n",__FILE__); return 0; } int open_video_socket(){ //视频socket int video_rtp_socket; struct sockaddr_in server; int len = sizeof(server); server.sin_family = AF_INET; server.sin_port = htons(videosendparam.dest_port); server.sin_addr.s_addr = inet_addr(videosendparam.dest_ip); video_rtp_socket = socket(AF_INET,SOCK_DGRAM|SOCK_NONBLOCK,0); //设置超时 struct timeval timeout={1,0};//1s //int ret=setsockopt(sock_fd,SOL_SOCKET,SO_SNDTIMEO,(const char*)&timeout,sizeof(timeout)); if(setsockopt(video_rtp_socket,SOL_SOCKET,SO_RCVTIMEO,(const char*)&timeout,sizeof(timeout))<0){ printf("setsockopt timeout fail"); return -1; } //端口复用 int flag=1; if( setsockopt(video_rtp_socket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) == -1) { fprintf(stderr, RED "[%s@%s,%d]:"NONE"socket setsockopt error\n", __func__, __FILE__, __LINE__); return -1; } //设置接收缓存 int recv_size=20*1024*1024; if(setsockopt(video_rtp_socket,SOL_SOCKET,SO_RCVBUF,(char*)&recv_size,sizeof(recv_size))<0){ printf("setsockopt recv_buff fail"); return -1; } //设置发送缓存 int send_size=20*1024*1024; if(setsockopt(video_rtp_socket,SOL_SOCKET,SO_SNDBUF,(char*)&send_size,sizeof(send_size))<0){ printf("setsockopt send_buff fail"); return -1; } //绑定本地端口 struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(videosendparam.local_port); ///监听端口 local.sin_addr.s_addr=INADDR_ANY; ///本机 if(bind(video_rtp_socket,(struct sockaddr*)&local,sizeof(local))==-1) { fprintf(stderr, RED "[%s@%s,%d]:"NONE"udp port bind error\n",__func__, __FILE__, __LINE__); return -1; } connect(video_rtp_socket, (struct sockaddr *)&server, len); videosendparam.video_rtp_socket=videorecvparam.video_rtp_socket=video_rtp_socket; fprintf(stderr,GREEN"[%s]:"NONE"open_video_socket!\n",__FILE__); return 0; } void *sipEventThread() { eXosip_event_t *je; for (;;) { je = eXosip_event_wait (0, 100); eXosip_lock(); eXosip_automatic_action (); //401,407错误 eXosip_unlock(); if (je == NULL) continue; switch (je->type) { /* REGISTER related events 1-4*/ case EXOSIP_REGISTRATION_NEW: printf("received new registration\n"); break; case EXOSIP_REGISTRATION_SUCCESS: printf( "registrered successfully\n"); break; case EXOSIP_REGISTRATION_FAILURE: printf("EXOSIP_REGISTRATION_FAILURE!\n"); break; case EXOSIP_REGISTRATION_REFRESHED: printf("REGISTRATION_REFRESHED\n"); break; case EXOSIP_REGISTRATION_TERMINATED: printf("Registration terminated\n"); break; /* INVITE related events within calls */ case EXOSIP_CALL_INVITE: printf ("Received a INVITE msg from %s:%s, UserName is %s, password is %s\n",je->request->req_uri->host, je->request->req_uri->port, je->request->req_uri->username, je->request->req_uri->password); calling = 1; eXosip_lock(); eXosip_call_send_answer (je->tid, 180, NULL); i = eXosip_call_build_answer (je->tid, 200, &answer); if (i != 0) { printf ("This request msg is invalid!Cann't response!/n"); eXosip_call_send_answer (je->tid, 400, NULL); } else { char localip[128]; eXosip_guess_localip(AF_INET, localip, 128); snprintf (tmp, 4096, "v=0\r\n" "o=- 0 0 IN IP4 %s\r\n" "s=No Name\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=audio %d RTP/AVP 0\r\n" "a=rtpmap:0 PCMU/8000\r\n" "m=video 54002 RTP/AVP 96\r\n" //"b=AS:4096\r\n" "a=rtpmap:96 H264/90000\r\n" //"a=fmtp:96 packetization-mode=1;\r\n" ,localip, localip,audiosendparam.local_port ); //设置回复的SDP消息体,下一步计划分析消息体 //没有分析消息体,直接回复原来的消息,这一块做的不好。 osip_message_set_body (answer, tmp, strlen(tmp)); osip_message_set_content_type (answer, "application/sdp"); eXosip_call_send_answer (je->tid, 200, answer); printf ("send 200 over!\n"); } eXosip_unlock (); printf ("the INFO is :\n"); //得到消息体,该消息就是SDP格式. remote_sdp = eXosip_get_remote_sdp (je->did); con_req = eXosip_get_audio_connection(remote_sdp); md_audio_req = eXosip_get_audio_media(remote_sdp); md_video_req = eXosip_get_video_media(remote_sdp); char *remote_sdp_str=NULL; sdp_message_to_str(remote_sdp,&remote_sdp_str); printf("remote_sdp_str=======================\n%s\n",remote_sdp_str); char *payload_str; int pos = 0; printf("audio info:----------------\n"); while (!osip_list_eol ( (const osip_list_t *)&md_audio_req->m_payloads, pos)) { payload_str = (char *)osip_list_get(&md_audio_req->m_payloads, pos);//获取媒体的pt(0,8) sdp_attribute_t *at; at = (sdp_attribute_t *) osip_list_get ((const osip_list_t *)&md_audio_req->a_attributes, pos); printf("payload_str=%s,m_media=%s\n",payload_str,at->a_att_value); pos++; } printf("video info:----------------\n"); pos = 0; while (!osip_list_eol ( (const osip_list_t *)&md_video_req->m_payloads, pos)) { payload_str = (char *)osip_list_get(&md_video_req->m_payloads, pos);//获取媒体的pt(96) sdp_attribute_t *at; at = (sdp_attribute_t *) osip_list_get ((const osip_list_t *)&md_video_req->a_attributes, pos); printf("payload_str=%s,m_media=%s\n",payload_str,at->a_att_value); pos++; } printf("--------------------------------------------------\n"); printf("SDP:conn_add=%s,audio_port=%s,video_port=%s\n",con_req->c_addr,md_audio_req->m_port,md_video_req->m_port); printf("--------------------------------------------------\n"); char ip[20]; strcpy(ip,con_req->c_addr);//这个地方不知道什么原因 //传入音频线程参数 audiosendparam.dest_ip=ip; audiorecvparam.dest_ip=ip; audiosendparam.dest_port=audiorecvparam.dest_port=atoi(md_audio_req->m_port); //audioparam.audio_hw = "default"; //传入视频线程参数 videosendparam.dest_ip=ip; videorecvparam.dest_ip=ip; videosendparam.dest_port=videorecvparam.dest_port=atoi(md_video_req->m_port); sdp_message_free(remote_sdp); remote_sdp = NULL; break; case EXOSIP_CALL_REINVITE: printf("REINVITE\n"); break; case EXOSIP_CALL_NOANSWER: break; case EXOSIP_CALL_PROCEEDING: printf ("proceeding!\n"); break; case EXOSIP_CALL_RINGING: printf ("ringing!\n"); printf ("call_id is %d, dialog_id is %d \n", je->cid, je->did); break; case EXOSIP_CALL_ANSWERED: printf ("ok! connected!\n"); call_id = je->cid; dialog_id = je->did; printf ("call_id is %d, dialog_id is %d \n", je->cid, je->did); eXosip_call_build_ack (je->did, &ack); eXosip_call_send_ack (je->did, ack); break; case EXOSIP_CALL_REDIRECTED: break; case EXOSIP_CALL_REQUESTFAILURE: break; case EXOSIP_CALL_SERVERFAILURE: break; case EXOSIP_CALL_GLOBALFAILURE: break; case EXOSIP_CALL_CANCELLED: break; case EXOSIP_CALL_TIMEOUT: break; case EXOSIP_CALL_CLOSED: printf ("the call sid closed!\n"); //呼叫结束 videorecvparam.recv_quit=0; videosendparam.send_quit=0; audiorecvparam.recv_quit=0; audiosendparam.send_quit=0; int thread_rc=-1; thread_rc=osip_thread_join(audio_thread_send); if (thread_rc==0) fprintf(stderr,GREEN"[%s]:"NONE"audio_send_thread exit\n",__FILE__); thread_rc=osip_thread_join(audio_thread_recv); if (thread_rc==0) fprintf(stderr,GREEN"[%s]:"NONE"audio_recv_thread exit\n",__FILE__); thread_rc=osip_thread_join(video_thread_send); if (thread_rc==0) fprintf(stderr,GREEN"[%s]:"NONE"video_send_thread exit\n",__FILE__); thread_rc=osip_thread_join(video_thread_recv); if (thread_rc==0) fprintf(stderr,GREEN"[%s]:"NONE"video_thread_recv exit\n",__FILE__); break; case EXOSIP_CALL_ACK: printf ("ACK received!\n"); call_id = je->cid; dialog_id = je->did; //获取到远程的sdp信息后,分别建立音频 视频4个线程 videorecvparam.recv_quit=1; videosendparam.send_quit=1; audiorecvparam.recv_quit=1; audiosendparam.send_quit=1; if(open_audio_socket()<0) break;//打开音频发送接收socket //音频发送线程 eXosip_lock(); printf("conn_add=%s,audio_port=%d\n",audiosendparam.dest_ip,audiosendparam.dest_port); audio_thread_send = osip_thread_create (20000, &audio_send , &audiosendparam);//开启音频线程 if (audio_thread_send==NULL){ fprintf(stderr,RED"[%s]:"NONE"audio_send_thread_create failed\n",__FILE__); } else{ fprintf(stderr,GREEN"[%s]:"NONE"audio_send_thread created!\n",__FILE__); } eXosip_unlock(); //音频接收线程 eXosip_lock(); audio_thread_recv = osip_thread_create (20000, &audio_recv ,&audiorecvparam);//开启音频线程 if (audio_thread_recv==NULL){ fprintf(stderr,RED"[%s]:"NONE"audio_thread_recv failed\n",__FILE__); } else{ fprintf(stderr,GREEN"[%s]:"NONE"audio_thread_recv created!\n",__FILE__); } eXosip_unlock(); if(open_video_socket()<0) break;//打开视频发送接收socket //视频发送线程 //======================================= eXosip_lock(); video_thread_send = osip_thread_create (20000, video_send , &videosendparam); if (video_thread_send==NULL){ fprintf(stderr,RED"[%s]:"NONE"video_thread_send failed\n",__FILE__); } else{ fprintf(stderr,GREEN"[%s]:"NONE"video_thread_send created!\n",__FILE__); } eXosip_unlock(); //视频接收线程 eXosip_lock(); video_thread_recv = osip_thread_create (20000, video_recv,&videorecvparam); if (video_thread_recv==NULL){ fprintf(stderr,RED"[%s]:"NONE"video_thread_recv failed\n",__FILE__); } else{ fprintf(stderr,GREEN"[%s]:"NONE"video_thread_recv created!\n",__FILE__); } eXosip_unlock(); break; case EXOSIP_MESSAGE_NEW: printf("EXOSIP_MESSAGE_NEW:"); if (MSG_IS_OPTIONS (je->request)) //如果接受到的消息类型是OPTIONS { printf("options\n"); } if (MSG_IS_MESSAGE (je->request))//如果接受到的消息类型是MESSAGE { osip_body_t *body; osip_message_get_body (je->request, 0, &body); printf ("message: %s\n", body->body); } eXosip_message_build_answer (je->tid, 200,&answer);//收到OPTIONS,必须回应200确认,否则无法callin,返回500错误 eXosip_message_send_answer (je->tid, 200,answer); break; case EXOSIP_CALL_MESSAGE_NEW: printf("EXOSIP_CALL_MESSAGE_NEW\n"); //osip_body_t *msg_body; //osip_message_get_body (je->request, 0, &msg_body); //printf ("call_message: %s\n", msg_body->body); //eXosip_message_build_answer (je->tid, 200,&answer);//收到OPTIONS,必须回应200确认,否则无法callin,返回500错误 //eXosip_message_send_answer (je->tid, 200,answer); break; case EXOSIP_CALL_MESSAGE_PROCEEDING: break; case EXOSIP_MESSAGE_ANSWERED: /**< announce a 200ok */ case EXOSIP_MESSAGE_REDIRECTED: /**< announce a failure. */ case EXOSIP_MESSAGE_REQUESTFAILURE: /**< announce a failure. */ case EXOSIP_MESSAGE_SERVERFAILURE: /**< announce a failure. */ case EXOSIP_MESSAGE_GLOBALFAILURE: /**< announce a failure. */ break; default: printf ("other response:type=%d\n",je->type); break; } eXosip_event_free(je); } } int main (int argc, char *argv[]) { int regid=0; osip_message_t *reg = NULL; printf("r 向服务器注册\n"); printf("c 取消注册\n"); printf("i 发起呼叫请求\n"); printf("h 挂断\n"); printf("q 退出程序\n"); printf("s 执行方法INFO\n"); printf("m 执行方法MESSAGE\n"); if (eXosip_init() != 0) { printf ("Couldn't initialize eXosip!\n"); return -1; } if ( eXosip_listen_addr (IPPROTO_UDP, NULL, 5062, AF_INET, 0) != 0) { eXosip_quit (); fprintf (stderr, "Couldn't initialize transport layer!\n"); return -1; } event_thread = osip_thread_create (20000, sipEventThread,NULL); if (event_thread==NULL){ fprintf (stderr, "event_thread_create failed"); exit (1); } else{ fprintf (stderr, "event_thread created!\n"); } while (quit_flag) { printf ("please input the comand:\n"); scanf ("%c", &command); getchar(); switch (command) { //--------------------注册---------------------------- case 'r': printf ("use [%s] start register!\n",fromuser); eXosip_add_authentication_info (fromuser, userid, passwd, NULL, NULL); regid = eXosip_register_build_initial_register(fromuser, proxy,NULL, 3600, ®); eXosip_register_send_register(regid, reg); quit_flag = 1; break; //--------------------呼叫---------------------------- case 'i':/* INVITE */ i = eXosip_call_build_initial_invite (&invite, dest_call, source_call, NULL, "This si a call for a conversation"); if (i != 0) { printf ("Intial INVITE failed!\n"); break; } char localip2[128]; eXosip_guess_localip(AF_INET, localip2, 128); snprintf (tmp, 4096, "v=0\r\n" "o=- 0 0 IN IP4 %s\r\n" "s=No Name\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=audio 54000 RTP/AVP 8\r\n" "a=rtpmap:8 PCMA/8000\r\n" "m=video 54002 RTP/AVP 96\r\n" "a=rtpmap:96 H264/90000\r\n", localip2,localip2 ); osip_message_set_body (invite, tmp, strlen(tmp)); osip_message_set_content_type (invite, "application/sdp"); eXosip_lock (); i = eXosip_call_send_initial_invite (invite); eXosip_unlock (); break; //--------------------挂断---------------------------- case 'h': printf ("Holded !\n"); eXosip_lock (); eXosip_call_terminate (call_id, dialog_id); eXosip_unlock (); break; //------------------注销------------------------ case 't': videosendparam.send_quit=0; videorecvparam.recv_quit=0; break; case 'c': printf ("This modal isn't commpleted!\n"); /*//注销是时间为0 eXosip_lock (); i = eXosip_register_build_register (regid, 0, NULL); if (i < 0) { eXosip_unlock (); break; } eXosip_register_send_register (regid, reg); eXosip_unlock (); */ break; //-------------------消息--------------------- case 's': printf ("send info\n"); eXosip_call_build_info (dialog_id, &info); snprintf (tmp , 4096, "hello,aphero"); osip_message_set_body (info, tmp, strlen(tmp)); osip_message_set_content_type (info, "text/plain"); eXosip_call_send_request (dialog_id, info); break; //-----------------短信------------------------- case 'm': printf ("send message\n"); eXosip_message_build_request (&message, "MESSAGE", dest_call,source_call, NULL); snprintf (tmp, 4096,"hello aphero"); osip_message_set_body (message, tmp, strlen(tmp)); osip_message_set_content_type (message, "text/plain"); eXosip_message_send_request (message); break; //--------------------退出--------------------- case 'q': quit_flag=0; printf ("Exit the setup!\n"); break; } } eXosip_quit (); return (0); }
16.video_rtp_recv.c
#include <stdio.h> #include <signal.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/time.h> #include <pthread.h> #include <assert.h> #include <stdint.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include <malloc.h> #include "video_rtp_recv.h" #include "omx_decode.h" #include "queue.h" #define RTP_HEADLEN 12 int UnpackRTPH264(unsigned char *bufIn,int len,unsigned char *bufout,video_frame *videoframe) { int outlen=0; if (len < RTP_HEADLEN){ return -1 ; } unsigned char *src = (unsigned char * )bufIn + RTP_HEADLEN; unsigned char head1 = * src; // 获取第一个字节 unsigned char nal = head1 & 0x1f ; // 获取FU indicator的类型域, unsigned char head2 = * (src + 1 ); // 获取第二个字节 unsigned char flag = head2 & 0xe0 ; // 获取FU header的前三位,判断当前是分包的开始、中间或结束 unsigned char nal_fua = (head1 & 0xe0 ) | (head2 & 0x1f ); // FU_A nal //这里可以获取CSRC/sequence number/timestamp/SSRC/CSRC等rtp头部信息. //是否可以定义rtp头部struct,memcopy缓存20个字节到rtp头部即可获取. videoframe->seq_no=bufIn[2] << 8|bufIn[3] << 0;//序号,用于判断rtp乱序 videoframe->timestamp=bufIn[4] << 24|bufIn[5] << 16|bufIn[6] << 8|bufIn[7] << 0;//时间戳,送到解码中 videoframe->flag= src[1]&0xe0;//判断包是开始 结束 videoframe->nal= src[0]&0x1f;//是否是fu-a,还是单个包 videoframe->frametype= src[1]&0x1f;//判断帧类型I,还有错误 if (nal == 0x1c ){//fu-a=28 if (flag == 0x80 ) // 开始=128 { //printf("s "); bufout[0]=0x0; bufout[1]=0x0; bufout[2]=0x0; bufout[3]=0x1; bufout[4]=nal_fua; outlen = len - RTP_HEADLEN -2+5;//-2跳过前2个字节,+5前面前导码和类型码,+5 memcpy(bufout+5,src+2,outlen); } else if (flag == 0x40 ) // 结束=64 { //printf("e "); outlen = len - RTP_HEADLEN -2 ; memcpy(bufout,src+2,len-RTP_HEADLEN-2); } else // 中间 { //printf("c "); outlen = len - RTP_HEADLEN -2 ; memcpy(bufout,src+2,len-RTP_HEADLEN-2); } } else {//当个包,1,7,8 //printf("*[%d]* ",nal); bufout[0]=0x0; bufout[1]=0x0; bufout[2]=0x0; bufout[3]=0x1; memcpy(bufout+4,src,len-RTP_HEADLEN); outlen=len-RTP_HEADLEN+4; } return outlen; } CircleQueue bufferqueue; frame in_avframe,out_avframe; pthread_mutex_t mut; void* dec_thread(void *arg){ struct video_param_recv *video_recv_param=arg; while((video_recv_param->recv_quit==1)){ if(IsQueueEmpty(&bufferqueue)) {continue;} if(bufferqueue.count<5){ usleep(1000);continue;} pthread_mutex_lock(&mut); out_avframe=DeQueue(&bufferqueue);//从队列中读取数据 pthread_mutex_unlock(&mut); //printf("[DeQueue]:[%d-%d]=%2x %2x %2x %2x\n",bufferqueue.count,out_avframe.seqnum,out_avframe.data[4],out_avframe.data[5],out_avframe.data[6],out_avframe.data[7]); //printf("[DeQueue]:[%d-%d]=%s\n",bufferqueue.count,out_avframe.seqnum,out_avframe.data); //if((out_avframe.seqnum) % 30==0) printf("[DeQueue]:[%d]:[%d]\n",bufferqueue.count,out_avframe.seqnum); if(omx_decode(out_avframe.data,out_avframe.len,out_avframe.timestamp)!=0) printf("omx_decode fail\n"); } return NULL; } #define LUANXU 0 //乱序后重新排序 #define NOLUANXU 0//乱序不排序 void *video_recv(void *videorecvparam){ unsigned char buffer[2048]; unsigned char outbuffer[2048]; unsigned char *in_buffer;// video_frame videoframe; unsigned short last_seq_no=0;//上一个包的序号 #if LUANXU int i; int count=0; unsigned short start_seq_no=0;//开始乱序的序号,eg.123546 =3 #define BUFCOUNT 20 frame luan_avframe[BUFCOUNT]; //memset(&luan_avframe, 0, sizeof(luan_avframe)); #endif struct video_param_recv *video_recv_param=videorecvparam; memset(&videoframe, 0, sizeof(videoframe)); int outbuffer_len; pthread_t decthread; if(omx_init()!=0){ fprintf(stderr,RED"[%s]:" NONE"omx_init fail\n",__FILE__); return NULL; } InitQueue(&bufferqueue); //if(pthread_create(&decthread,NULL,dec_thread,videorecvparam)<0) printf ("Create dec pthread error!\n"); while((video_recv_param->recv_quit==1)){ usleep(100); int recv_len; //outbuffer=(unsigned char *)malloc(2048); bzero(buffer, sizeof(buffer)); recv_len = recv(video_recv_param->video_rtp_socket, buffer, sizeof(buffer),0); if(recv_len<0) continue; outbuffer_len=UnpackRTPH264(buffer,recv_len,outbuffer,&videoframe); //fprintf(stderr,BLUE"[%s]:" NONE"seq_no[%d],flage[%d],video_recv_len[%d],outbuffer_len[%d]\n",__FILE__,videoframe.seq_no,videoframe.flag,recv_len,outbuffer_len); //printf("frame:seq_no[%d],nal[%d],type[%d],flage[%d]\n",videoframe.seq_no,videoframe.nal,videoframe.frametype,videoframe.flag); //in_buffer=(unsigned char *)malloc(outbuffer_len);//分配内存,保存到队列中去 //memcpy(in_buffer,outbuffer,outbuffer_len); #if LUANXU //这里注意rtp乱序 if((videoframe.seq_no!=0 && videoframe.seq_no!=(last_seq_no+1))){//乱序 if(start_seq_no==0)start_seq_no=last_seq_no; printf("rtp seq error:S[%d][%d]F[%d]C[%d]\n",start_seq_no,count,last_seq_no,videoframe.seq_no); luan_avframe[videoframe.seq_no-start_seq_no].data=in_buffer; luan_avframe[videoframe.seq_no-start_seq_no].len=outbuffer_len; luan_avframe[videoframe.seq_no-start_seq_no].timestamp=videoframe.timestamp; luan_avframe[videoframe.seq_no-start_seq_no].seqnum=videoframe.seq_no; last_seq_no=videoframe.seq_no;//保存上一个包的时间戳 //count++;//乱包计数//注释掉,可以跳过乱包,不存入队列 continue; }else{//正确序 if(start_seq_no!=0){ for(i=0;i<count;i++){ pthread_mutex_lock(&mut); //EnQueue(&bufferqueue,luan_avframe[i+1]);//将乱包重新排序后存入队列 pthread_mutex_unlock(&mut); //printf("[EnQueue]:[%d-%d]=%2x %2x %2x %2x\n",bufferqueue.count,luan_avframe[i+1].seqnum,luan_avframe[i+1].data[4],luan_avframe[i+1].data[5],luan_avframe[i+1].data[6],luan_avframe[i+1].data[7]); } start_seq_no=0; count=0; } in_avframe.data=in_buffer; in_avframe.len=outbuffer_len; in_avframe.timestamp=videoframe.timestamp; in_avframe.seqnum=videoframe.seq_no; if(!IsQueueFull(&bufferqueue)){ pthread_mutex_lock(&mut); EnQueue(&bufferqueue,in_avframe); pthread_mutex_unlock(&mut); // printf("[EnQueue]:[%d-%d]=%2x %2x %2x %2x\n",bufferqueue.count,in_avframe.seqnum,in_avframe.data[4],in_avframe.data[5],in_avframe.data[6],in_avframe.data[7]); } } last_seq_no=videoframe.seq_no;//保存上一个包的时间戳 if(outbuffer_len==0) continue; #endif #if NOLUANXU if((videoframe.seq_no!=0 && videoframe.seq_no!=(last_seq_no+1))){//乱序 printf("rtp seq error:F[%d]C[%d]\n",last_seq_no,videoframe.seq_no); } last_seq_no=videoframe.seq_no;//保存上一个包的时间戳 in_avframe.data=in_buffer; in_avframe.len=outbuffer_len; in_avframe.timestamp=videoframe.timestamp; in_avframe.seqnum=videoframe.seq_no; if(!IsQueueFull(&bufferqueue)){ pthread_mutex_lock(&mut); EnQueue(&bufferqueue,in_avframe); pthread_mutex_unlock(&mut); } //printf("[EnQueue]:[%d-%d]=%2x %2x %2x %2x\n",bufferqueue.count,in_avframe.seqnum,in_avframe.data[4],in_avframe.data[5],in_avframe.data[6],in_avframe.data[7]); //printf("[EnQueue]:[%d-%d]=%s\n",bufferqueue.count,in_avframe.seqnum,in_avframe.data); #endif //直接解码 if((videoframe.seq_no!=0 && videoframe.seq_no!=(last_seq_no+1))){//乱序 printf("rtp seq error:F[%d]C[%d]\n",last_seq_no,videoframe.seq_no); } last_seq_no=videoframe.seq_no;//保存上一个包的时间戳 if(omx_decode(outbuffer,outbuffer_len,videoframe.timestamp)!=0) printf("omx_decode fail\n");//直接解码 } if(omx_deinit()!=0){ printf("omx_deinit fail\n");} close(video_recv_param->video_rtp_socket); return NULL; }
17.video_rtp_recv.h
#define NONE "\033[m" #define RED "\033[1;31m" #define GREEN "\033[1;32m" #define BLUE "\033[1;34m" typedef struct video_param_recv { int video_rtp_socket; char *video_hw; char *dest_ip ; int dest_port; int local_port; int width; int height; int fps; int bitrate; int recv_thread; int recv_quit; int send_thread; int send_quit; } video_param_recv; typedef struct video_frame { unsigned char nal; unsigned char frametype; unsigned char flag; unsigned long timestamp; unsigned short seq_no; } video_frame; void *video_recv(void *videorecvparam);
18.video_rtp_send.c
#include <stdio.h> #include <signal.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/time.h> #include <pthread.h> #include <assert.h> #include <stdint.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include "video_rtp_send.h" #include "omx_decode.h" #include "omxcam/omxcam.h" #include "rtpavcsend.h" struct sockaddr_in s_peer; static RtpSend* rtpsock; static int udpsock; static int sock_create(const char* peer_addr, int port,int localport) { int s; memset(&s_peer, 0, sizeof(s_peer)); s_peer.sin_family = AF_INET; s_peer.sin_addr.s_addr = inet_addr(peer_addr); s_peer.sin_port = htons(port); if (s_peer.sin_addr.s_addr == 0 || s_peer.sin_addr.s_addr == 0xffffffff) { fprintf(stderr, "Invalid address(%s)\n", peer_addr); return -1; } if (port <= 0 || port > 65535) { fprintf(stderr, "Invalid port(%d)\n", port); return -1; } s = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0); if (s < 0) { perror("socket"); return -1; } //端口复用 int flag=1; if( setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) == -1) { fprintf(stderr, RED "[%s@%s,%d]:"NONE"socket setsockopt error\n", __func__, __FILE__, __LINE__); return -1; } //绑定本地端口 struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(localport); ///监听端口 local.sin_addr.s_addr=INADDR_ANY; ///本机 if(bind(s,(struct sockaddr*)&local,sizeof(local))==-1) { fprintf(stderr, RED "[%s@%s,%d]:"NONE"udp port bind error\n",__func__, __FILE__, __LINE__); return -1; } return s; } void *video_send(void *videosendparam){ video_param_send *video_send_param=videosendparam; //使用video_send_param->video_rtp_socket这个socket不发送数据,暂时还是再创建socket omxcam_video_settings_t videoset = {}; //摄像头/编码器初始化 omxcam_video_init(&videoset); //摄像头参数设置 videoset.camera.width = video_send_param->width; videoset.camera.height = video_send_param->height; videoset.camera.framerate = video_send_param->fps; videoset.camera.rotation=180; //H264编码器设置 videoset.h264.bitrate = (video_send_param->bitrate)*1000; //12Mbps videoset.h264.idr_period = 10; //30 IDR间隔 videoset.h264.inline_headers = OMXCAM_TRUE; // SPS/PPS头插入 videoset.h264.profile=OMXCAM_H264_AVC_PROFILE_HIGH;//OMXCAM_H264_AVC_PROFILE_BASELINE; fprintf(stderr,BLUE"[%s]:" NONE"video_send_param:%s:%d\n", __FILE__,video_send_param->dest_ip, video_send_param->dest_port); fprintf(stderr,BLUE"[%s]:" NONE"camera_param:%dX%d,fps:%d,%d\n", __FILE__,video_send_param->width, video_send_param->height,video_send_param->fps,video_send_param->bitrate); //网络初始化 udpsock = sock_create(video_send_param->dest_ip, video_send_param->dest_port,video_send_param->local_port); //udpsock = sock_create("192.168.1.114", 8888);//用于调试,可将视频发送到第三个设备上,比如电脑上vlc接收 // RTP初始化 rtpopen(&rtpsock, 10110825/*SSRC*/, 96/*payload_type*/, udpsock/*video_send_param->video_rtp_socket*/, &s_peer); // 开始发送数据---videoset.on_data = video_encoded; omxcam_video_start_npt(&videoset); omxcam_buffer_t buffer; while (video_send_param->send_quit==1){ if (omxcam_video_read_npt (&buffer, 0)) return NULL; AvcAnalyzeAndSend(rtpsock, buffer.data, buffer.length); //printf("encoded len=%d\n",buffer.length); } omxcam_video_stop_npt(); rtpclose(rtpsock); //fprintf(stderr,BLUE"[%s]:" NONE"video send thread exit\n", __func__, __FILE__); return NULL; }
19.video_rtp_send.h
#define NONE "\033[m" #define RED "\033[1;31m" #define GREEN "\033[1;32m" #define BLUE "\033[1;34m" typedef struct video_param_send { int video_rtp_socket; char *video_hw; char *dest_ip ; int dest_port; int local_port; int width; int height; int fps; int bitrate; int recv_thread; int recv_quit; int send_thread; int send_quit; } video_param_send; struct video_param_send *video_send_param; void *video_send(void *videosendparam);
20.makefile
CC=cc CCFLAGS=-g -Wall -ftree-vectorize -pipe -Wno-psabi -DOSIP_MT CCFLAGS+=-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -U_FORTIFY_SOURCE -Wall -g -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -Wno-psabi CCFLAGS+=-I/opt/vc/src/hello_pi/libs/ilclient -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux LDFLAGS=-leXosip2 -losip2 -losipparser2 -lasound -lcamkit -lasound LDFLAGS+=-L/opt/vc/lib/ -lGLESv2 -lEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm -L/opt/vc/src/hello_pi/libs/ilclient -L/opt/vc/src/hello_pi/libs/vgfont OBJS=sip.o omx_decode.o video_rtp_send.o video_rtp_recv.o audio_rtp_recv.o audio_rtp_send.o queue.o g711.o g711codec.o rtpavcsend.o omxcam/debug.o omxcam/video.o omxcam/h264.o omxcam/core.o omxcam/error.o omxcam/still.o omxcam/camera.o omxcam/dump_omx.o omxcam/version.o omxcam/event.o omxcam/jpeg.o omxcam/utils.o TARGET=sipclient all:: $(TARGET) %.o: %.c @rm -f $@ $(CC) $(CCFLAGS) $(LDFLAGS) -c $< -o $@ -Wno-deprecated-declarations $(TARGET): $(OBJS) $(CC) $(CCFLAGS) -o $@ $(OBJS) $(LDFLAGS) clean:: rm -f $(OBJS) $(TARGET)
还有omxcam文件夹中的文件github中可以找到
camera.c dump_omx.c h264.c omxcam.h utils.c
core.c error.c internal.h omxcam_version.h version.c
debug.c event.c jpeg.c still.c video.c
相关文章推荐
- 树莓派sip视频电话-1:exosip2ctype+python使用硬件编解码
- 树莓派sip视频电话-3:exosip2+硬件h264+g711编码初步实现
- 树莓派sip视频电话-2:alsa+g711+rtp传输音频
- Camkit 树莓派视频传输,使用分析
- hoRTC网络视频加密电话原理测试版使用说明
- OneNET视频推流------使用树莓派交叉编译工具
- 使用miniSipServer构建自己的视频电话平台
- 树莓派3 web视频服务器 mjpg-streamer 安装和使用
- 树莓派下python,使用USB和CSI摄像头进行视频连续截图,并进行灰度、二值化处理
- OneNET视频能力体验------使用树莓派摄像头推流
- 基于 HTML5技术的SIP视频电话
- 使用SIP 电话软件Express Talk,测试CISCO SPA系列IP电话
- 一个使用FFmpeg库读取3gp视频的例子-Android中使用FFmpeg媒体库(三)
- 一个使用了 osip 和 eXosip 库的 UAC 代理客户端的演示程序
- 播放视频的框架Vitamio的使用问题
- 使用 Ansible 在树莓派上构建一个基于 Linux 的高性能计算系统
- oSIP协议栈(及eXoSIP,Ortp等)使用入门(转)
- <极客学院>视频教程学习笔记-iOS中CALayer的使用
- 解决Android中使用webview加载音视频返回后还有声音问题
- 录制音视频及promise的使用