树莓派sip视频电话-4:使用高清摄像头
·
很久没有更新树莓派上的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, ®);
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
更多推荐
已为社区贡献1条内容
所有评论(0)