树莓派sip视频电话-4:使用高清摄像头

论坛 期权论坛 脚本     
匿名网站用户   2020-12-20 16:24   11   0

很久没有更新树莓派上的sip视频电话程序了,最近入手了csi接口的摄像头,就完善一下程序.

可以配合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, &reg);
  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










分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP