Initial commit: TXW82x FPV v2.7.0.7-42229 SDK + project sources
This commit is contained in:
58
sdk/app/spook/access_log.c
Normal file
58
sdk/app/spook/access_log.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
#include <rtp.h>
|
||||
|
||||
void write_access_log( char *path, struct sockaddr *addr, int code, char *req,
|
||||
int length, char *referer, char *user_agent )
|
||||
{
|
||||
/*
|
||||
char s[128];
|
||||
time_t now;
|
||||
int req_len;
|
||||
|
||||
if( log_fd < 0 ) return;
|
||||
|
||||
for( req_len = 0; req[req_len] && req[req_len] != '\r' &&
|
||||
req[req_len] != '\n'; ++req_len );
|
||||
if( ! referer || ! referer[0] ) referer = "-";
|
||||
if( ! user_agent || ! user_agent[0] ) user_agent = "-";
|
||||
|
||||
time( &now );
|
||||
strcpy( s, inet_ntoa( ((struct sockaddr_in *)addr)->sin_addr ) );
|
||||
write( log_fd, s, strlen( s ) );
|
||||
write( log_fd, s,
|
||||
strftime( s, sizeof( s ), " - - [%d/%b/%Y:%T %z] \"",
|
||||
localtime( &now ) ) );
|
||||
write( log_fd, req, req_len );
|
||||
write( log_fd, s, sprintf( s, "\" %d %d \"", code, length ) );
|
||||
write( log_fd, referer, strlen( referer ) );
|
||||
write( log_fd, "\" \"", 3 );
|
||||
write( log_fd, user_agent, strlen( user_agent ) );
|
||||
write( log_fd, "\"\n", 2 );
|
||||
*/
|
||||
}
|
||||
|
||||
162
sdk/app/spook/custom_rtsp_jpg.c
Normal file
162
sdk/app/spook/custom_rtsp_jpg.c
Normal file
@@ -0,0 +1,162 @@
|
||||
#include "sys_config.h"
|
||||
#include "tx_platform.h"
|
||||
#include <csi_kernel.h>
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
#include "lwip\api.h"
|
||||
#include "lwip\tcp.h"
|
||||
|
||||
#include "rtsp_common.h"
|
||||
#include "osal/string.h"
|
||||
#include "stream_define.h"
|
||||
#include "video_app/video_app.h"
|
||||
#include "stream_frame.h"
|
||||
#include "log.h"
|
||||
#include "lib/video/dvp/jpeg/jpg.h"
|
||||
#include "frame.h"
|
||||
#include "audio_media_ctrl/audio_code_ctrl.h"
|
||||
#include "audio_msi/audio_adc.h"
|
||||
#include "jpg_concat_msi.h"
|
||||
#include "gen420_hardware_msi.h"
|
||||
|
||||
extern void spook_send_thread_stream(struct rtsp_priv *r);
|
||||
extern void err_rtsp_stream(struct rtp_node *node);
|
||||
static void self_thread(void *d)
|
||||
{
|
||||
struct rtsp_priv *r = (struct rtsp_priv*)d;
|
||||
spook_send_thread_stream(r);
|
||||
}
|
||||
|
||||
static void err_exit_thread(void *d)
|
||||
{
|
||||
struct rtp_node *node = (struct rtp_node *)d;
|
||||
err_rtsp_stream(node);
|
||||
}
|
||||
//创建实时预览的的线程
|
||||
static void self_creat(struct rtsp_source *source,void *priv)
|
||||
{
|
||||
const char *path = (const char *)priv;
|
||||
while((*path) && (*path != '?'))
|
||||
{
|
||||
path++;
|
||||
}
|
||||
os_printf("match path:%s\n",path);
|
||||
source->priv = (struct rtsp_priv*)os_zalloc(sizeof(struct rtsp_priv));
|
||||
struct rtsp_priv *r = (struct rtsp_priv*)source->priv;
|
||||
if(source->priv && *path == '?')
|
||||
{
|
||||
path++;
|
||||
r->live_node = &source->live_node;
|
||||
r->video_msi = msi_find(path, 1);
|
||||
os_printf("video_msi :%s\tmsi:%X\n",path,r->video_msi);
|
||||
if(r->video_msi)
|
||||
{
|
||||
r->v_msi = rtsp_msi_init("custom_rtp",~0,0);
|
||||
if(r->v_msi)
|
||||
{
|
||||
msi_add_output(r->video_msi, NULL, "custom_rtp");
|
||||
OS_TASK_INIT("live_rtsp_custom", &source->handle, self_thread, r, OS_TASK_PRIORITY_NORMAL + 2, NULL, 1024);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
os_printf("%s jpg_concat_msi_init_start fail\n", __FUNCTION__);
|
||||
OS_TASK_INIT("live_rtsp_err", &source->handle, err_exit_thread, &source->live_node, OS_TASK_PRIORITY_NORMAL + 2, NULL, 1024);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
os_printf("%s not enough space\n",__FUNCTION__);
|
||||
OS_TASK_INIT("live_rtsp_err", &source->handle, err_exit_thread, &source->live_node, OS_TASK_PRIORITY_NORMAL + 2, NULL, 1024);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void self_destory(struct rtsp_source *source)
|
||||
{
|
||||
//stop_jpeg();
|
||||
void *tmp = source->handle.hdl;
|
||||
struct rtsp_priv *r = source->priv;
|
||||
if(r)
|
||||
{
|
||||
if(r->video_msi)
|
||||
{
|
||||
msi_del_output(r->video_msi,NULL,"custom_rtp");
|
||||
msi_put(r->video_msi);
|
||||
r->video_msi = NULL;
|
||||
}
|
||||
rtsp_msi_deinit((void*)r->v_msi);
|
||||
os_free(source->priv);
|
||||
source->priv = NULL;
|
||||
}
|
||||
if(tmp)
|
||||
{
|
||||
os_task_del(&source->handle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int self_get_sdp( struct session *s, char *dest, int *len,
|
||||
char *path )
|
||||
{
|
||||
struct rtsp_session *ls = (struct rtsp_session *)s->private;
|
||||
int i = 0;
|
||||
int t = 0;
|
||||
char *addr = "IP4 0.0.0.0";
|
||||
os_printf("%s:%d\tpath:%s\n",__FUNCTION__,__LINE__,path);
|
||||
|
||||
if( s->ep[0] && s->ep[0]->trans_type == RTP_TRANS_UDP )
|
||||
addr = s->ep[0]->trans.udp.sdp_addr;
|
||||
|
||||
i = snprintf( dest, *len,"v=0\r\no=- 1 1 IN IP4 127.0.0.1\r\ns=Test\r\na=type:broadcast\r\nt=0 0\r\nc=IN %s\r\n", addr );
|
||||
for( t = 0; t < MAX_TRACKS && ls->source->track[t].rtp; ++t )
|
||||
{
|
||||
int port;
|
||||
|
||||
if( s->ep[t] && s->ep[t]->trans_type == RTP_TRANS_UDP )
|
||||
port = s->ep[t]->trans.udp.sdp_port;
|
||||
else
|
||||
port = 0;
|
||||
|
||||
if(ls->source->track[t].rtp->type == 0)
|
||||
{
|
||||
i += ls->source->track[t].rtp->get_sdp( dest + i, *len - i,96 + t, port,ls->source->track[t].rtp->private );
|
||||
}
|
||||
else
|
||||
{
|
||||
i += ls->source->track[t].rtp->get_sdp( dest + i, *len - i,96 + t, port,NULL);
|
||||
}
|
||||
|
||||
if( port == 0 ) // XXX What's a better way to do this?
|
||||
i += sprintf( dest + i, "a=control:track%d\r\n", t );
|
||||
}
|
||||
*len = i;
|
||||
return t;
|
||||
}
|
||||
|
||||
static struct session *self_rtsp_open( char *path, void *d )
|
||||
{
|
||||
//默认的,各自模式可以各自去修改
|
||||
struct session *sess = rtsp_open(path,d);
|
||||
if(sess)
|
||||
{
|
||||
sess->get_sdp = self_get_sdp;
|
||||
}
|
||||
return sess;
|
||||
}
|
||||
|
||||
void custom_rtsp_jpeg_init(const rtp_name *rtsp)
|
||||
{
|
||||
struct rtsp_source *source;
|
||||
source = rtsp_start_block();
|
||||
rtsp_set_path( rtsp->path, source,self_rtsp_open );
|
||||
set_video_track( (char*)rtsp->video_encode_name, source );
|
||||
register_live_fn(source,self_creat,self_destory,NULL);
|
||||
rtsp_end_block(source);
|
||||
return ;
|
||||
}
|
||||
2233
sdk/app/spook/data.h
Normal file
2233
sdk/app/spook/data.h
Normal file
File diff suppressed because it is too large
Load Diff
147
sdk/app/spook/encoder-audio.c
Normal file
147
sdk/app/spook/encoder-audio.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
//#include <stdio.h>
|
||||
//#include <fcntl.h>
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
#include <spook_config.h>
|
||||
#include <csi_kernel.h>
|
||||
#include "encoder-audio.h"
|
||||
|
||||
|
||||
//需要修改
|
||||
static void get_framerate( struct stream *s, int *fincr, int *fbase )
|
||||
{
|
||||
/*struct jpeg_encoder *en = (struct jpeg_encoder *)s->private;*/
|
||||
|
||||
spook_log( SL_DEBUG, "jpeg get_framerate" );
|
||||
*fincr = JPEG_FRAMEINC;
|
||||
*fbase = 25;//JPEG_FRAMERATE-4;
|
||||
}
|
||||
|
||||
static void set_running( struct stream *s, int running )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************ CONFIGURATION DIRECTIVES ************************/
|
||||
|
||||
static void *start_block(void)
|
||||
{
|
||||
struct audio_encoder *en;
|
||||
|
||||
en = (struct audio_encoder *)malloc( sizeof( struct audio_encoder ) );
|
||||
en->output = NULL;
|
||||
|
||||
return en;
|
||||
}
|
||||
|
||||
static void get_back_audio_frame( struct frame *f, void *d )
|
||||
{
|
||||
struct audio_encoder *en = (struct audio_encoder *)d;
|
||||
_os_printf("-");
|
||||
//exchange_frame( en->ex, new_frame() );
|
||||
deliver_frame_to_stream( f, en->output );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int end_block( void *d )
|
||||
{
|
||||
struct audio_encoder *en = (struct audio_encoder *)d;
|
||||
//int i;
|
||||
|
||||
if( ! en->output )
|
||||
{
|
||||
spook_log( SL_ERR, "jpeg: missing output stream name" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
en->ex = new_exchanger_audio( 0, get_back_audio_frame, en );
|
||||
|
||||
exchange_frame( en->ex, new_audio_frame() );
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_output( const char *name, void *d )
|
||||
{
|
||||
struct audio_encoder *en = (struct audio_encoder *)d;
|
||||
|
||||
spook_log( SL_DEBUG, "audio set_output" );
|
||||
en->output = new_stream( name, FORMAT_AUDIO, en );
|
||||
if( ! en->output )
|
||||
{
|
||||
spook_log( SL_ERR, "jpeg: unable to create stream \"%s\"", name );
|
||||
return -1;
|
||||
}
|
||||
en->output->get_framerate = get_framerate;
|
||||
en->output->set_running = set_running;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void *get_audio_ex(struct audio_encoder *en)
|
||||
{
|
||||
return en->ex;
|
||||
}
|
||||
|
||||
|
||||
void * rtp_audio_init_ret(void)
|
||||
{
|
||||
struct audio_encoder *en;
|
||||
|
||||
en = start_block();
|
||||
set_output(AUDIO_ENCODER_NAME, en);
|
||||
end_block(en);
|
||||
|
||||
return en->ex;
|
||||
}
|
||||
|
||||
void * file_audio_init_ret(void)
|
||||
{
|
||||
struct audio_encoder *en;
|
||||
|
||||
en = start_block();
|
||||
set_output(FILE_JPEG_ENCODER_NAME, en);
|
||||
end_block(en);
|
||||
|
||||
return en->ex;
|
||||
}
|
||||
|
||||
|
||||
void *rtsp_audio_encode_init(const char *encode_name)
|
||||
{
|
||||
struct audio_encoder *en;
|
||||
en = start_block();
|
||||
set_output(encode_name, en);
|
||||
end_block(en);
|
||||
return en->ex;
|
||||
}
|
||||
|
||||
|
||||
20
sdk/app/spook/encoder-audio.h
Normal file
20
sdk/app/spook/encoder-audio.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef __ENCODER_AUDIO_H
|
||||
#define __ENCODER_AUDIO_H
|
||||
|
||||
struct audio_encoder {
|
||||
struct stream *output;
|
||||
int format;
|
||||
struct frame_exchanger *ex;
|
||||
volatile int running;
|
||||
|
||||
};
|
||||
|
||||
void * file_audio_init_ret(void);
|
||||
void * rtp_audio_init_ret(void);
|
||||
void *get_audio_ex(struct audio_encoder *en);
|
||||
int rtp_audio_init(void);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
137
sdk/app/spook/encoder-h264.c
Normal file
137
sdk/app/spook/encoder-h264.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
#include <spook_config.h>
|
||||
#include <csi_kernel.h>
|
||||
#include "encoder-jpeg.h"
|
||||
|
||||
extern volatile uint8_t framerate_c;
|
||||
|
||||
static void get_framerate( struct stream *s, int *fincr, int *fbase )
|
||||
{
|
||||
/*struct jpeg_encoder *en = (struct jpeg_encoder *)s->private;*/
|
||||
|
||||
spook_log( SL_DEBUG, "h264 get_framerate" );
|
||||
*fincr = H264_FRAMEINC;
|
||||
*fbase = 25;//JPEG_FRAMERATE-4;
|
||||
}
|
||||
|
||||
static void set_running( struct stream *s, int running )
|
||||
{
|
||||
struct h264_encoder *en = (struct h264_encoder *)s->private;
|
||||
|
||||
spook_log( SL_DEBUG, "h264 set_running: %d", running);
|
||||
|
||||
en->running = running;
|
||||
en->ex->scan_ready = running;
|
||||
}
|
||||
|
||||
|
||||
/************************ CONFIGURATION DIRECTIVES ************************/
|
||||
|
||||
static void *start_block(void)
|
||||
{
|
||||
struct h264_encoder *en;
|
||||
|
||||
en = (struct h264_encoder *)malloc( sizeof( struct h264_encoder ) );
|
||||
en->output = NULL;
|
||||
en->running = 0;
|
||||
|
||||
return en;
|
||||
}
|
||||
|
||||
static void get_back_frame( struct frame *f, void *d )
|
||||
{
|
||||
struct h264_encoder *en = (struct h264_encoder *)d;
|
||||
//exchange_frame( en->ex, new_frame() );
|
||||
deliver_frame_to_stream( f, en->output );
|
||||
}
|
||||
|
||||
|
||||
static int end_block( void *d )
|
||||
{
|
||||
struct h264_encoder *en = (struct h264_encoder *)d;
|
||||
//int i;
|
||||
|
||||
if( ! en->output )
|
||||
{
|
||||
spook_log( SL_ERR, "h264: missing output stream name" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
en->ex = new_exchanger( EXCHANGER_SLOT_SIZE, get_back_frame, en );
|
||||
en->ex->ready = 0;
|
||||
en->ex->scan_ready = 0;
|
||||
//for( i = 0; i < EXCHANGER_SLOT_SIZE; ++i )
|
||||
exchange_frame( en->ex, new_frame() );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_output( const char *name, void *d )
|
||||
{
|
||||
struct h264_encoder *en = (struct h264_encoder *)d;
|
||||
|
||||
spook_log( SL_DEBUG, "h264 set_output" );
|
||||
en->output = new_stream( name, FORMAT_H264, en );
|
||||
if( ! en->output )
|
||||
{
|
||||
spook_log( SL_ERR, "h264: unable to create stream \"%s\"", name );
|
||||
return -1;
|
||||
}
|
||||
en->output->get_framerate = get_framerate;
|
||||
en->output->set_running = set_running;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void *get_video_ex_h264(struct h264_encoder *en)
|
||||
{
|
||||
return en->ex;
|
||||
}
|
||||
|
||||
|
||||
void * file_h264_init_ret(void)
|
||||
{
|
||||
struct h264_encoder *en;
|
||||
|
||||
en = start_block();
|
||||
set_output(FILE_H264_ENCODER_NAME, en);
|
||||
end_block(en);
|
||||
|
||||
return en->ex;
|
||||
}
|
||||
|
||||
|
||||
void *h264_encode_init(const char *encode_name)
|
||||
{
|
||||
struct h264_encoder *en;
|
||||
en = start_block();
|
||||
set_output(encode_name, en);
|
||||
end_block(en);
|
||||
return en->ex;
|
||||
}
|
||||
148
sdk/app/spook/encoder-jpeg.c
Normal file
148
sdk/app/spook/encoder-jpeg.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
//#include <fcntl.h>
|
||||
//#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
#include <spook_config.h>
|
||||
#include <csi_kernel.h>
|
||||
#include "encoder-jpeg.h"
|
||||
|
||||
extern volatile uint8_t framerate_c;
|
||||
|
||||
static void get_framerate( struct stream *s, int *fincr, int *fbase )
|
||||
{
|
||||
/*struct jpeg_encoder *en = (struct jpeg_encoder *)s->private;*/
|
||||
|
||||
spook_log( SL_DEBUG, "jpeg get_framerate" );
|
||||
*fincr = JPEG_FRAMEINC;
|
||||
*fbase = 25;//JPEG_FRAMERATE-4;
|
||||
}
|
||||
|
||||
static void set_running( struct stream *s, int running )
|
||||
{
|
||||
struct jpeg_encoder *en = (struct jpeg_encoder *)s->private;
|
||||
|
||||
spook_log( SL_DEBUG, "jpeg set_running: %d", running);
|
||||
|
||||
en->running = running;
|
||||
en->ex->scan_ready = running;
|
||||
}
|
||||
|
||||
/************************ CONFIGURATION DIRECTIVES ************************/
|
||||
|
||||
static void *start_block(void)
|
||||
{
|
||||
struct jpeg_encoder *en;
|
||||
|
||||
en = (struct jpeg_encoder *)malloc( sizeof( struct jpeg_encoder ) );
|
||||
en->output = NULL;
|
||||
en->running = 0;
|
||||
|
||||
return en;
|
||||
}
|
||||
|
||||
static void get_back_frame( struct frame *f, void *d )
|
||||
{
|
||||
struct jpeg_encoder *en = (struct jpeg_encoder *)d;
|
||||
//exchange_frame( en->ex, new_frame() );
|
||||
deliver_frame_to_stream( f, en->output );
|
||||
}
|
||||
|
||||
//extern k_task_handle_t jpeg_scan_handle;
|
||||
extern k_task_handle_t jpeg_send_handle;
|
||||
extern k_task_handle_t jpeg_scan_handle;
|
||||
|
||||
|
||||
//void spook_scan_thread(void *d);
|
||||
void spook_send_thread(void *d);
|
||||
void spook_scan_thread(void *d);
|
||||
extern void jpeg_user();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int end_block( void *d )
|
||||
{
|
||||
struct jpeg_encoder *en = (struct jpeg_encoder *)d;
|
||||
//int i;
|
||||
|
||||
if( ! en->output )
|
||||
{
|
||||
spook_log( SL_ERR, "jpeg: missing output stream name" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
en->ex = new_exchanger( EXCHANGER_SLOT_SIZE, get_back_frame, en );
|
||||
en->ex->ready = 0;
|
||||
en->ex->scan_ready = 0;
|
||||
//for( i = 0; i < EXCHANGER_SLOT_SIZE; ++i )
|
||||
exchange_frame( en->ex, new_frame() );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_output( const char *name, void *d )
|
||||
{
|
||||
struct jpeg_encoder *en = (struct jpeg_encoder *)d;
|
||||
|
||||
spook_log( SL_DEBUG, "jpeg set_output" );
|
||||
en->output = new_stream( name, FORMAT_JPEG, en );
|
||||
if( ! en->output )
|
||||
{
|
||||
spook_log( SL_ERR, "jpeg: unable to create stream \"%s\"", name );
|
||||
return -1;
|
||||
}
|
||||
en->output->get_framerate = get_framerate;
|
||||
en->output->set_running = set_running;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void *get_video_ex(struct jpeg_encoder *en)
|
||||
{
|
||||
return en->ex;
|
||||
}
|
||||
|
||||
void * file_jpeg_init_ret(void)
|
||||
{
|
||||
struct jpeg_encoder *en;
|
||||
|
||||
en = start_block();
|
||||
set_output(FILE_JPEG_ENCODER_NAME, en);
|
||||
end_block(en);
|
||||
|
||||
return en->ex;
|
||||
}
|
||||
|
||||
|
||||
void *jpeg_encode_init(const char *encode_name)
|
||||
{
|
||||
struct jpeg_encoder *en;
|
||||
en = start_block();
|
||||
set_output(encode_name, en);
|
||||
end_block(en);
|
||||
return en->ex;
|
||||
}
|
||||
29
sdk/app/spook/encoder-jpeg.h
Normal file
29
sdk/app/spook/encoder-jpeg.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef __ENCODER_JPEG_H
|
||||
#define __ENCODER_JPEG_H
|
||||
#include "stream.h"
|
||||
struct jpeg_encoder {
|
||||
struct stream *output;
|
||||
int format;
|
||||
struct frame_exchanger *ex;
|
||||
volatile int running;
|
||||
};
|
||||
|
||||
struct h264_encoder {
|
||||
struct stream *output;
|
||||
int format;
|
||||
struct frame_exchanger *ex;
|
||||
volatile int running;
|
||||
};
|
||||
void * file_jpeg_init_ret(void);
|
||||
|
||||
void * jpeg_init_ret(void);
|
||||
void *get_video_ex(struct jpeg_encoder *en);
|
||||
void * jpeg_init(const rtp_name *jpg_name);
|
||||
|
||||
|
||||
void * file_h264_init_ret(void);
|
||||
void * h264_init_ret(void);
|
||||
void *get_video_ex_h264(struct h264_encoder *en);
|
||||
void * h264_spook_init(const rtp_name *h264_name);
|
||||
|
||||
#endif
|
||||
1192
sdk/app/spook/ephoto.c
Normal file
1192
sdk/app/spook/ephoto.c
Normal file
File diff suppressed because it is too large
Load Diff
80
sdk/app/spook/event.h
Normal file
80
sdk/app/spook/event.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "lwip\sockets.h"
|
||||
#include "list.h"
|
||||
#include "lib/net/eloop/eloop.h"
|
||||
|
||||
//#include "mutex_os.h"
|
||||
|
||||
|
||||
#ifndef _EVENT_H
|
||||
#define _EVENT_H
|
||||
#define EVENT_TIME 1
|
||||
#define EVENT_FD 2
|
||||
#define EVENT_ALWAYS 3
|
||||
#define EVENT_SOFT_QUEUE 4
|
||||
#define EVENT_F_ENABLED 1
|
||||
#define EVENT_F_REMOVE 2
|
||||
#define EVENT_F_ONESHOT 4
|
||||
#define EVENT_F_RUNNING 8
|
||||
|
||||
struct event_info;
|
||||
|
||||
typedef void (*callback)( struct event_info *e, void *d );
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct event_info {
|
||||
struct event *e;
|
||||
int type;
|
||||
void *data;
|
||||
};
|
||||
|
||||
int time_diff( eloop_time_ref *tr_start, eloop_time_ref *tr_end );
|
||||
int time_ago( eloop_time_ref *tr );
|
||||
|
||||
void time_now( eloop_time_ref *tr );
|
||||
void time_add( eloop_time_ref *tr, int msec );
|
||||
void time_future( eloop_time_ref *tr, int msec );
|
||||
|
||||
struct soft_queue *new_soft_queue( int length );
|
||||
void *get_next_event( struct soft_queue *sq );
|
||||
int soft_queue_add( struct soft_queue *sq, void *d );
|
||||
struct event *add_timer_event( int msec, unsigned int flags, callback f, void *d );
|
||||
struct event *add_alarm_event( eloop_time_ref *t, unsigned int flags, callback f, void *d );
|
||||
void resched_event( struct event *e, eloop_time_ref *t );
|
||||
struct event *add_fd_event( int fd, int write, unsigned int flags, callback f, void *d );
|
||||
struct event *add_always_event( unsigned int flags, callback f, void *d );
|
||||
struct event *add_softqueue_event( struct soft_queue *sq, unsigned int flags,
|
||||
callback f, void *d );
|
||||
void remove_event( struct event *e );
|
||||
void set_event_interval( struct event *e, int msec );
|
||||
void set_event_enabled( struct event *e, int enabled );
|
||||
int get_event_enabled( struct event *e );
|
||||
void exit_event_loop(void);
|
||||
void event_loop( int single );
|
||||
|
||||
#endif /* EVENT_H */
|
||||
409
sdk/app/spook/frame.c
Normal file
409
sdk/app/spook/frame.c
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
//#include <test_util.h>
|
||||
#include <csi_config.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <list.h>
|
||||
//#include <linux/linux_mutex.h>
|
||||
#include "spook_config.h"
|
||||
#include "rtp.h"
|
||||
|
||||
#include "lwip/api.h"
|
||||
#include <csi_kernel.h>
|
||||
|
||||
#include "csi_core.h"
|
||||
#include "list.h"
|
||||
|
||||
#include "jpgdef.h"
|
||||
|
||||
|
||||
#include "osal/sleep.h"
|
||||
|
||||
#ifdef USB_EN
|
||||
#include "dev/usb/uvc_host.h"
|
||||
#endif
|
||||
|
||||
#include "osal/string.h"
|
||||
|
||||
|
||||
#include "stream_frame.h"
|
||||
#include "basic_include.h"
|
||||
#include "lib/multimedia/msi.h"
|
||||
#include "lib/heap/av_heap.h"
|
||||
#include "lib/heap/av_psram_heap.h"
|
||||
|
||||
|
||||
|
||||
|
||||
//data申请空间函数
|
||||
#define STREAM_MALLOC av_psram_malloc
|
||||
#define STREAM_FREE av_psram_free
|
||||
#define STREAM_ZALLOC av_psram_zalloc
|
||||
|
||||
//结构体申请空间函数
|
||||
#define STREAM_LIBC_MALLOC av_malloc
|
||||
#define STREAM_LIBC_FREE av_free
|
||||
#define STREAM_LIBC_ZALLOC av_zalloc
|
||||
|
||||
|
||||
#define MAX_RTSP_JPG_RECV 28
|
||||
struct jpg_rtsp_msi {
|
||||
struct msi *msi;
|
||||
uint8_t filter_type; //暂时只是支持某个类型的数据(0xffff是不过滤,只是判断stype)
|
||||
uint8_t srcID;
|
||||
//struct os_event evt;
|
||||
//RBUFFER_DEF(data, struct framebuff *, MAX_RTSP_JPG_RECV);
|
||||
};
|
||||
|
||||
#define MAX_RTSP_AUDIO_RECV 10
|
||||
struct rtsp_audio_msi {
|
||||
struct msi *msi;
|
||||
};
|
||||
|
||||
static int32 rtsp_jpg_action(struct msi *msi, uint32 cmd_id, uint32 param1, uint32 param2)
|
||||
{
|
||||
struct jpg_rtsp_msi *jpg_rtsp = (struct jpg_rtsp_msi *)msi->priv;
|
||||
int32_t ret = RET_OK;
|
||||
//os_printf("cmd_id:%d\n",cmd_id);
|
||||
switch (cmd_id) {
|
||||
case MSI_CMD_POST_DESTROY:
|
||||
{
|
||||
os_printf("%s:%d\taddr:%X\n",__FUNCTION__,__LINE__,RETURN_ADDR());
|
||||
STREAM_LIBC_FREE(jpg_rtsp);
|
||||
}
|
||||
break;
|
||||
case MSI_CMD_TRANS_FB:
|
||||
{
|
||||
struct framebuff *fb = (struct framebuff *)param1;
|
||||
//来源要匹配
|
||||
if(jpg_rtsp->srcID && jpg_rtsp->srcID != fb->srcID)
|
||||
{
|
||||
ret = RET_ERR;
|
||||
break;
|
||||
}
|
||||
if(jpg_rtsp->filter_type != (uint8_t)~0)
|
||||
{
|
||||
ret = RET_ERR;
|
||||
if(jpg_rtsp->filter_type == fb->stype)
|
||||
{
|
||||
ret = RET_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MSI_CMD_SET_SPEED: //设置播放倍速,控制数据读取行为
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int32_t rtsp_audio_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
|
||||
{
|
||||
struct rtsp_audio_msi *audio_rtsp = (struct rtsp_audio_msi*)msi->priv;
|
||||
int32_t ret = RET_OK;
|
||||
switch(cmd_id) {
|
||||
case MSI_CMD_TRANS_FB:
|
||||
{
|
||||
ret = RET_OK;
|
||||
break;
|
||||
}
|
||||
case MSI_CMD_POST_DESTROY:
|
||||
{
|
||||
STREAM_LIBC_FREE(audio_rtsp);
|
||||
ret = RET_OK;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct msi *rtsp_msi_init(const char *name,uint8_t filter_type,uint8_t srcID)
|
||||
{
|
||||
struct msi *rtsp_msi = msi_new(name,MAX_RTSP_JPG_RECV,NULL);
|
||||
struct jpg_rtsp_msi *jpg_rtsp = rtsp_msi->priv;
|
||||
|
||||
if(!jpg_rtsp)
|
||||
{
|
||||
jpg_rtsp = (struct jpg_rtsp_msi*)STREAM_LIBC_ZALLOC(sizeof(struct jpg_rtsp_msi));
|
||||
jpg_rtsp->filter_type = filter_type;
|
||||
jpg_rtsp->srcID = srcID;
|
||||
rtsp_msi->priv = (void*)jpg_rtsp;
|
||||
rtsp_msi->action = rtsp_jpg_action;
|
||||
jpg_rtsp->msi = rtsp_msi;
|
||||
rtsp_msi->priv = (void*)jpg_rtsp;
|
||||
}
|
||||
rtsp_msi->enable = 1;
|
||||
return rtsp_msi;
|
||||
}
|
||||
|
||||
struct msi *rtsp_audio_msi_init(const char *name)
|
||||
{
|
||||
struct msi *rtsp_audio_msi = msi_new(name, MAX_RTSP_AUDIO_RECV,NULL);
|
||||
struct rtsp_audio_msi *audio_rtsp = rtsp_audio_msi->priv;
|
||||
|
||||
if(!audio_rtsp) {
|
||||
audio_rtsp = (struct rtsp_audio_msi*)STREAM_LIBC_ZALLOC(sizeof(struct rtsp_audio_msi));
|
||||
rtsp_audio_msi->priv = (void*)audio_rtsp;
|
||||
rtsp_audio_msi->action = (msi_action)rtsp_audio_action;
|
||||
audio_rtsp->msi = rtsp_audio_msi;
|
||||
}
|
||||
rtsp_audio_msi->enable = 1;
|
||||
return rtsp_audio_msi;
|
||||
}
|
||||
|
||||
void rtsp_msi_deinit(struct msi *msi)
|
||||
{
|
||||
if(msi)
|
||||
{
|
||||
msi->enable = 0;
|
||||
msi_destroy(msi);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void rtsp_audio_msi_deinit(struct msi *msi)
|
||||
{
|
||||
if(msi)
|
||||
{
|
||||
msi->enable = 0;
|
||||
msi_destroy(msi);
|
||||
}
|
||||
}
|
||||
|
||||
struct frame *new_frame(void)
|
||||
{
|
||||
struct frame *f;
|
||||
|
||||
//新帧
|
||||
f = (struct frame*)STREAM_LIBC_MALLOC(sizeof(struct frame));
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
struct frame *new_audio_frame(void)
|
||||
{
|
||||
struct frame *f;
|
||||
|
||||
//新帧
|
||||
f = (struct frame*)STREAM_LIBC_MALLOC(sizeof(struct frame));
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
struct frame_exchanger *new_exchanger_audio( int slots,
|
||||
frame_deliver_func func, void *d )
|
||||
{
|
||||
struct frame_exchanger *ex;
|
||||
ex = (struct frame_exchanger *)STREAM_LIBC_MALLOC( sizeof( struct frame_exchanger ) );
|
||||
|
||||
ex->f = func;
|
||||
ex->d = d;
|
||||
return ex;
|
||||
}
|
||||
|
||||
|
||||
struct frame_exchanger *new_exchanger( int slots,
|
||||
frame_deliver_func func, void *d )
|
||||
{
|
||||
struct frame_exchanger *ex;
|
||||
|
||||
ex = (struct frame_exchanger *)
|
||||
STREAM_LIBC_MALLOC( sizeof( struct frame_exchanger ) );
|
||||
ex->f = func;
|
||||
ex->d = d;
|
||||
|
||||
|
||||
|
||||
/* read_mutex is used to avoid closing the tcp when frame is sending through sendmsg */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
int exchange_frame( struct frame_exchanger *ex, struct frame *frame )
|
||||
{
|
||||
ex->jf= frame;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void unref_frame( struct frame *f )
|
||||
{
|
||||
// int r;
|
||||
//del_frame
|
||||
if(!f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DEL_JPG_FRAME((struct framebuff *)f->get_f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int opcode_func(stream *s,void *priv,int opcode)
|
||||
{
|
||||
int res = 0;
|
||||
switch(opcode)
|
||||
{
|
||||
case STREAM_OPEN_ENTER:
|
||||
break;
|
||||
case STREAM_OPEN_EXIT:
|
||||
{
|
||||
enable_stream(s,1);
|
||||
}
|
||||
break;
|
||||
case STREAM_OPEN_FAIL:
|
||||
break;
|
||||
case STREAM_RECV_DATA_FINISH:
|
||||
break;
|
||||
|
||||
case STREAM_CLOSE_EXIT:
|
||||
os_printf("%s:%d close\n",__FUNCTION__,__LINE__);
|
||||
break;
|
||||
|
||||
|
||||
|
||||
default:
|
||||
//默认都返回成功
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
//正常这个就是运行就退出
|
||||
void err_rtsp_stream(struct rtp_node *node)
|
||||
{
|
||||
struct frame_exchanger *ex = node->video_ex;
|
||||
while(1)
|
||||
{
|
||||
ex->f( NULL, ex->d );
|
||||
os_sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
void spook_send_thread_stream(struct rtsp_priv *r)
|
||||
{
|
||||
struct rtp_node *node = r->live_node;
|
||||
struct frame_exchanger *ex = node->video_ex;
|
||||
struct frame_exchanger *audio_ex = node->audio_ex;
|
||||
struct jpg_rtsp_msi *jpg_rtsp = r->v_msi?(struct jpg_rtsp_msi *)r->v_msi->priv:NULL;
|
||||
struct rtsp_audio_msi *audio_rtsp = r->a_msi?(struct rtsp_audio_msi *)r->a_msi->priv:NULL;
|
||||
struct frame *jpeg;
|
||||
struct frame *audio;
|
||||
struct framebuff *fb = NULL;
|
||||
struct framebuff *audio_fb = NULL;
|
||||
|
||||
int count_times = 0;
|
||||
int get_times_out = 0;
|
||||
int cnt_num = 0;
|
||||
uint32_t time_for_count;
|
||||
uint32_t time = 0;
|
||||
time_for_count = os_jiffies();
|
||||
|
||||
while(1)
|
||||
{
|
||||
if(audio_rtsp) {
|
||||
audio_fb = msi_get_fb(audio_rtsp->msi, 0);
|
||||
if(audio_fb && audio_fb->mtype != F_AUDIO) {
|
||||
msi_delete_fb(NULL, audio_fb);
|
||||
audio_fb = NULL;
|
||||
}
|
||||
if(audio_fb) {
|
||||
audio = audio_ex->jf;
|
||||
audio->get_f = (void*)audio_fb;
|
||||
audio->length = audio_fb->len;
|
||||
audio->timestamp = audio_fb->time;
|
||||
audio->format = FORMAT_AUDIO;
|
||||
audio_ex->f(audio,audio_ex->d);
|
||||
}
|
||||
}
|
||||
fb = msi_get_fb(jpg_rtsp->msi, 0);
|
||||
if(fb && (fb->mtype != F_H264 && fb->mtype != F_JPG))
|
||||
{
|
||||
msi_delete_fb(NULL, fb);
|
||||
fb = NULL;
|
||||
}
|
||||
if (fb)
|
||||
{
|
||||
jpeg = ex->jf;
|
||||
jpeg->get_f = (void*)fb;
|
||||
jpeg->node_len = fb->len;
|
||||
jpeg->d = (uint8_t*)fb->data;
|
||||
jpeg->first_length = fb->len;
|
||||
jpeg->length = fb->len;
|
||||
jpeg->timestamp = fb->time;
|
||||
jpeg->format = FORMAT_JPEG;
|
||||
|
||||
cnt_num++;
|
||||
if((os_jiffies() - time_for_count) > 1000){
|
||||
time_for_count = os_jiffies();
|
||||
_os_printf(KERN_INFO"cnt_num:%d\r\n",cnt_num);
|
||||
cnt_num = 0;
|
||||
}
|
||||
|
||||
_os_printf("#");
|
||||
ex->f( jpeg, ex->d );
|
||||
}
|
||||
|
||||
if(fb)
|
||||
{
|
||||
ex->ready = 0;
|
||||
count_times++;
|
||||
if(count_times>25)
|
||||
{
|
||||
_os_printf("time:%lld\n", os_jiffies() - time);
|
||||
count_times = 0;
|
||||
time = os_jiffies();
|
||||
}
|
||||
}
|
||||
if(!fb && !audio_fb)
|
||||
{
|
||||
os_sleep_ms(1);
|
||||
get_times_out++;
|
||||
if(get_times_out>1000)
|
||||
{
|
||||
ex->f( NULL, ex->d );
|
||||
get_times_out = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
get_times_out = 0;
|
||||
}
|
||||
|
||||
fb = NULL;
|
||||
}
|
||||
}
|
||||
159
sdk/app/spook/frame.h
Normal file
159
sdk/app/spook/frame.h
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
/*
|
||||
#define FORMAT_EMPTY 0
|
||||
#define FORMAT_RAW_RGB24 1
|
||||
#define FORMAT_RAW_UYVY 2
|
||||
#define FORMAT_RAW_BGR24 3
|
||||
#define FORMAT_PCM 64
|
||||
#define FORMAT_MPEG4 100
|
||||
#define FORMAT_JPEG 101
|
||||
#define FORMAT_MPV 102
|
||||
#define FORMAT_H263 103
|
||||
#define FORMAT_MPA 200
|
||||
#define FORMAT_ALAW 201
|
||||
*/
|
||||
#ifndef __FRAME_H
|
||||
#define __FRAME_H
|
||||
#include "spook_config.h"
|
||||
#include "jpgdef.h"
|
||||
#include "sys_config.h"
|
||||
#include "stream_frame.h"
|
||||
#include "basic_include.h"
|
||||
#include "lib/multimedia/msi.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#define FORMAT_JPEG 101
|
||||
#define FORMAT_H264 96
|
||||
#define FORMAT_AUDIO 97
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
RECORD_FLAG,
|
||||
SAVE_PIC_FLAG,
|
||||
WIFI_FPV_FLAG,
|
||||
|
||||
};
|
||||
|
||||
struct frame;
|
||||
|
||||
typedef int (*frame_destructor)( struct frame *f, void *d );
|
||||
typedef void (*free_func)(void*node);
|
||||
|
||||
struct frame
|
||||
{
|
||||
int ref_count;
|
||||
int size;
|
||||
int format;
|
||||
int width;
|
||||
int height;
|
||||
int length;
|
||||
int node_len;
|
||||
int first_length;
|
||||
unsigned char *d;//链表第一帧数据
|
||||
void * get_f;
|
||||
struct jpeg_fifo *j;
|
||||
uint32_t timestamp;
|
||||
free_func free;
|
||||
};
|
||||
|
||||
|
||||
void init_frame_heap( int size, int count );
|
||||
int get_max_frame_size(void);
|
||||
struct frame *new_frame(void);
|
||||
void ref_frame( struct frame *f );
|
||||
void unref_frame( struct frame *f );
|
||||
|
||||
|
||||
struct frame_slot {
|
||||
struct frame_slot *next;
|
||||
struct frame_slot *prev;
|
||||
int pending;
|
||||
struct frame *f;
|
||||
};
|
||||
|
||||
typedef void (*frame_deliver_func)( struct frame *f, void *d );
|
||||
typedef void (*frame_disconnect_func)( struct frame *f, void *d );
|
||||
|
||||
|
||||
struct frame_exchanger {
|
||||
int ready;
|
||||
int scan_ready;
|
||||
|
||||
frame_deliver_func f;
|
||||
struct frame *jf;
|
||||
void *d;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct rtp_node
|
||||
{
|
||||
void *video_ex; //frame_exchanger
|
||||
void *audio_ex;
|
||||
void *priv;
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct rtsp_priv
|
||||
{
|
||||
struct rtp_node *live_node;
|
||||
//stream *video_s;
|
||||
//stream *audio_s;
|
||||
stream *webfile_s;
|
||||
struct msi *video_msi;
|
||||
struct msi *audio_msi;
|
||||
struct msi *v_msi;
|
||||
struct msi *a_msi;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct frame_exchanger *new_exchanger( int slots,
|
||||
frame_deliver_func func, void *d );
|
||||
|
||||
/* master functions */
|
||||
int exchange_frame( struct frame_exchanger *ex, struct frame *frame );
|
||||
|
||||
/* slave functions */
|
||||
struct frame *get_next_frame( struct frame_exchanger *ex, int wait );
|
||||
void deliver_frame( struct frame_exchanger *ex, struct frame *f );
|
||||
|
||||
|
||||
|
||||
void *spook_register_video(const char *name,const char *action_name,int node_num);
|
||||
void *spook_register_audio(const char *name,const char *action_name,int node_num,void *afmemt);
|
||||
void spook_send_thread2(void *d);
|
||||
int32_t spook_register_del_msg(void *signal);
|
||||
struct frame_exchanger *new_exchanger_audio( int slots,frame_deliver_func func, void *d );
|
||||
struct frame *new_audio_frame(void);
|
||||
|
||||
struct msi *rtsp_msi_init(const char *name,uint8_t filter_type,uint8_t srcID);
|
||||
struct msi *rtsp_audio_msi_init(const char *name);
|
||||
void rtsp_msi_deinit(struct msi *msi);
|
||||
void rtsp_audio_msi_deinit(struct msi *msi);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
72
sdk/app/spook/log.c
Normal file
72
sdk/app/spook/log.c
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
//#include <test_util.h>
|
||||
#include <csi_config.h>
|
||||
|
||||
#include "lwip/api.h"
|
||||
|
||||
#if 0
|
||||
|
||||
//#include <portmacro.h>
|
||||
|
||||
//extern void Uart_PutStr(unsigned char *p_str);
|
||||
|
||||
#define LOG_BUFFER_SIZE 1024
|
||||
|
||||
static int minlevel;
|
||||
void spook_log_init( int min )
|
||||
{
|
||||
minlevel = min;
|
||||
}
|
||||
|
||||
char string[ LOG_BUFFER_SIZE ];
|
||||
|
||||
void spook_debug( int level, char *fmt, ... )
|
||||
{
|
||||
|
||||
#if 1
|
||||
va_list ap;
|
||||
//static char string[ LOG_BUFFER_SIZE ];
|
||||
//OS_CPU_SR cpu_sr = 0u;
|
||||
|
||||
if( level < minlevel )
|
||||
{
|
||||
va_end( ap );
|
||||
return;
|
||||
}
|
||||
|
||||
//OS_ENTER_CRITICAL();
|
||||
//disable_irq();
|
||||
|
||||
va_start( ap, fmt );
|
||||
vsprintf( string , fmt, ap );
|
||||
va_end( ap );
|
||||
|
||||
//Uart_PutStr(string);
|
||||
_os_printf("%s",string);
|
||||
//__enable_irq();
|
||||
#endif
|
||||
|
||||
//OS_EXIT_CRITICAL();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
33
sdk/app/spook/log.h
Normal file
33
sdk/app/spook/log.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#define SL_DEBUG 0
|
||||
#define SL_VERBOSE 1
|
||||
#define SL_INFO 2
|
||||
#define SL_WARN 3
|
||||
#define SL_ERR 4
|
||||
|
||||
#define spook_log(l, s, args...) //spook_debug(l, s"\n", ##args)
|
||||
#define c99_log(l, s, ...) //spook_debug(l, s"\n", ##__VA_ARGS__)
|
||||
|
||||
//void spook_debug( int level, char *fmt, ... );
|
||||
//void spook_log_init( int min );
|
||||
|
||||
|
||||
|
||||
|
||||
411
sdk/app/spook/pmsg.c
Normal file
411
sdk/app/spook/pmsg.c
Normal file
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
//#include "lib/common.h"
|
||||
//#include "sys_config.h"
|
||||
|
||||
|
||||
#include <log.h>
|
||||
#include <pmsg.h>
|
||||
#include "typesdef.h"
|
||||
#include "osal/string.h"
|
||||
|
||||
#if 0
|
||||
int strcasecmp(const char* str1, const char* str2)
|
||||
{
|
||||
while (*str1 != '\0' && *str2 != '\0')
|
||||
{
|
||||
if (*str1 != *str2)
|
||||
{
|
||||
if (((*str1 > 'a' || *str1 < 'z') && (*str1 - 'a' == *str2 - 'A')) ||
|
||||
((*str1 > 'A' || *str1 < 'Z') && (*str1 - 'A' == *str2 - 'a')))
|
||||
{
|
||||
str1++;
|
||||
str2++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
str1++;
|
||||
str2++;
|
||||
}
|
||||
}
|
||||
|
||||
return *str1 - *str2;
|
||||
}
|
||||
|
||||
|
||||
int strncasecmp(const char* str1, const char* str2, size_t n)
|
||||
{
|
||||
while (*str1 != '\0' && *str2 != '\0' && n-- > 1)
|
||||
{
|
||||
if (*str1 != *str2)
|
||||
{
|
||||
if (((*str1 > 'a' || *str1 < 'z') && (*str1 - 'a' == *str2 - 'A')) ||
|
||||
((*str1 > 'A' || *str1 < 'Z') && (*str1 - 'A' == *str2 - 'a')))
|
||||
{
|
||||
str1++;
|
||||
str2++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
str1++;
|
||||
str2++;
|
||||
}
|
||||
}
|
||||
|
||||
if (((*str1 > 'a' || *str1 < 'z') && (*str1 - 'a' == *str2 - 'A')) ||
|
||||
((*str1 > 'A' || *str1 < 'Z') && (*str1 - 'A' == *str2 - 'a')))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return *str1 - *str2;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
char *add_pmsg_string( struct pmsg *msg, char *s )
|
||||
{
|
||||
int len = strlen( s ) + 1;
|
||||
char *p;
|
||||
|
||||
if( msg->msg_len + len > msg->max_len ) return NULL;
|
||||
p = (char *)(msg->msg + msg->msg_len);
|
||||
msg->msg_len += len;
|
||||
strcpy( p, s );
|
||||
return p;
|
||||
}
|
||||
|
||||
static int inline delim( char *d, int len, char term1, char term2 )
|
||||
{
|
||||
int s;
|
||||
|
||||
for( s = 0; s < len; ++s )
|
||||
if( d[s] == term1 || d[s] == term2 ||
|
||||
d[s] == '\r' || d[s] == '\n' )
|
||||
break;
|
||||
return s;
|
||||
}
|
||||
|
||||
int parse_pmsg( struct pmsg *msg )
|
||||
{
|
||||
int i = 0;
|
||||
unsigned char *d = msg->msg;
|
||||
int len = msg->msg_len;
|
||||
|
||||
msg->header_count = 0;
|
||||
|
||||
/* Was it all whitespace? */
|
||||
if( len - i < 4 ) return -1;
|
||||
|
||||
/* Check for a slash in the first word */
|
||||
for( i = 0; i < len && d[i] != ' ' && d[i] != '/'; ++i );
|
||||
if( i == len ) return -1;
|
||||
|
||||
/* Responses begin with "PROTO/" */
|
||||
if( d[i] == '/' )
|
||||
{
|
||||
msg->type = PMSG_RESP;
|
||||
/* The first word is the protocol name and version */
|
||||
msg->proto_id = (char *)d;
|
||||
i = delim( (char *)d, len, ' ', 0 );
|
||||
/* Check for a space following the version number */
|
||||
if( d[i] != ' ' ) return -1;
|
||||
d[i++] = 0;
|
||||
while( i < len && d[i] == ' ' ) ++i;
|
||||
/* The next word is the 3-digit response code, then a space */
|
||||
if( len - i < 4 || ! isdigit( d[i] ) || ! isdigit( d[i+1] )
|
||||
|| ! isdigit( d[i+2] ) || d[i+3] != ' ' )
|
||||
return -1;
|
||||
msg->sl.stat.code = atoi( (char *)(d + i) );
|
||||
i += 4;
|
||||
while( i < len && d[i] == ' ' ) ++i;
|
||||
/* The rest of the line is the textual response */
|
||||
msg->sl.stat.reason = (char *)(d + i);
|
||||
i += delim( (char *)(d + i), len - i, 0, 0 );
|
||||
d[i++] = 0;
|
||||
} else
|
||||
{
|
||||
msg->type = PMSG_REQ;
|
||||
/* The first word is the method */
|
||||
msg->sl.req.method = (char *)d;
|
||||
i = delim( (char *)d, len, ' ', 0 );
|
||||
/* Then a space */
|
||||
if( i >= len || d[i] != ' ' ) return -1;
|
||||
d[i++] = 0;
|
||||
while( i < len && d[i] == ' ' ) ++i;
|
||||
/* The second word is the URI */
|
||||
msg->sl.req.uri = (char *)(d + i);
|
||||
i += delim( (char *)(d + i), len - i, ' ', 0 );
|
||||
/* Then a space */
|
||||
if( i >= len || d[i] != ' ' ) return -1;
|
||||
d[i++] = 0;
|
||||
while( i < len && d[i] == ' ' ) ++i;
|
||||
/* The last word is the protocol name and version */
|
||||
msg->proto_id = (char *)(d + i);
|
||||
i += delim( (char *)(d + i), len - i, 0, 0 );
|
||||
d[i++] = 0;
|
||||
#if 0
|
||||
if(!strcmp(msg->sl.req.method, "SETID") ||
|
||||
!strcmp(msg->sl.req.method, "GETID") ||
|
||||
!strcmp(msg->sl.req.method, "RESET")) {
|
||||
/* The last word is the protocol name and version */
|
||||
msg->proto_id = (char *)(d + i);
|
||||
i += delim( (char *)(d + i), len - i, ' ', 0 );
|
||||
/* Then a space */
|
||||
if( i >= len || d[i] != ' ' ) return -1;
|
||||
d[i++] = 0;
|
||||
while( i < len && d[i] == ' ' ) ++i;
|
||||
/* The last word is the essid */
|
||||
msg->essid = (char *)(d + i);
|
||||
i += delim( (char *)(d + i), len - i, ' ', 0 );
|
||||
/* Then a space */
|
||||
if( i >= len || d[i] != ' ' ) return -1;
|
||||
d[i++] = 0;
|
||||
while( i < len && d[i] == ' ' ) ++i;
|
||||
/* The last word is the password */
|
||||
msg->password = (char *)(d + i);
|
||||
i += delim( (char *)(d + i), len - i, ' ', 0 );
|
||||
/* Then a space */
|
||||
if( i >= len || d[i] != ' ' ) return -1;
|
||||
d[i++] = 0;
|
||||
while( i < len && d[i] == ' ' ) ++i;
|
||||
/* The last word is the fly_id */
|
||||
msg->fly_id = (char *)(d + i);
|
||||
i += delim( (char *)(d + i), len - i, 0, 0 );
|
||||
d[i++] = 0;
|
||||
} else {
|
||||
/* The last word is the protocol name and version */
|
||||
msg->proto_id = (char *)(d + i);
|
||||
i += delim( (char *)(d + i), len - i, 0, 0 );
|
||||
d[i++] = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* Skip any trailing space */
|
||||
while( i < len && d[i] == ' ' ) ++i;
|
||||
/* Skip the \r if we didn't kill it already */
|
||||
if( i < len && d[i] == '\r' ) ++i;
|
||||
/* We should be at the end of the line now */
|
||||
if( i >= len || d[i++] != '\n' ) return -1;
|
||||
|
||||
/* Now, parse all the header lines */
|
||||
for(;;)
|
||||
{
|
||||
/* There may be a \r here if we're at the end of the headers */
|
||||
if( i < len && d[i] == '\r' ) ++i;
|
||||
/* If there's no more data, we're done */
|
||||
if( i == len ) return len;
|
||||
/* If there's a newline, we're at the end of the headers */
|
||||
if( d[i] == '\n' ) return i + 1;
|
||||
/* XXX headers beginning with whitespace are continuations */
|
||||
if( d[i] == '\t' || d[i] == ' ' ) return -1;
|
||||
/* The first thing on the line is the header name */
|
||||
msg->fields[msg->header_count].name = (char *)(d + i);
|
||||
/* The name ends with optional spaces then a colon */
|
||||
i += delim( (char *)(d + i), len - i, ' ', ':' );
|
||||
if( i >= len ) return -1;
|
||||
/* If the optional spaces are present, skip them */
|
||||
if( d[i] == ' ' )
|
||||
{
|
||||
d[i++] = 0;
|
||||
while( i < len && d[i] == ' ' ) ++i;
|
||||
}
|
||||
/* Make sure the colon is present */
|
||||
if( i >= len || d[i] != ':' ) return -1;
|
||||
d[i++] = 0;
|
||||
/* Skip any whitespace after the colon */
|
||||
while( i < len && ( d[i] == ' ' || d[i] == '\t' ) ) ++i;
|
||||
/* Everything else on the line is the header data */
|
||||
msg->fields[msg->header_count].value = (char *)(d + i);
|
||||
i += delim( (char *)(d + i), len - i, 0, 0 );
|
||||
d[i++] = 0;
|
||||
/* We should be at the end of the line now */
|
||||
if( i >= len || d[i++] != '\n' ) return -1;
|
||||
++msg->header_count;
|
||||
}
|
||||
}
|
||||
|
||||
char *get_header( struct pmsg *msg, char *name )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < msg->header_count; ++i )
|
||||
if( ! os_strcasecmp( msg->fields[i].name, name ) )
|
||||
return msg->fields[i].value;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int add_header( struct pmsg *msg, char *name, char *value )
|
||||
{
|
||||
if( msg->header_count == MAX_FIELDS ) return -1;
|
||||
/* Put the name and value back-to-back at the end of the message */
|
||||
msg->fields[msg->header_count].name = add_pmsg_string( msg, name );
|
||||
if( ! msg->fields[msg->header_count].name ) return -1;
|
||||
msg->fields[msg->header_count].value = add_pmsg_string( msg, value );
|
||||
if( ! msg->fields[msg->header_count].value ) return -1;
|
||||
++msg->header_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_header_printf( struct pmsg *msg, char *name, char *fmt, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int len;
|
||||
|
||||
/* do the vsnprintf first to clean up the stack */
|
||||
va_start( ap, fmt );
|
||||
len = vsnprintf( (char *)(msg->msg + msg->msg_len), msg->max_len - msg->msg_len,
|
||||
fmt, ap );
|
||||
va_end( ap );
|
||||
|
||||
/* check for errors */
|
||||
if( msg->header_count == MAX_FIELDS ) return -1;
|
||||
/* vsnprintf will return the length of the formatted string
|
||||
* regardless of length, although the actual output may be truncated */
|
||||
if( msg->msg_len + len >= msg->max_len ) return -1;
|
||||
msg->fields[msg->header_count].value = (char *)(msg->msg + msg->msg_len);
|
||||
msg->msg_len += len + 1;
|
||||
/* add the name */
|
||||
msg->fields[msg->header_count].name = add_pmsg_string( msg, name );
|
||||
if( ! msg->fields[msg->header_count].name ) return -1;
|
||||
++msg->header_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int replace_header( struct pmsg *msg, char *name, char *value )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < msg->header_count; ++i )
|
||||
if( ! strcasecmp( msg->fields[i].name, name ) )
|
||||
{
|
||||
/* If we can't reuse the space from the original value,
|
||||
* we have to allocate space for the new value at the
|
||||
* end of the message */
|
||||
if( strlen( msg->fields[i].value ) < strlen( value ) )
|
||||
{
|
||||
char *p = add_pmsg_string( msg, value );
|
||||
if( ! p ) return -1;
|
||||
msg->fields[i].value = p;
|
||||
} else strcpy( msg->fields[i].value, value );
|
||||
return 0;
|
||||
}
|
||||
/* It doesn't exist, so we'll insert it as new */
|
||||
return add_header( msg, name, value );
|
||||
}
|
||||
|
||||
int copy_headers( struct pmsg *dest, struct pmsg *src, char *name )
|
||||
{
|
||||
int i, count = 0;
|
||||
|
||||
for( i = 0; i < src->header_count; ++i )
|
||||
if( ! strcasecmp( src->fields[i].name, name ) )
|
||||
{
|
||||
add_header( dest, src->fields[i].name,
|
||||
src->fields[i].value );
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* get_param() returns 1 for found, 0 for not found, -1 for error */
|
||||
int get_param( char *value, char *tag, char *dest, int size )
|
||||
{
|
||||
int taglen, i;
|
||||
char *c;
|
||||
|
||||
if( ! value ) return -1;
|
||||
|
||||
taglen = strlen( tag );
|
||||
|
||||
for( c = value; c; c = strchr( c, ';' ) )
|
||||
{
|
||||
++c;
|
||||
if( ! strncasecmp( c, tag, taglen ) )
|
||||
{
|
||||
c += taglen;
|
||||
if( *c == 0 || *c == ';' ) /* Value-less tag */
|
||||
{
|
||||
if( dest && size > 0 ) *dest = 0;
|
||||
return 1;
|
||||
} else if( *c == '=' ) /* Tag has an associated value */
|
||||
{
|
||||
if( ! dest || size <= 0 ) return 1;
|
||||
++c;
|
||||
for( i = 0; *c && *c != ';' && i < size;
|
||||
++i, ++c )
|
||||
dest[i] = *c;
|
||||
if( i == size ) return -1;
|
||||
dest[i] = 0;
|
||||
return 1;
|
||||
} /* Otherwise, it's a false hit */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
struct pmsg *new_pmsg( int size )
|
||||
{
|
||||
struct pmsg *msg;
|
||||
void *v;
|
||||
|
||||
if( ! ( v = malloc( sizeof( struct pmsg ) + size ) ) )
|
||||
{
|
||||
spook_log( SL_ERR, "unable to allocate memory for message" );
|
||||
return NULL;
|
||||
}
|
||||
msg = (struct pmsg *)v;
|
||||
msg->msg = v + sizeof( struct pmsg );
|
||||
msg->max_len = size;
|
||||
msg->msg_len = 0;
|
||||
msg->header_count = 0;
|
||||
msg->proto_id = NULL;
|
||||
|
||||
|
||||
//_os_printf("%s malloc:%X\n",__FUNCTION__,msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
void free_pmsg( struct pmsg *msg )
|
||||
{
|
||||
//_os_printf("%s free:%X\n",__FUNCTION__,msg);
|
||||
free( msg );
|
||||
}
|
||||
45
sdk/app/spook/pmsg.h
Normal file
45
sdk/app/spook/pmsg.h
Normal file
@@ -0,0 +1,45 @@
|
||||
struct hdrf {
|
||||
char *name;
|
||||
char *value;
|
||||
};
|
||||
|
||||
#define MAX_FIELDS 32
|
||||
|
||||
struct pmsg {
|
||||
unsigned char *msg;
|
||||
int max_len;
|
||||
int msg_len;
|
||||
struct hdrf fields[MAX_FIELDS];
|
||||
int header_count;
|
||||
enum { PMSG_REQ, PMSG_RESP } type;
|
||||
char *proto_id;
|
||||
// char *essid;
|
||||
// char *password;
|
||||
// char *fly_id;
|
||||
union {
|
||||
struct {
|
||||
char *method;
|
||||
char *uri;
|
||||
} req;
|
||||
struct {
|
||||
int code;
|
||||
char *reason;
|
||||
} stat;
|
||||
} sl;
|
||||
};
|
||||
|
||||
char *add_pmsg_string( struct pmsg *msg, char *s );
|
||||
int parse_pmsg( struct pmsg *msg );
|
||||
char *get_header( struct pmsg *msg, char *name );
|
||||
int add_header( struct pmsg *msg, char *name, char *value );
|
||||
int add_header_printf( struct pmsg *msg, char *name, char *fmt, ... );
|
||||
int replace_header( struct pmsg *msg, char *name, char *value );
|
||||
int copy_headers( struct pmsg *dest, struct pmsg *src, char *name );
|
||||
int get_param( char *value, char *tag, char *dest, int size );
|
||||
struct pmsg *new_pmsg( int size );
|
||||
void free_pmsg( struct pmsg *msg );
|
||||
|
||||
/* http-auth.c */
|
||||
int check_digest_response( struct pmsg *msg, char *realm,
|
||||
char *username, char *password );
|
||||
int add_digest_challenge( struct pmsg *msg, char *realm, int stale );
|
||||
199
sdk/app/spook/rtp-audio.c
Normal file
199
sdk/app/spook/rtp-audio.c
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
|
||||
#include "lwip\api.h"
|
||||
#include "lwip\tcp.h"
|
||||
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
#include <rtp.h>
|
||||
#include <rtp_media.h>
|
||||
#include <spook_config.h>
|
||||
#include "list.h"
|
||||
#include "jpgdef.h"
|
||||
#include "rtp.h"
|
||||
#include "session.h"
|
||||
#include "osal/sleep.h"
|
||||
#include "lib/audio/audio_code/audio_code.h"
|
||||
|
||||
#define AUDIO_SAMPLE 8000
|
||||
|
||||
struct rtp_audio {
|
||||
//为了获取到frame,因为使用了链表形式
|
||||
struct frame *f;
|
||||
unsigned int timestamp;
|
||||
};
|
||||
|
||||
static int audio_get_sdp( char *dest, int len, int payload, int port, void *d )
|
||||
{
|
||||
uint32_t audio_sample_rate = (uint32)d;
|
||||
_os_printf("audio_sample_rate:%d\n",audio_sample_rate);
|
||||
if(audio_sample_rate == 0)
|
||||
{
|
||||
audio_sample_rate = AUDIO_SAMPLE;//默认采样率
|
||||
}
|
||||
|
||||
return snprintf( dest, len, "m=audio %d RTP/AVP 97\r\na=rtpmap:97 mpeg4-generic/%d/1\r\na=fmtp:97 profile-level-id=1; mode=AAC-hbr; config=1588; sizeLength=13; indexlength=3; indexdeltalength=3\r\n", port,audio_sample_rate);
|
||||
// return snprintf( dest, len, "m=audio %d RTP/AVP 97\r\na=rtpmap:97 PCMA/%d/1\r\n", port,audio_sample_rate);
|
||||
// return snprintf( dest, len, "m=audio %d RTP/AVP 97\r\na=rtpmap:97 L16/%d/1\r\n", port,audio_sample_rate);
|
||||
}
|
||||
|
||||
static int audio_process_frame( struct frame *f, void *d )
|
||||
{
|
||||
_os_printf("+");
|
||||
struct rtp_media *rtp = (struct rtp_media *)d;
|
||||
struct rtp_audio *out = (struct rtp_audio *)rtp->private;
|
||||
out->f = f;
|
||||
out->timestamp = f->timestamp*(rtp->sample_rate/(1000));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int audio_get_payload( int payload, void *d )
|
||||
{
|
||||
_os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
return 97;
|
||||
}
|
||||
|
||||
static int audio_send( struct rtp_endpoint *ep, void *d )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audio_build_au_header( unsigned char *au_hdr, int au_size )
|
||||
{
|
||||
au_hdr[0] = 0x00;
|
||||
au_hdr[1] = 0x10;
|
||||
au_hdr[2] = ( au_size & 0x1FE0 ) >> 5;
|
||||
au_hdr[3] = ( au_size & 0x1F ) << 3;
|
||||
}
|
||||
|
||||
static int audio_send_au_to_endpoint( struct rtp_endpoint *ep, uint32_t timestamp, uint8_t *au_data, uint32_t au_size, int times )
|
||||
{
|
||||
struct iovec v[3];
|
||||
uint8_t au_hdr[4];
|
||||
int max_payload;
|
||||
uint32_t max_fragment_size;
|
||||
uint32_t offset = 0;
|
||||
uint32_t fragment_len;
|
||||
|
||||
if( !ep || !ep->sendEnable || !au_data || au_size <= 0 ) return -1;
|
||||
if( au_size > 0x1FFF )
|
||||
{
|
||||
spook_log( SL_ERR, "AAC AU too large for 13-bit AU-size field: %d", au_size );
|
||||
ep->sendEnable = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
max_payload = rtp_get_payload_size_limit( ep, RTP_HEADER_SIZE );
|
||||
if( max_payload <= (int)sizeof( au_hdr ) )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
max_fragment_size = max_payload - sizeof( au_hdr );
|
||||
audio_build_au_header( au_hdr, au_size );
|
||||
v[1].iov_base = au_hdr;
|
||||
v[1].iov_len = sizeof( au_hdr );
|
||||
|
||||
while( offset < au_size )
|
||||
{
|
||||
fragment_len = au_size - offset;
|
||||
if( fragment_len > max_fragment_size )
|
||||
fragment_len = max_fragment_size;
|
||||
|
||||
v[2].iov_base = (void *)( au_data + offset );
|
||||
v[2].iov_len = fragment_len;
|
||||
if( rtp_sendmsg( ep, v, 3, timestamp, offset + fragment_len >= au_size, times ) < 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
offset += fragment_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_send_more( rtp_loop_search_ep search, void *ls, void *track, void *d )
|
||||
{
|
||||
struct rtp_endpoint *ep;
|
||||
void *head;
|
||||
uint32_t au_size;
|
||||
struct rtp_audio *out = (struct rtp_audio *)d;
|
||||
struct framebuff *fb = (struct framebuff *)out->f->get_f;
|
||||
|
||||
if( !fb || fb->len <= 7 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
au_size = fb->len - 7;
|
||||
|
||||
head = ls;
|
||||
while(head)
|
||||
{
|
||||
head = search(head,track,(void*)&ep);
|
||||
if(!ep)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(ep->sendEnable)
|
||||
{
|
||||
if( audio_send_au_to_endpoint( ep, out->timestamp, fb->data + 7, au_size, 10 ) < 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
}
|
||||
}
|
||||
ep->sendEnable = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct rtp_media *new_rtp_media_audio_stream( struct stream *stream )
|
||||
{
|
||||
struct rtp_audio *out;
|
||||
int fincr, fbase;
|
||||
struct rtp_media *m;
|
||||
|
||||
stream->get_framerate( stream, &fincr, &fbase );
|
||||
out = (struct rtp_audio *)malloc( sizeof( struct rtp_audio ) );
|
||||
out->f = NULL;
|
||||
out->timestamp = 0;
|
||||
m = new_rtp_rtcp_media( audio_get_sdp, audio_get_payload, audio_process_frame, audio_send, new_rtcp_send, out );
|
||||
if(m)
|
||||
{
|
||||
m->sample_rate = AUDIO_SAMPLE;//默认
|
||||
m->type = 1; //音频
|
||||
m->send_more = audio_send_more;
|
||||
m->per_ms_incr = AUDIO_SAMPLE/(1000);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
6
sdk/app/spook/rtp-audio.h
Normal file
6
sdk/app/spook/rtp-audio.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef __RTP_AUDIO_H
|
||||
#define __RTP_AUDIO_H
|
||||
#include "stream.h"
|
||||
struct rtp_media *new_rtp_media_audio_stream( struct stream *stream );
|
||||
|
||||
#endif
|
||||
320
sdk/app/spook/rtp-h264.c
Normal file
320
sdk/app/spook/rtp-h264.c
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
|
||||
#include "lwip\api.h"
|
||||
#include "lwip\tcp.h"
|
||||
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
#include <rtp.h>
|
||||
#include <rtp_media.h>
|
||||
#include <spook_config.h>
|
||||
#include "list.h"
|
||||
#include "jpgdef.h"
|
||||
#include "rtp.h"
|
||||
#include "session.h"
|
||||
#include "osal/sleep.h"
|
||||
#include "stream_define.h"
|
||||
|
||||
#define H264_SAMPLE 90000
|
||||
|
||||
struct rtp_h264 {
|
||||
//为了获取到frame,因为使用了链表形式
|
||||
unsigned char *d;//链表第一帧数据
|
||||
struct frame *f;
|
||||
unsigned int timestamp;
|
||||
uint8_t first_flag;
|
||||
uint8_t last_count;
|
||||
};
|
||||
|
||||
static int h264_get_sdp( char *dest, int len, int payload, int port, void *d )
|
||||
{
|
||||
uint32_t h264_sample_rate = (uint32)d;
|
||||
_os_printf("h264_sample_rate:%d\n",h264_sample_rate);
|
||||
if(h264_sample_rate == 0)
|
||||
{
|
||||
h264_sample_rate = H264_SAMPLE;//默认采样率
|
||||
}
|
||||
|
||||
return snprintf( dest, len, "m=video %d RTP/AVP 96\r\na=rtpmap:96 H264/%d\r\na=decode_buf=300\r\n", port,h264_sample_rate);
|
||||
}
|
||||
|
||||
static int h264_process_frame( struct frame *f, void *d )
|
||||
{
|
||||
|
||||
struct rtp_media *rtp = (struct rtp_media *)d;
|
||||
struct rtp_h264 *out = (struct rtp_h264 *)rtp->private;
|
||||
_os_printf("+");
|
||||
out->f = f;
|
||||
out->d = f->d;
|
||||
out->timestamp = f->timestamp*(rtp->sample_rate/1000);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int h264_get_payload( int payload, void *d )
|
||||
{
|
||||
return FORMAT_H264;
|
||||
}
|
||||
|
||||
|
||||
static int h264_send( struct rtp_endpoint *ep, void *d )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h264_send_nalu( struct rtp_endpoint *ep, uint32_t timestamp, uint8_t *nalu, uint32_t nalu_len, uint8_t marker, int times, int max_payload )
|
||||
{
|
||||
struct iovec v[3];
|
||||
uint8_t fu_hdr[2];
|
||||
uint32_t offset;
|
||||
uint32_t chunk;
|
||||
|
||||
if( !ep || !ep->sendEnable || !nalu || nalu_len <= 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( nalu_len <= max_payload )
|
||||
{
|
||||
// single NALU
|
||||
v[1].iov_base = (void *)nalu;
|
||||
v[1].iov_len = nalu_len;
|
||||
if( rtp_sendmsg( ep, v, 2, timestamp, marker, times ) < 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( nalu_len <= 1 || max_payload <= 2 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// FU-A
|
||||
fu_hdr[0] = ( nalu[0] & 0xE0 ) | 28;
|
||||
for( offset = 1; offset < nalu_len; offset += chunk )
|
||||
{
|
||||
chunk = nalu_len - offset;
|
||||
if( chunk > max_payload - 2 )
|
||||
{
|
||||
chunk = max_payload - 2;
|
||||
}
|
||||
fu_hdr[1] = nalu[0] & 0x1F;
|
||||
if( offset == 1 )
|
||||
{
|
||||
fu_hdr[1] |= 0x80;
|
||||
}
|
||||
|
||||
if( offset + chunk >= nalu_len )
|
||||
{
|
||||
fu_hdr[1] |= 0x40;
|
||||
}
|
||||
|
||||
v[1].iov_base = fu_hdr;
|
||||
v[1].iov_len = sizeof( fu_hdr );
|
||||
v[2].iov_base = (void *)( nalu + offset );
|
||||
v[2].iov_len = chunk;
|
||||
if( rtp_sendmsg( ep, v, 3, timestamp, offset + chunk >= nalu_len ? marker : 0, times ) < 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void h264_send_nalu_to_endpoint( struct rtp_endpoint *ep, struct rtp_h264 *out, uint8_t *nalu, uint32_t nalu_len, uint8_t marker, int times )
|
||||
{
|
||||
int max_payload;
|
||||
|
||||
if( !ep || !ep->sendEnable )
|
||||
{
|
||||
return;
|
||||
}
|
||||
max_payload = rtp_get_payload_size_limit( ep, RTP_HEADER_SIZE );
|
||||
if( max_payload <= 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
return;
|
||||
}
|
||||
if( h264_send_nalu( ep, out->timestamp, nalu, nalu_len, marker, times, max_payload ) < 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void h264_send_sps_pps( struct rtp_endpoint *ep, struct rtp_h264 *out, struct fb_h264_s *h264, int retries )
|
||||
{
|
||||
// stap-a: 1B头 + 2B sps_len + sps + 2B pps_len + pps
|
||||
struct iovec v[6];
|
||||
unsigned char stap_hdr;
|
||||
unsigned char sps_len[2];
|
||||
unsigned char pps_len[2];
|
||||
int max_payload;
|
||||
int stap_payload_len;
|
||||
|
||||
if( !ep || !ep->sendEnable || !h264 || !h264->sps || !h264->pps || !h264->sps_len || !h264->pps_len )
|
||||
{
|
||||
_os_printf("%s %d\n", __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
max_payload = rtp_get_payload_size_limit( ep, RTP_HEADER_SIZE );
|
||||
if( max_payload <= 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
_os_printf("%s %d\n", __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
stap_payload_len = 1 + 2 + h264->sps_len + 2 + h264->pps_len;
|
||||
if( stap_payload_len > max_payload )
|
||||
{
|
||||
h264_send_nalu_to_endpoint( ep, out, h264->sps, h264->sps_len, 0, retries );
|
||||
if( ep->sendEnable )
|
||||
{
|
||||
h264_send_nalu_to_endpoint( ep, out, h264->pps, h264->pps_len, 0, retries );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
stap_hdr = ( h264->sps[0] & 0xE0 ) | 24;
|
||||
PUT_16( sps_len, h264->sps_len );
|
||||
PUT_16( pps_len, h264->pps_len );
|
||||
v[1].iov_base = &stap_hdr;
|
||||
v[1].iov_len = 1;
|
||||
v[2].iov_base = sps_len;
|
||||
v[2].iov_len = 2;
|
||||
v[3].iov_base = h264->sps;
|
||||
v[3].iov_len = h264->sps_len;
|
||||
v[4].iov_base = pps_len;
|
||||
v[4].iov_len = 2;
|
||||
v[5].iov_base = h264->pps;
|
||||
v[5].iov_len = h264->pps_len;
|
||||
if( rtp_sendmsg( ep, v, 6, out->timestamp, 0, retries ) < 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int h264_send_more( rtp_loop_search_ep search, void *ls, void *track, void *d )
|
||||
{
|
||||
struct rtp_h264 *out = (struct rtp_h264 *)d;
|
||||
struct framebuff *fb = (struct framebuff *)out->f->get_f;
|
||||
struct fb_h264_s *h264;
|
||||
uint8_t *nalu;
|
||||
uint32_t nalu_len;
|
||||
struct rtp_endpoint *ep;
|
||||
void *head;
|
||||
|
||||
if( !fb || !fb->data || fb->len <= 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
h264 = (struct fb_h264_s *)fb->priv;
|
||||
if( h264 )
|
||||
{
|
||||
if( h264->type == 1 )
|
||||
{
|
||||
out->first_flag = 1;
|
||||
}
|
||||
else if( out->last_count != h264->count )
|
||||
{
|
||||
out->first_flag = 0;
|
||||
os_printf(KERN_NOTICE"drop frame\n");
|
||||
}
|
||||
|
||||
if( !out->first_flag )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
nalu = fb->data + h264->start_len;
|
||||
nalu_len = fb->len - h264->start_len;
|
||||
if( nalu_len <= 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
out->last_count = h264->count + 1;
|
||||
head = ls;
|
||||
while( head )
|
||||
{
|
||||
head = search( head, track, (void *)&ep );
|
||||
if( !ep )
|
||||
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !ep->sendEnable )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( h264->type == 1 )
|
||||
{
|
||||
h264_send_sps_pps( ep, out, h264, 30 );
|
||||
}
|
||||
|
||||
if( ep->sendEnable )
|
||||
{
|
||||
h264_send_nalu_to_endpoint( ep, out, nalu, nalu_len, 1, 30 );
|
||||
}
|
||||
|
||||
ep->sendEnable = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct rtp_media *new_rtp_media_h264_stream( struct stream *stream )
|
||||
{
|
||||
struct rtp_h264 *out;
|
||||
int fincr, fbase;
|
||||
struct rtp_media *m;
|
||||
|
||||
stream->get_framerate( stream, &fincr, &fbase );
|
||||
out = (struct rtp_h264 *)malloc( sizeof( struct rtp_h264 ) );
|
||||
out->f = NULL;
|
||||
out->timestamp = 0;
|
||||
//return new_rtp_media( audio_get_sdp, audio_get_payload,audio_process_frame, audio_send, out );
|
||||
m = new_rtp_rtcp_media( h264_get_sdp, h264_get_payload,h264_process_frame, h264_send,new_rtcp_send, out );
|
||||
if(m)
|
||||
{
|
||||
m->sample_rate = H264_SAMPLE;//默认
|
||||
m->type = 1; //音频
|
||||
m->send_more = h264_send_more;
|
||||
m->per_ms_incr = H264_SAMPLE/(1000);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
567
sdk/app/spook/rtp-jpeg.c
Normal file
567
sdk/app/spook/rtp-jpeg.c
Normal file
@@ -0,0 +1,567 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
|
||||
#include "lwip\api.h"
|
||||
#include "lwip\tcp.h"
|
||||
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
#include <rtp.h>
|
||||
#include <rtp_media.h>
|
||||
#include <spook_config.h>
|
||||
#include "list.h"
|
||||
#include "jpgdef.h"
|
||||
#include "osal/sleep.h"
|
||||
#include "session.h"
|
||||
//#include "common.h"
|
||||
#include "csi_kernel.h"
|
||||
#include "osal/string.h"
|
||||
#include "utlist.h"
|
||||
|
||||
|
||||
#ifdef USB_EN
|
||||
#include "dev/usb/uvc_host.h"
|
||||
#endif
|
||||
|
||||
#define EXTHDR_LEN 6
|
||||
#define EXTHDROFF_MAGIC 0
|
||||
#define EXTHDROFF_VER 1
|
||||
#define EXTHDROFF_DRI 2
|
||||
#define EXTHDROFF_HUFF 4
|
||||
|
||||
struct rtp_jpeg {
|
||||
unsigned char *d;//链表第一帧数据
|
||||
int type;
|
||||
int q;
|
||||
int width;
|
||||
int height;
|
||||
int luma_table;
|
||||
int chroma_table;
|
||||
int quant[16];
|
||||
int huffoff;
|
||||
int hufflen;
|
||||
unsigned char exthdr[EXTHDR_LEN];
|
||||
unsigned char *scan_data;
|
||||
int scan_data_len;
|
||||
int offset;
|
||||
int init_done;
|
||||
int ts_incr;
|
||||
unsigned int timestamp;
|
||||
int reset_interval;
|
||||
int dri_num;
|
||||
unsigned short int dri_len[100];
|
||||
//为了获取到frame,因为使用了链表形式
|
||||
struct frame *f;
|
||||
};
|
||||
|
||||
static int parse_DQT( struct rtp_jpeg *out, unsigned char *d, int len )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < len; i += 65 )
|
||||
{
|
||||
if( ( d[i] & 0xF0 ) != 0 )
|
||||
{
|
||||
_os_printf( "Unsupported quant table precision 0x%X!\n",
|
||||
d[i] & 0xF0 );
|
||||
return -1;
|
||||
}
|
||||
//out->quant[d[i] & 0xF] = d + i + 1;
|
||||
out->quant[d[i] & 0xF] = d-out->d+i+1;
|
||||
//out->quant[d[i] & 0xF] = out->scan_data - (d + i + 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_SOF( struct rtp_jpeg *out, unsigned char *d, int len )
|
||||
{
|
||||
int c;
|
||||
|
||||
out->chroma_table = -1;
|
||||
if( d[0] != 8 )
|
||||
{
|
||||
_os_printf( "Invalid precision %d in SOF0\n", d[0] );
|
||||
return -1;
|
||||
}
|
||||
out->height = GET_16( d + 1 );
|
||||
out->width = GET_16( d + 3 );
|
||||
|
||||
|
||||
if( ( out->height & 0x7 ) || ( out->width & 0x7 ) )
|
||||
{
|
||||
os_printf(KERN_ERR"Width/height not divisible by 8!\n" );
|
||||
os_printf(KERN_ERR"w:%d\th:%d\n",out->width,out->height);
|
||||
return -1;
|
||||
}
|
||||
out->width >>= 3;
|
||||
out->height >>= 3;
|
||||
if( d[5] != 3 )
|
||||
{
|
||||
_os_printf( "Number of components is %d, not 3!\n", d[5] );
|
||||
return -1;
|
||||
}
|
||||
/* Loop over the parameters for each component */
|
||||
for( c = 6; c < 6 + 3 * 3; c += 3 )
|
||||
{
|
||||
// if( d[c + 2] >= 16 || ! out->quant[d[c + 2]] )
|
||||
// {
|
||||
// _os_printf( "Component %d specified undefined quant table %d!\n", d[c], d[c + 2] );
|
||||
// return -1;
|
||||
// }
|
||||
switch( d[c] ) /* d[c] contains the component ID */
|
||||
{
|
||||
case 1: /* Y */
|
||||
/*
|
||||
if( d[c + 1] == 0x11 ) out->type = 0;
|
||||
else if( d[c + 1] == 0x22 ) out->type = 1;
|
||||
*/
|
||||
if( d[c + 1] == 0x21 ) out->type = out->type & ~1; //YUV422
|
||||
else if( d[c + 1] == 0x22 ) out->type = (out->type & ~1)|1; //YUV420
|
||||
else
|
||||
{
|
||||
_os_printf( "Invalid sampling factor 0x%02X in Y component!\n", d[c + 1] );
|
||||
return -1;
|
||||
}
|
||||
out->luma_table = d[c + 2];
|
||||
break;
|
||||
case 2: /* Cb */
|
||||
case 3: /* Cr */
|
||||
if( d[c + 1] != 0x11 )
|
||||
{
|
||||
_os_printf( "Invalid sampling factor 0x%02X in %s component!\n", d[c + 1], d[c] == 2 ? "Cb" : "Cr" );
|
||||
return -1;
|
||||
}
|
||||
if( out->chroma_table < 0 )
|
||||
out->chroma_table = d[c + 2];
|
||||
else if( out->chroma_table != d[c + 2] )
|
||||
{
|
||||
_os_printf( "Cb and Cr components do not share a quantization table!\n" );
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_os_printf( "Invalid component %d in SOF!\n", d[c] );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_DHT( struct rtp_jpeg *out, unsigned char *d, int len )
|
||||
{
|
||||
/* We should verify that this coder uses the standard Huffman tables */
|
||||
/* Kaifan:
|
||||
* Some USB-Sensor do not use standard Huffman tables.
|
||||
* So I add Huffman following quant table.
|
||||
* It's compatible to the old version of App,
|
||||
* It just looks like some dummy quant tables exist.
|
||||
* But the dummy size can not be too long, because the old rtpdec-jpeg.c uses hdr[1024]
|
||||
*/
|
||||
if(0 == out->hufflen)
|
||||
{
|
||||
//out->huffoff = out->scan_data - (d - 4);
|
||||
out->huffoff = (int)( ( d - 4 ) - out->d );
|
||||
}
|
||||
out->hufflen += len + 4;
|
||||
PUT_16(out->exthdr+EXTHDROFF_HUFF, out->hufflen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parse_DRI (struct rtp_jpeg *out, unsigned char *d)
|
||||
{
|
||||
if (GET_16( d )) {
|
||||
out->type |= 0x40;
|
||||
out->reset_interval = GET_16( d );
|
||||
/* Kaifan:
|
||||
* Some USB-Sensor's reset-interval is not width/16.
|
||||
* But the origin room for reset-interval is used by res_len.
|
||||
* So Save the reset-interval before the Huffman table
|
||||
*/
|
||||
PUT_16(out->exthdr+EXTHDROFF_DRI, out->reset_interval);
|
||||
}
|
||||
}
|
||||
|
||||
extern volatile uint8_t framerate_c;
|
||||
|
||||
void clear_init_done(void *d)
|
||||
{
|
||||
_os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
struct rtp_jpeg *out = (struct rtp_jpeg *)d;
|
||||
out->init_done = 0;
|
||||
}
|
||||
|
||||
static int jpeg_process_frame( struct frame *f, void *d )
|
||||
{
|
||||
struct rtp_media *rtp = (struct rtp_media *)d;
|
||||
struct rtp_jpeg *out = (struct rtp_jpeg *)rtp->private;
|
||||
out->f = f;
|
||||
int i, blen;
|
||||
uint32_t per_ms_incr = (25 * out->ts_incr)/1000;
|
||||
//static uint32_t last_time = 0;
|
||||
//uint32_t sys_time = 0;
|
||||
out->timestamp = per_ms_incr*f->timestamp;
|
||||
|
||||
//out->scan_data = j->scan_data;
|
||||
//out->scan_data_len = j->scan_data_len;
|
||||
//out->dri_num = j->dri_num;
|
||||
//memcpy(out->dri_len, j->dri_len, sizeof(j->dri_len));
|
||||
|
||||
/* note by jornny
|
||||
* just parse once in order to reduce time of processing frame
|
||||
*/
|
||||
//uvc 每一次都要扫描
|
||||
#if USB_EN == 1
|
||||
out->init_done = 0;
|
||||
#endif
|
||||
|
||||
if(out->init_done == 0 ) {
|
||||
out->init_done =0;
|
||||
for( i = 0; i < 16; ++i ) out->quant[i] = 0;
|
||||
out->type = 0;
|
||||
out->reset_interval = 0;
|
||||
/* Kaifan:
|
||||
* exthdr[0] = 0 let the App knows the extend protocol
|
||||
* exthdr[1] = 0 extend protocal version 0
|
||||
*/
|
||||
out->hufflen = 0;
|
||||
out->exthdr[EXTHDROFF_MAGIC] = 0;
|
||||
out->exthdr[EXTHDROFF_VER] = 0;
|
||||
|
||||
} else {
|
||||
return out->init_done;
|
||||
}
|
||||
//out->dri_num = 0;
|
||||
|
||||
out->d = f->d;
|
||||
|
||||
//增加一个前置空数的地方
|
||||
for( i = 0; i < f->first_length; i += blen + 2 )
|
||||
{
|
||||
if( f->d[i] != 0xFF )
|
||||
{
|
||||
_os_printf( "Found %02X at %d, expecting FF\n", f->d[i], i );
|
||||
out->scan_data_len = 0;
|
||||
return 0;
|
||||
}
|
||||
while(f->d[i+1] == 0xFF) ++i;
|
||||
|
||||
/* SOI (FF D8) is a bare marker with no length field */
|
||||
if( f->d[i + 1] == 0xD8 ) blen = 0;
|
||||
else blen = GET_16( f->d + i + 2 );
|
||||
|
||||
switch( f->d[i + 1] )
|
||||
{
|
||||
case 0xDB: /* Quantization Table */
|
||||
if( out->init_done ) break;
|
||||
if( parse_DQT( out, f->d + i + 4, blen - 2 ) < 0 )
|
||||
{
|
||||
out->scan_data_len = 0;
|
||||
_os_printf( "jpeg_process_frame Quantization Table err!\n" );
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case 0xC0: /* Start of Frame */
|
||||
if( out->init_done ) break;
|
||||
if( parse_SOF( out, f->d + i + 4, blen - 2 ) < 0 )
|
||||
{
|
||||
out->scan_data_len = 0;
|
||||
_os_printf( "jpeg_process_frame Start of Frame err!\n" );
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case 0xC4: /* Huffman Table */
|
||||
// if( out->init_done ) break; /* only parse DHT once */ /* Kaifan: No! Maybe has 4 DHTs! */
|
||||
if( parse_DHT( out, f->d + i + 4, blen - 2 ) < 0 )
|
||||
{
|
||||
out->scan_data_len = 0;
|
||||
_os_printf( "jpeg_process_frame Huffman Table err!\n" );
|
||||
return 0;
|
||||
}
|
||||
// out->init_done = 1; /* Kaifan: Maybe has 4 DHTs! So can't done here */
|
||||
break;
|
||||
case 0xDD: /* DRI */
|
||||
if( out->init_done ) break;
|
||||
parse_DRI (out, f->d + i + 4);
|
||||
break;
|
||||
case 0xDA: /* Start of Scan */
|
||||
out->init_done = 1; /* Kaifan: Now can done here */
|
||||
out->scan_data = f->d + i + 14;
|
||||
out->scan_data_len = f->length - (out->scan_data - f->d);
|
||||
out->offset = out->scan_data-f->d;
|
||||
//_os_printf("scan_data_len:%d\t%d\n",f->length,(out->scan_data - f->d));
|
||||
|
||||
return out->init_done;
|
||||
/*out->scan_data = f->d + i + 2 + blen;
|
||||
out->scan_data_len = f->length - i - 2 - blen;
|
||||
out->timestamp += out->ts_incr;
|
||||
if(scan_DRI(out))
|
||||
return 0;
|
||||
else
|
||||
return out->init_done;*/
|
||||
}
|
||||
}
|
||||
|
||||
_os_printf( "Found no scan data!\n" );
|
||||
uint32_t xi=0;
|
||||
for(xi=0;xi<0x290;xi++)
|
||||
{
|
||||
_os_printf(" %02x",f->d[xi]);
|
||||
}
|
||||
_os_printf(" \n");
|
||||
out->scan_data_len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jpeg_get_sdp( char *dest, int len, int payload, int port, void *d )
|
||||
{
|
||||
return snprintf( dest, len, "m=video %d RTP/AVP 26\r\n", port );
|
||||
}
|
||||
|
||||
static int jpeg_get_payload( int payload, void *d )
|
||||
{
|
||||
return 26;
|
||||
}
|
||||
|
||||
static int cal_data_len(struct rtp_jpeg *out, int *dri_index, int *res_len, int max_size)
|
||||
{
|
||||
/* not by jorrny
|
||||
* At the beginning, we try to send the packet aligned to dri.
|
||||
* But we found it inefficient when the dri length is almost a half of the max_size
|
||||
* So we decide to send the packet with the max_size always to improve efficiency.
|
||||
* In the same way, we try to send the packet within max_size in order to prevent fragment in ip layer.
|
||||
*/
|
||||
|
||||
int len = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if(*res_len) {
|
||||
len = *res_len;
|
||||
} else {
|
||||
len += out->dri_len[*dri_index];
|
||||
}
|
||||
*res_len = 0;
|
||||
if(len > max_size) {
|
||||
*res_len = len - max_size;
|
||||
len = max_size;
|
||||
break;
|
||||
} else if (len == max_size) {
|
||||
(*dri_index)++;
|
||||
break;
|
||||
}
|
||||
if(++(*dri_index) > out->dri_num)
|
||||
break;
|
||||
} while(1);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
//版本已经移除
|
||||
static int jpeg_send( struct rtp_endpoint *ep, void *d )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jpeg_send_frame_to_endpoint( struct rtp_endpoint *ep, struct rtp_jpeg *out, struct framebuff *fb )
|
||||
{
|
||||
uint8_t *jpeg_buf_addr = (uint8_t *)fb->data;
|
||||
int i = 0, plen, vcnt, hdr_len;
|
||||
uint32_t node_offset = out->offset;
|
||||
struct iovec v[8];
|
||||
uint8_t vhdr[12], qhdr[4];
|
||||
int res_len = 1, max_data_size;
|
||||
uint32_t node_len = out->f->node_len;
|
||||
|
||||
if( !ep )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if( !ep->sendEnable )
|
||||
{
|
||||
ep->sendEnable = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
vhdr[0] = 0;
|
||||
vhdr[4] = out->type;
|
||||
vhdr[5] = 255;
|
||||
vhdr[6] = out->width;
|
||||
vhdr[7] = out->height;
|
||||
v[1].iov_base = vhdr;
|
||||
v[1].iov_len = ( out->type & 0x40 ) ? 12 : 8;
|
||||
|
||||
qhdr[0] = 0;
|
||||
qhdr[1] = 0;
|
||||
#ifdef USE_EXTHDR
|
||||
PUT_16( qhdr + 2, 2 * 64 + out->hufflen + EXTHDR_LEN );
|
||||
#else
|
||||
PUT_16( qhdr + 2, 2 * 64 );
|
||||
#endif
|
||||
v[2].iov_base = qhdr;
|
||||
v[2].iov_len = 4;
|
||||
v[3].iov_base = jpeg_buf_addr + out->quant[out->luma_table];
|
||||
v[3].iov_len = 64;
|
||||
v[4].iov_base = jpeg_buf_addr + out->quant[out->chroma_table];
|
||||
v[4].iov_len = 64;
|
||||
hdr_len = 132 + v[1].iov_len;
|
||||
#ifdef USE_EXTHDR
|
||||
vcnt = 7;
|
||||
v[5].iov_base = out->exthdr;
|
||||
v[5].iov_len = EXTHDR_LEN;
|
||||
/* The DHT block stays inside the current JPEG frame, so it can be sent directly. */
|
||||
v[6].iov_base = jpeg_buf_addr + out->huffoff;
|
||||
v[6].iov_len = out->hufflen;
|
||||
hdr_len += v[5].iov_len + v[6].iov_len;
|
||||
#else
|
||||
vcnt = 5;
|
||||
#endif
|
||||
|
||||
while( i < out->scan_data_len )
|
||||
{
|
||||
if( node_offset >= node_len )
|
||||
{
|
||||
node_offset = 0;
|
||||
}
|
||||
|
||||
max_data_size = rtp_get_payload_size_limit( ep, RTP_HEADER_SIZE + hdr_len );
|
||||
if( max_data_size <= 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
break;
|
||||
}
|
||||
if( out->type & 0x40 )
|
||||
{
|
||||
if( ( out->scan_data_len - i ) > max_data_size )
|
||||
{
|
||||
plen = max_data_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
plen = out->scan_data_len - i;
|
||||
}
|
||||
|
||||
if( res_len )
|
||||
{
|
||||
vhdr[8] = out->exthdr[EXTHDROFF_DRI];
|
||||
vhdr[9] = out->exthdr[EXTHDROFF_DRI + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
PUT_16( vhdr + 8, 0x28 );
|
||||
}
|
||||
PUT_16( vhdr + 10, 0xffff );
|
||||
}
|
||||
else
|
||||
{
|
||||
plen = out->scan_data_len - i;
|
||||
if( plen > max_data_size )
|
||||
{
|
||||
plen = max_data_size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vhdr[1] = i >> 16;
|
||||
vhdr[2] = ( i >> 8 ) & 0xff;
|
||||
vhdr[3] = i & 0xff;
|
||||
v[vcnt].iov_base = jpeg_buf_addr + node_offset;
|
||||
|
||||
if( node_offset + plen >= node_len )
|
||||
{
|
||||
plen = node_len - node_offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t align_32 = ( node_offset + plen ) & ( ~0x1F );
|
||||
plen = align_32 - node_offset;
|
||||
}
|
||||
|
||||
if( i + plen >= out->scan_data_len ) plen = out->scan_data_len - i;
|
||||
node_offset += plen;
|
||||
v[vcnt].iov_len = plen;
|
||||
if( rtp_sendmsg( ep, v, vcnt + 1, out->timestamp, plen + i == out->scan_data_len, 30 ) < 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
vcnt = 2;
|
||||
hdr_len = v[1].iov_len;
|
||||
i += plen;
|
||||
}
|
||||
|
||||
ep->sendEnable = 1;
|
||||
}
|
||||
|
||||
static int jpeg_send_more( rtp_loop_search_ep search,void *ls,void *track, void *d )
|
||||
{
|
||||
struct rtp_jpeg *out = (struct rtp_jpeg *)d;
|
||||
struct framebuff *fb = (struct framebuff *)out->f->get_f;
|
||||
struct rtp_endpoint *ep;
|
||||
void *head;
|
||||
|
||||
if( !fb )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
out->scan_data_len = out->f->length - out->offset;
|
||||
|
||||
head = ls;
|
||||
while( head )
|
||||
{
|
||||
head = search( head, track, (void **)&ep );
|
||||
if( ep )
|
||||
{
|
||||
jpeg_send_frame_to_endpoint( ep, out, fb );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct rtp_media *new_rtp_media_jpeg_stream( struct stream *stream )
|
||||
{
|
||||
struct rtp_jpeg *out;
|
||||
int fincr, fbase;
|
||||
struct rtp_media *m;
|
||||
|
||||
stream->get_framerate( stream, &fincr, &fbase );
|
||||
out = (struct rtp_jpeg *)malloc( sizeof( struct rtp_jpeg ) );
|
||||
out->init_done = 0;
|
||||
out->timestamp = 0;
|
||||
out->scan_data = NULL;
|
||||
out->scan_data_len = 0;
|
||||
out->ts_incr = 90000 * fincr / fbase;
|
||||
m = new_rtp_rtcp_media( jpeg_get_sdp, jpeg_get_payload, jpeg_process_frame, jpeg_send, new_rtcp_send, out );
|
||||
if(m)
|
||||
{
|
||||
m->type = 0;
|
||||
m->send_more = jpeg_send_more;
|
||||
m->per_ms_incr = (25 * out->ts_incr)/1000;
|
||||
}
|
||||
return m;
|
||||
|
||||
}
|
||||
620
sdk/app/spook/rtp.c
Normal file
620
sdk/app/spook/rtp.c
Normal file
@@ -0,0 +1,620 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
//#include "lwip\fcntl.h"
|
||||
#include "lwip\api.h"
|
||||
#include "lwip\tcp.h"
|
||||
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <rtp.h>
|
||||
#include "spook_config.h"
|
||||
//#include <linux/linux_mutex.h>
|
||||
#include "lwip/api.h"
|
||||
#include <csi_kernel.h>
|
||||
#include "rtp_media.h"
|
||||
#include "lib/net/eloop/eloop.h"
|
||||
|
||||
|
||||
int rtcp_send( struct rtp_endpoint *ep, uint8_t *pbuf,uint16 len);
|
||||
|
||||
static int rtp_port_start = 50000, rtp_port_end = 60000;
|
||||
k_task_handle_t spook_rtcp_handle = NULL;
|
||||
|
||||
/*************rtp command to APP***********/
|
||||
struct rtp_endpoint *new_rtp_endpoint( int payload )
|
||||
{
|
||||
struct rtp_endpoint *ep;
|
||||
|
||||
if( ! ( ep = (struct rtp_endpoint *)
|
||||
malloc( sizeof( struct rtp_endpoint ) ) ) )
|
||||
return NULL;
|
||||
ep->payload = payload;
|
||||
ep->max_data_size = MAX_DATA_PACKET_SIZE; /* default maximum */
|
||||
ep->ssrc = 0;
|
||||
random_bytes( (unsigned char *)&ep->ssrc, 4 );
|
||||
ep->start_timestamp = 0;
|
||||
random_bytes( (unsigned char *)&ep->start_timestamp, 4 );
|
||||
ep->start_timestamp &= 0xFFFF;
|
||||
ep->last_timestamp = ep->start_timestamp;
|
||||
ep->seqnum = 0;
|
||||
random_bytes( (unsigned char *)&ep->seqnum, 2 );
|
||||
ep->packet_count = 0;
|
||||
ep->octet_count = 0;
|
||||
gettimeofday( &ep->last_rtcp_recv, NULL );
|
||||
ep->trans_type = 0;
|
||||
ep->rtcp_send_timestamp = os_jiffies();
|
||||
ep->sendEnable = 1;
|
||||
return ep;
|
||||
}
|
||||
|
||||
void close_rtp_socket( void *ei, void *d )
|
||||
{
|
||||
struct event* e = (struct event*)ei;
|
||||
int fd = (int)d;
|
||||
close( fd);
|
||||
_os_printf("%s fd:%d\n",__FUNCTION__,fd);
|
||||
e->flags |= EVENT_F_REMOVE;
|
||||
}
|
||||
|
||||
//socket的关闭会通过eloop内部去关闭,这样就不存在有异步操作的问题了(移除对应事件和关闭socket)
|
||||
void del_rtp_endpoint( struct rtp_endpoint *ep )
|
||||
{
|
||||
switch( ep->trans_type )
|
||||
{
|
||||
case RTP_TRANS_UDP:
|
||||
|
||||
eloop_remove_event( ep->trans.udp.rtp_event );
|
||||
//创建socket关闭的事件
|
||||
eloop_add_alarm(os_jiffies(),EVENT_F_ENABLED,close_rtp_socket,(void*)ep->trans.udp.rtp_fd);
|
||||
eloop_remove_event( ep->trans.udp.rtcp_event );
|
||||
eloop_add_alarm(os_jiffies(),EVENT_F_ENABLED,close_rtp_socket,(void*)ep->trans.udp.rtcp_fd);
|
||||
break;
|
||||
case RTP_TRANS_INTER:
|
||||
interleave_disconnect( ep->trans.inter.conn,
|
||||
ep->trans.inter.rtp_chan );
|
||||
interleave_disconnect( ep->trans.inter.conn,
|
||||
ep->trans.inter.rtcp_chan );
|
||||
break;
|
||||
}
|
||||
|
||||
free( ep );
|
||||
}
|
||||
|
||||
static void udp_rtp_read( void *ei, void *d )
|
||||
{
|
||||
struct rtp_endpoint *ep = (struct rtp_endpoint *)d;
|
||||
unsigned char buf[1024];
|
||||
int ret;
|
||||
ret = read( ep->trans.udp.rtp_fd, buf, sizeof( buf ) );
|
||||
if( ret > 0 )
|
||||
{
|
||||
/* some SIP phones don't send RTCP */
|
||||
gettimeofday( &ep->last_rtcp_recv, NULL );
|
||||
return;
|
||||
} else if( ret < 0 )
|
||||
spook_log( SL_VERBOSE, "error on UDP RTP socket: %s",
|
||||
strerror( get_errno() ) );
|
||||
else spook_log( SL_VERBOSE, "UDP RTP socket closed" );
|
||||
_os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
ep->session->select_close(ep->session, ep);
|
||||
}
|
||||
|
||||
static void udp_rtcp_read( void *ei, void *d )
|
||||
{
|
||||
#if JPG_EN
|
||||
static uint8_t rtcp_speed_count = 0x80;
|
||||
#endif
|
||||
struct rtp_endpoint *ep = (struct rtp_endpoint *)d;
|
||||
unsigned char buf[1024];
|
||||
int ret;
|
||||
ret = read( ep->trans.udp.rtcp_fd, buf, sizeof( buf ) );
|
||||
if( ret > 0 )
|
||||
{
|
||||
#if JPG_EN
|
||||
//Wifi_data_receive(buf,ret);
|
||||
//_os_printf("(%d)rtcp get:%d ===== %x%x\r\n",os_jiffies(),buf[12],buf[0],buf[1]);
|
||||
if((buf[0] == 0x81)&&(buf[1] == 0xc9)){
|
||||
if(buf[12] != 0){
|
||||
rtcp_speed_count++;
|
||||
if(rtcp_speed_count >= 0x83){
|
||||
rtcp_speed_count = 0x82;
|
||||
}
|
||||
}
|
||||
|
||||
if(buf[12] == 0){
|
||||
rtcp_speed_count--;
|
||||
if(rtcp_speed_count <= 0x7d){
|
||||
rtcp_speed_count = 0x7e;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
gettimeofday( &ep->last_rtcp_recv, NULL );
|
||||
return;
|
||||
} else if( ret < 0 )
|
||||
spook_log( SL_VERBOSE, "error on UDP RTCP socket: %s",
|
||||
strerror( get_errno() ) );
|
||||
else spook_log( SL_VERBOSE, "UDP RTCP socket closed" );
|
||||
_os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
ep->session->select_close(ep->session, ep);
|
||||
}
|
||||
|
||||
void interleave_recv_rtcp( struct rtp_endpoint *ep, unsigned char *d, int len )
|
||||
{
|
||||
spook_log( SL_DEBUG, "received RTCP packet from client INTERLEAVE" );
|
||||
gettimeofday( &ep->last_rtcp_recv, NULL );
|
||||
}
|
||||
|
||||
int g_timeout = 0;
|
||||
|
||||
#define RECORDER_RTCP_NTP 0 // 使用录风者时可以开启,降低延时
|
||||
|
||||
int new_rtcp_send( struct rtp_endpoint *ep, void *d )/*RFC3550*/
|
||||
{
|
||||
// struct rtp_media *rtp = (struct rtp_media*)d;
|
||||
if(ep->trans_type == RTP_TRANS_UDP)
|
||||
{
|
||||
int res;
|
||||
unsigned char buf[64];
|
||||
unsigned int ntp_sec, ntp_usec;
|
||||
unsigned int rtp_timestamp;
|
||||
uint16 len_send;
|
||||
uint16 len = 6; //字符串长度,"taixin"
|
||||
|
||||
if(ep == NULL || ep->session == NULL){
|
||||
_os_printf("send rtcp no session!!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(os_jiffies()-ep->rtcp_send_timestamp < 5000)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
ep->rtcp_send_timestamp = os_jiffies();
|
||||
rtp_timestamp = ep->last_timestamp;
|
||||
#if RECORDER_RTCP_NTP
|
||||
ntp_sec = 0;
|
||||
ntp_usec = 0;
|
||||
#else
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
ntp_sec = now.tv_sec + 0x83AA7E80;
|
||||
ntp_usec = (double)( (double)now.tv_usec * (double)0x4000000 ) / 15625.0;
|
||||
#endif
|
||||
|
||||
buf[0] = 2 << 6; // version
|
||||
buf[1] = 200; // packet type is Sender Report
|
||||
PUT_16( buf + 2, 6 ); // length in words minus one
|
||||
PUT_32( buf + 4, ep->ssrc );
|
||||
PUT_32( buf + 8, ntp_sec );
|
||||
PUT_32( buf + 12, ntp_usec );
|
||||
PUT_32( buf + 16, rtp_timestamp);//ep->rtcp_send_timestamp*per_ms_incr+ep->start_timestamp );
|
||||
PUT_32( buf + 20, ep->packet_count );
|
||||
PUT_32( buf + 24, ep->octet_count );
|
||||
buf[28] = ( 2 << 6 ) | 1; // version; source count = 1
|
||||
buf[29] = 202; // packet type is Source Description SDES
|
||||
PUT_16( buf + 30, (4+4+2+len)/4-1 ); // length in words minus one sr = 5word 5-1 = 4 !!!!!!!
|
||||
PUT_32( buf + 32, ep->ssrc );
|
||||
buf[36] = 0x01; // field type is CNAME 36/4=9 52/4 = 13
|
||||
buf[37] = len; // text length
|
||||
memcpy( buf + 38, "taixin", len );
|
||||
len_send = (32+4+2+len);
|
||||
|
||||
res = send( ep->trans.udp.rtcp_fd, buf, len_send, 0 );
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int connect_udp_endpoint( struct rtp_endpoint *ep,
|
||||
struct in_addr dest_ip, int dest_port, int *our_port )
|
||||
{
|
||||
struct sockaddr_in rtpaddr, rtcpaddr;
|
||||
int port, success = 0, max_tries, rtpfd = -1, rtcpfd = -1;
|
||||
unsigned int i;
|
||||
|
||||
rtpaddr.sin_family = rtcpaddr.sin_family = AF_INET;
|
||||
rtpaddr.sin_addr.s_addr = rtcpaddr.sin_addr.s_addr = 0;
|
||||
|
||||
port = rtp_port_start + rand() % ( rtp_port_end - rtp_port_start );
|
||||
if( port & 0x1 ) ++port;
|
||||
max_tries = ( rtp_port_end - rtp_port_start + 1 ) / 2;
|
||||
|
||||
for( i = 0; i < max_tries; ++i )
|
||||
{
|
||||
if( port + 1 > rtp_port_end ) port = rtp_port_start;
|
||||
rtpaddr.sin_port = htons( port );
|
||||
rtcpaddr.sin_port = htons( port + 1 );
|
||||
if( rtpfd < 0 &&
|
||||
( rtpfd = socket( PF_INET, SOCK_DGRAM, 0 ) ) < 0 )
|
||||
{
|
||||
spook_log( SL_WARN, "unable to create UDP RTP socket: %s",
|
||||
strerror( get_errno() ) );
|
||||
return -1;
|
||||
}
|
||||
if( rtcpfd < 0 &&
|
||||
( rtcpfd = socket( PF_INET, SOCK_DGRAM, 0 ) ) < 0 )
|
||||
{
|
||||
spook_log( SL_WARN, "unable to create UDP RTCP socket: %s",
|
||||
strerror( get_errno() ) );
|
||||
close( rtpfd );
|
||||
return -1;
|
||||
}
|
||||
if( bind( rtpfd, (struct sockaddr *)&rtpaddr,
|
||||
sizeof( rtpaddr ) ) < 0 )
|
||||
{
|
||||
if( get_errno() == EADDRINUSE )
|
||||
{
|
||||
port += 2;
|
||||
continue;
|
||||
}
|
||||
spook_log( SL_WARN, "strange error when binding RTP socket: %s",
|
||||
strerror( get_errno() ) );
|
||||
close( rtpfd );
|
||||
close( rtcpfd );
|
||||
return -1;
|
||||
}
|
||||
if( bind( rtcpfd, (struct sockaddr *)&rtcpaddr,
|
||||
sizeof( rtcpaddr ) ) < 0 )
|
||||
{
|
||||
if( get_errno() == EADDRINUSE )
|
||||
{
|
||||
close( rtpfd );
|
||||
rtpfd = -1;
|
||||
port += 2;
|
||||
continue;
|
||||
}
|
||||
spook_log( SL_WARN, "strange error when binding RTCP socket: %s",
|
||||
strerror( get_errno() ) );
|
||||
close( rtpfd );
|
||||
close( rtcpfd );
|
||||
return -1;
|
||||
}
|
||||
success = 1;
|
||||
break;
|
||||
}
|
||||
if( ! success )
|
||||
{
|
||||
spook_log( SL_WARN, "ran out of UDP RTP ports!" );
|
||||
return -1;
|
||||
}
|
||||
rtpaddr.sin_family = rtcpaddr.sin_family = AF_INET;
|
||||
rtpaddr.sin_addr = rtcpaddr.sin_addr = dest_ip;
|
||||
rtpaddr.sin_port = htons( dest_port );
|
||||
rtcpaddr.sin_port = htons( dest_port + 1 );
|
||||
if( connect( rtpfd, (struct sockaddr *)&rtpaddr,
|
||||
sizeof( rtpaddr ) ) < 0 )
|
||||
{
|
||||
spook_log( SL_WARN, "strange error when connecting RTP socket: %s",
|
||||
strerror( get_errno() ) );
|
||||
close( rtpfd );
|
||||
close( rtcpfd );
|
||||
return -1;
|
||||
}
|
||||
if( connect( rtcpfd, (struct sockaddr *)&rtcpaddr,
|
||||
sizeof( rtcpaddr ) ) < 0 )
|
||||
{
|
||||
spook_log( SL_WARN, "strange error when connecting RTCP socket: %s",
|
||||
strerror( get_errno() ) );
|
||||
close( rtpfd );
|
||||
close( rtcpfd );
|
||||
return -1;
|
||||
}
|
||||
i = sizeof( rtpaddr );
|
||||
if( getsockname( rtpfd, (struct sockaddr *)&rtpaddr, &i ) < 0 )
|
||||
{
|
||||
spook_log( SL_WARN, "strange error from getsockname: %s",
|
||||
strerror( get_errno() ) );
|
||||
close( rtpfd );
|
||||
close( rtcpfd );
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// int tos = 0xe0;
|
||||
// setsockopt(rtpfd, IPPROTO_IP, IP_TOS, (void *)&tos , sizeof(tos));
|
||||
|
||||
// int tos = 0xb8;
|
||||
// setsockopt(rtpfd, IPPROTO_IP, IP_TOS, (void *)&tos , sizeof(tos));
|
||||
|
||||
ep->max_data_size = MAX_DATA_PACKET_SIZE; /* good guess for preventing fragmentation */
|
||||
ep->trans_type = RTP_TRANS_UDP;
|
||||
sprintf( ep->trans.udp.sdp_addr, "IP4 %s",
|
||||
inet_ntoa( rtpaddr.sin_addr ) );
|
||||
ep->trans.udp.sdp_port = ntohs( rtpaddr.sin_port );
|
||||
ep->trans.udp.rtp_fd = rtpfd;
|
||||
ep->trans.udp.rtcp_fd = rtcpfd;
|
||||
ep->trans.udp.rtp_event = eloop_add_fd( rtpfd, EVENT_READ, EVENT_F_ENABLED, udp_rtp_read, ep );
|
||||
ep->trans.udp.rtcp_event = eloop_add_fd( rtcpfd, EVENT_READ, EVENT_F_ENABLED, udp_rtcp_read, ep );
|
||||
spook_log( SL_DEBUG, "connect_udp_endpoint add_fd_event udp_rtp_read udp_rtcp_read");
|
||||
*our_port = port;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void connect_interleaved_endpoint( struct rtp_endpoint *ep,
|
||||
struct conn *conn, int rtp_chan, int rtcp_chan )
|
||||
{
|
||||
ep->trans_type = RTP_TRANS_INTER;
|
||||
ep->trans.inter.conn = conn;
|
||||
ep->trans.inter.rtp_chan = rtp_chan;
|
||||
ep->trans.inter.rtcp_chan = rtcp_chan;
|
||||
ep->trans.inter.running = 1;
|
||||
}
|
||||
|
||||
int rtp_get_packet_size_limit( const struct rtp_endpoint *ep )
|
||||
{
|
||||
if( ep && ep->trans_type == RTP_TRANS_INTER )
|
||||
{
|
||||
return RTP_TCP_MAX_PACKET_SIZE;
|
||||
}
|
||||
|
||||
if( ep && ep->max_data_size > 0 )
|
||||
{
|
||||
return ep->max_data_size;
|
||||
}
|
||||
|
||||
return MAX_DATA_PACKET_SIZE;
|
||||
}
|
||||
|
||||
int rtp_get_payload_size_limit( const struct rtp_endpoint *ep, int packet_overhead )
|
||||
{
|
||||
int packet_limit = rtp_get_packet_size_limit( ep );
|
||||
|
||||
if( packet_overhead < 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if( packet_limit <= packet_overhead )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return packet_limit - packet_overhead;
|
||||
}
|
||||
|
||||
static void rtp_build_header( struct rtp_endpoint *ep, uint32_t timestamp, uint8_t marker, uint8_t *rtphdr )
|
||||
{
|
||||
ep->last_timestamp = ( ep->start_timestamp + timestamp ) & 0xFFFFFFFF;
|
||||
|
||||
rtphdr[0] = 2 << 6;
|
||||
rtphdr[1] = ep->payload;
|
||||
if( marker )
|
||||
{
|
||||
rtphdr[1] |= 0x80;
|
||||
}
|
||||
|
||||
PUT_16( rtphdr + 2, ep->seqnum );
|
||||
PUT_32( rtphdr + 4, ep->last_timestamp );
|
||||
PUT_32( rtphdr + 8, ep->ssrc );
|
||||
}
|
||||
|
||||
static uint32_t rtp_get_payload_len( struct iovec *v, int count )
|
||||
{
|
||||
uint32_t payload_len = 0;
|
||||
|
||||
for( int i = 1; i < count; ++i )
|
||||
{
|
||||
payload_len += v[i].iov_len;
|
||||
}
|
||||
|
||||
return payload_len;
|
||||
}
|
||||
|
||||
static void rtp_advance_iov( struct iovec **iov, int *count, int bytes )
|
||||
{
|
||||
while( bytes > 0 && *count > 0 )
|
||||
{
|
||||
if( bytes >= (int)(*iov)[0].iov_len )
|
||||
{
|
||||
bytes -= (*iov)[0].iov_len;
|
||||
++(*iov);
|
||||
--(*count);
|
||||
}
|
||||
else
|
||||
{
|
||||
(*iov)[0].iov_base = (unsigned char *)(*iov)[0].iov_base + bytes;
|
||||
(*iov)[0].iov_len -= bytes;
|
||||
bytes = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int rtp_send_udp( int fd, struct iovec *iov, int count, int retries )
|
||||
{
|
||||
struct msghdr mh;
|
||||
int retry_count = 0;
|
||||
int total_len = 0;
|
||||
int ret;
|
||||
|
||||
for( int i = 0; i < count; ++i )
|
||||
{
|
||||
total_len += iov[i].iov_len;
|
||||
}
|
||||
|
||||
while( retry_count <= retries )
|
||||
{
|
||||
memset( &mh, 0, sizeof( mh ) );
|
||||
mh.msg_iov = iov;
|
||||
mh.msg_iovlen = count;
|
||||
ret = sendmsg( fd, &mh, MSG_DONTWAIT );
|
||||
if( ret == total_len )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if( get_errno() == ENOMEM )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if( ret >= 0 || get_errno() != EAGAIN )
|
||||
{
|
||||
_os_printf( "%s %d, err: %d\n", __FUNCTION__, __LINE__, get_errno() );
|
||||
return -1;
|
||||
}
|
||||
++retry_count;
|
||||
os_sleep_ms( 2 );
|
||||
}
|
||||
|
||||
_os_printf( "%s %d, err: %d\n", __FUNCTION__, __LINE__, get_errno() );
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int rtp_send_tcp( struct rtp_endpoint *ep, struct iovec *v, int count)
|
||||
{
|
||||
struct conn *conn = ep->trans.inter.conn;
|
||||
struct iovec local_iov[17];
|
||||
struct iovec *send_iov = local_iov;
|
||||
uint8_t interleave_hdr[4];
|
||||
struct msghdr mh;
|
||||
int payload_len = 0;
|
||||
int send_count;
|
||||
uint32_t start_time = os_jiffies();
|
||||
int ret;
|
||||
|
||||
if( !conn || conn->fd <= 0 || !conn->running )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( count + 1 > (int)( sizeof( local_iov ) / sizeof( local_iov[0] ) ) )
|
||||
{
|
||||
_os_printf( "too many interleaved iov entries: %d", count + 1 );
|
||||
return -1;
|
||||
}
|
||||
|
||||
for( int i = 0; i < count; ++i ) payload_len += v[i].iov_len;
|
||||
if( payload_len > RTP_TCP_MAX_PACKET_SIZE )
|
||||
{
|
||||
_os_printf( "interleaved RTP packet too large: %d", payload_len );
|
||||
return -1;
|
||||
}
|
||||
interleave_hdr[0] = '$';
|
||||
interleave_hdr[1] = ep->trans.inter.rtp_chan;
|
||||
PUT_16( interleave_hdr + 2, payload_len );
|
||||
|
||||
local_iov[0].iov_base = interleave_hdr;
|
||||
local_iov[0].iov_len = sizeof( interleave_hdr );
|
||||
memcpy( &local_iov[1], v, sizeof( v[0] ) * count );
|
||||
send_count = count + 1;
|
||||
|
||||
os_event_wait( &conn->evt, RTSP_TCP_WRITE_MUTEX, NULL, OS_EVENT_WMODE_AND | OS_EVENT_WMODE_CLEAR, -1 );
|
||||
|
||||
while( send_count > 0 )
|
||||
{
|
||||
memset( &mh, 0, sizeof( mh ) );
|
||||
mh.msg_iov = send_iov;
|
||||
mh.msg_iovlen = send_count;
|
||||
os_event_wait( &conn->evt, RTSP_TCP_READ_MUTEX, NULL, OS_EVENT_WMODE_AND | OS_EVENT_WMODE_CLEAR, -1 );
|
||||
ret = sendmsg( conn->fd, &mh, MSG_DONTWAIT );
|
||||
os_event_set( &conn->evt, RTSP_TCP_READ_MUTEX, NULL );
|
||||
|
||||
if( ret > 0 )
|
||||
{
|
||||
rtp_advance_iov( &send_iov, &send_count, ret );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( ret == 0 || get_errno() != EAGAIN )
|
||||
{
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if( os_jiffies() - start_time >= 4000 )
|
||||
{
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
os_sleep_ms( 1 );
|
||||
}
|
||||
|
||||
os_event_set( &conn->evt, RTSP_TCP_WRITE_MUTEX, NULL );
|
||||
if( send_count <= 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if( ret < 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
if( ep->session && ep->session->closed )
|
||||
{
|
||||
ep->session->closed( ep->session, ep );
|
||||
}
|
||||
_os_printf( "%s %d, err: %d\n", __FUNCTION__, __LINE__, get_errno() );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rtp_sendmsg( struct rtp_endpoint *ep, struct iovec *v, int count, uint32_t timestamp, uint8_t marker, int retries )
|
||||
{
|
||||
uint8_t rtphdr[12];
|
||||
uint32_t payload_len;
|
||||
int ret = -1;
|
||||
|
||||
if( !ep || !v || count <= 0 )
|
||||
{
|
||||
_os_printf("%s %d\n", __FUNCTION__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( !ep->sendEnable )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
rtp_build_header( ep, timestamp, marker, rtphdr );
|
||||
v[0].iov_base = rtphdr;
|
||||
v[0].iov_len = sizeof( rtphdr );
|
||||
payload_len = rtp_get_payload_len( v, count );
|
||||
|
||||
switch( ep->trans_type )
|
||||
{
|
||||
case RTP_TRANS_UDP:
|
||||
ret = rtp_send_udp( ep->trans.udp.rtp_fd, v, count, retries > 0 ? retries : 10 );
|
||||
if( ret < 0 )
|
||||
{
|
||||
ep->sendEnable = 0;
|
||||
}
|
||||
break;
|
||||
case RTP_TRANS_INTER:
|
||||
ret = rtp_send_tcp( ep, v, count );
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if( ret < 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ep->octet_count += payload_len;
|
||||
++ep->packet_count;
|
||||
ep->seqnum = ( ep->seqnum + 1 ) & 0xFFFF;
|
||||
return 0;
|
||||
}
|
||||
206
sdk/app/spook/rtp.h
Normal file
206
sdk/app/spook/rtp.h
Normal file
@@ -0,0 +1,206 @@
|
||||
#ifndef __RTP_H
|
||||
#define __RTP_H
|
||||
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* These should probably be an enum */
|
||||
#define RTP_TRANS_UDP 1
|
||||
#define RTP_TRANS_INTER 2
|
||||
|
||||
#define MAX_INTERLEAVE_CHANNELS 8
|
||||
#define RTP_HEADER_SIZE 12
|
||||
#define RTP_TCP_MAX_PACKET_SIZE (16*1024)
|
||||
|
||||
struct rtp_endpoint;
|
||||
struct conn;
|
||||
struct session;
|
||||
|
||||
struct rtp_endpoint {
|
||||
struct session *session;
|
||||
int payload;
|
||||
int max_data_size;
|
||||
unsigned int ssrc;
|
||||
unsigned int start_timestamp;
|
||||
unsigned int last_timestamp;
|
||||
unsigned int rtcp_send_timestamp;
|
||||
int seqnum;
|
||||
int packet_count;
|
||||
int octet_count;
|
||||
struct event *rtcp_send_event;
|
||||
struct timeval last_rtcp_recv;
|
||||
int trans_type;
|
||||
int sendEnable;
|
||||
union {
|
||||
struct {
|
||||
char sdp_addr[48];
|
||||
int sdp_port;
|
||||
int rtp_fd;
|
||||
struct event *rtp_event;
|
||||
int rtcp_fd;
|
||||
struct event *rtcp_event;
|
||||
} udp;
|
||||
struct {
|
||||
struct conn *conn;
|
||||
int rtp_chan;
|
||||
int rtcp_chan;
|
||||
int running;
|
||||
} inter;
|
||||
} trans;
|
||||
};
|
||||
|
||||
typedef int (*get_sdp_func)( struct session *s, char *dest, int *len,
|
||||
char *path );
|
||||
typedef int (*setup_func)( struct session *s, int track );
|
||||
typedef void (*play_func)( struct session *s, double *start );
|
||||
typedef void (*pause_func)( struct session *s );
|
||||
typedef void (*teardown_func)( struct session *s, struct rtp_endpoint *ep );
|
||||
typedef void (*session_close_func)( struct session *sess );
|
||||
typedef void (*close_func)( struct session *s, struct rtp_endpoint *ep );
|
||||
typedef void (*select_func)( struct session *s, struct rtp_endpoint *ep );
|
||||
|
||||
#define MAX_TRACKS 4
|
||||
|
||||
struct session {
|
||||
struct session *next;
|
||||
struct session *prev;
|
||||
get_sdp_func get_sdp;
|
||||
setup_func setup;
|
||||
play_func play;
|
||||
pause_func pause;
|
||||
close_func closed;
|
||||
teardown_func teardown;
|
||||
select_func select_close;
|
||||
void *private;
|
||||
session_close_func control_close;
|
||||
void *control_private;
|
||||
struct timeval open_time;
|
||||
char addr[64];
|
||||
struct rtp_endpoint *ep[MAX_TRACKS];
|
||||
struct conn *conn; /* add by jornny */
|
||||
int connect_type;
|
||||
};
|
||||
|
||||
struct loc_node {
|
||||
struct loc_node *next;
|
||||
struct loc_node *prev;
|
||||
char path[16];
|
||||
};
|
||||
|
||||
typedef struct session *(*open_func)( char *path, void *d );
|
||||
|
||||
struct req {
|
||||
struct conn *conn;
|
||||
struct pmsg *req;
|
||||
struct pmsg *resp;
|
||||
};
|
||||
|
||||
struct jpeg_fifo
|
||||
{
|
||||
unsigned char *buf;
|
||||
int len;
|
||||
unsigned char *scan_data;
|
||||
int scan_data_len;
|
||||
unsigned short int dri_len[100];
|
||||
|
||||
int dri_num;
|
||||
unsigned int items;
|
||||
//struct list_head list;
|
||||
//boolean *pIsUsing;
|
||||
};
|
||||
|
||||
|
||||
#define CONN_PROTO_START 0
|
||||
#define CONN_PROTO_RTSP 1
|
||||
#define CONN_PROTO_HTTP 2
|
||||
#define CONN_PROTO_SIP 3
|
||||
#define CONN_PROTO_APPO 4
|
||||
#include "osal/event.h"
|
||||
enum
|
||||
{
|
||||
RTSP_TCP_SEND_FINISH = BIT(0), // done的时候报错
|
||||
RTSP_TCP_READ_MUTEX = BIT(1),
|
||||
RTSP_TCP_WRITE_MUTEX = BIT(2),
|
||||
};
|
||||
struct conn {
|
||||
struct conn *next;
|
||||
struct conn *prev;
|
||||
int fd;
|
||||
int running;
|
||||
uint8_t *sendbuf;
|
||||
int send_buf_len;
|
||||
struct os_event evt;
|
||||
struct sockaddr_in client_addr;
|
||||
struct event *read_event;
|
||||
struct event *write_event;
|
||||
int proto;
|
||||
unsigned char req_buf[512];
|
||||
int req_len;
|
||||
struct req *req_list;
|
||||
unsigned char send_buf[512];
|
||||
int send_buf_r;
|
||||
int send_buf_w;
|
||||
int drop_after;
|
||||
void *proto_state;
|
||||
|
||||
/* add by jornny*/
|
||||
struct session *sess;
|
||||
|
||||
/* Client Hardware Addr of this Conn*/
|
||||
//unsigned char chaddr[6];
|
||||
};
|
||||
|
||||
/* There's probably a better place to put these... */
|
||||
#define PUT_16(p,v) ((p)[0]=((v)>>8)&0xff,(p)[1]=(v)&0xff)
|
||||
#define PUT_32(p,v) ((p)[0]=((v)>>24)&0xff,(p)[1]=((v)>>16)&0xff,(p)[2]=((v)>>8)&0xff,(p)[3]=(v)&0xff)
|
||||
#define GET_16(p) (((p)[0]<<8)|(p)[1])
|
||||
#define GET_32(p) (((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|(p)[3])
|
||||
|
||||
void random_bytes( unsigned char *dest, int len );
|
||||
void random_id( unsigned char *dest, int len );
|
||||
struct rtp_endpoint *new_rtp_endpoint( int payload );
|
||||
int connect_udp_endpoint( struct rtp_endpoint *ep,
|
||||
struct in_addr dest_ip, int dest_port, int *our_port );
|
||||
int avail_send_buf( struct conn *c );
|
||||
int send_data( struct conn *c, unsigned char *d, int len );
|
||||
int tcp_send_pmsg( struct conn *c, struct pmsg *msg, int len );
|
||||
void connect_interleaved_endpoint( struct rtp_endpoint *ep,
|
||||
struct conn *conn, int rtp_chan, int rtcp_chan );
|
||||
void rtsp_conn_disconnect( struct conn *c );
|
||||
void http_conn_disconnect( struct conn *c );
|
||||
void interleave_disconnect( struct conn *c, int chan );
|
||||
int interleave_send( struct conn *conn, int chan, struct iovec *v, int count );
|
||||
int interleave_recv( struct conn *c, int chan, unsigned char *d, int len );
|
||||
void interleave_recv_rtcp( struct rtp_endpoint *ep, unsigned char *d, int len );
|
||||
void del_rtp_endpoint( struct rtp_endpoint *ep );
|
||||
int rtp_get_packet_size_limit( const struct rtp_endpoint *ep );
|
||||
int rtp_get_payload_size_limit( const struct rtp_endpoint *ep, int packet_overhead );
|
||||
int rtp_sendmsg( struct rtp_endpoint *ep, struct iovec *v, int count,
|
||||
unsigned int timestamp, uint8_t marker, int retries );
|
||||
void new_rtsp_location( const char *path, char *realm, char *username, char *password,
|
||||
open_func open, void *private );
|
||||
struct session *new_session(void);
|
||||
void del_session( struct session *sess );
|
||||
int print_session_list( char *s, int maxlen );
|
||||
int http_handle_msg( struct req *req );
|
||||
int rtsp_handle_msg( struct req *req );
|
||||
void send_rtcp(uint8_t *data);
|
||||
int new_rtcp_send( struct rtp_endpoint *ep, void *d );
|
||||
#endif
|
||||
|
||||
|
||||
54
sdk/app/spook/rtp_media.h
Normal file
54
sdk/app/spook/rtp_media.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef __RTP_MEDIA_H
|
||||
#define __RTP_MEDIA_H
|
||||
#include "frame.h"
|
||||
#include "stream.h"
|
||||
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
typedef int (*rtp_media_get_sdp_func)( char *dest, int len, int payload,
|
||||
int port, void *d );
|
||||
typedef int (*rtp_media_get_payload_func)( int payload, void *d );
|
||||
typedef int (*rtp_media_frame_func)( struct frame *f, void *d );
|
||||
typedef int (*rtp_media_send_func)( struct rtp_endpoint *ep, void *d );
|
||||
typedef void* (*rtp_loop_search_ep)( void *head,void *track,void **ep );
|
||||
typedef int (*rtp_media_send_more_func)( rtp_loop_search_ep search,void *ls, void *track, void *d); //track是live那边的结构体
|
||||
|
||||
|
||||
|
||||
struct rtp_media {
|
||||
rtp_media_get_sdp_func get_sdp;
|
||||
rtp_media_get_payload_func get_payload;
|
||||
rtp_media_frame_func frame;
|
||||
rtp_media_send_func send;
|
||||
rtp_media_send_more_func send_more;
|
||||
rtp_media_send_func rtcp_send;
|
||||
void *private;
|
||||
int per_ms_incr;
|
||||
uint32_t sample_rate;
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
|
||||
struct rtp_media *new_rtp_media( rtp_media_get_sdp_func get_sdp,
|
||||
rtp_media_get_payload_func get_payload, rtp_media_frame_func frame,
|
||||
rtp_media_send_func send, void *private );
|
||||
struct rtp_media *new_rtp_media_jpeg_stream( struct stream *stream );
|
||||
struct rtp_media *new_rtp_media_h264_stream( struct stream *stream );
|
||||
#endif
|
||||
|
||||
694
sdk/app/spook/rtsp.c
Normal file
694
sdk/app/spook/rtsp.c
Normal file
@@ -0,0 +1,694 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
|
||||
#include "lwip\api.h"
|
||||
|
||||
#include "lwip\tcp.h"
|
||||
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
#include <pmsg.h>
|
||||
#include <rtp.h>
|
||||
//#include "common.h"
|
||||
#include "osal/string.h"
|
||||
|
||||
|
||||
|
||||
void write_access_log( char *path, struct sockaddr *addr, int code, char *req,
|
||||
int length, char *referer, char *user_agent );
|
||||
|
||||
struct rtsp_session {
|
||||
struct rtsp_session *next;
|
||||
struct rtsp_session *prev;
|
||||
char id[32];
|
||||
struct session *sess;
|
||||
};
|
||||
|
||||
struct rtsp_location {
|
||||
struct loc_node node;
|
||||
char realm[128];
|
||||
char username[128];
|
||||
char password[128];
|
||||
open_func open;
|
||||
void *private;
|
||||
};
|
||||
|
||||
struct rtsp_conn {
|
||||
struct {
|
||||
struct rtp_endpoint *ep;
|
||||
int rtcp;
|
||||
} ichan[MAX_INTERLEAVE_CHANNELS];
|
||||
};
|
||||
|
||||
static struct rtsp_location *rtsp_loc_list = NULL;
|
||||
static struct rtsp_session *sess_list = NULL;
|
||||
static char *get_local_path( char *path )
|
||||
{
|
||||
char *c;
|
||||
static char *root = "/";
|
||||
|
||||
if( os_strncasecmp( path, "rtsp://", 7 ) ) return NULL;
|
||||
for( c = path + 7; *c != '/'; ++c ) if( *c == 0 ) return root;
|
||||
return c;
|
||||
}
|
||||
|
||||
static struct loc_node *node_find_location( struct loc_node *list,
|
||||
char *path, int len )
|
||||
{
|
||||
struct loc_node *loc;
|
||||
|
||||
for( loc = list; loc; loc = loc->next )
|
||||
{
|
||||
//_os_printf("%s path:%s\tloc:%s\tlen:%d\tres:%d\t%X\t%d\n",__FUNCTION__,path,loc->path,len,loc->path[len],'/',strncmp( path, loc->path, strlen( loc->path ) ));
|
||||
//if( (! strncmp( path, loc->path, strlen( loc->path ) )) && ( loc->path[len] == '/' || loc->path[len] == 0 ) )
|
||||
if( ! strncmp( path, loc->path, strlen( loc->path ) ))
|
||||
{
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct rtsp_location *find_rtsp_location( char *uri, char *base, int *track )
|
||||
{
|
||||
char *path, *c, *end;
|
||||
int len;
|
||||
|
||||
if( ! ( path = get_local_path( uri ) ) ) return NULL;
|
||||
len = strlen( path );
|
||||
|
||||
if( track )
|
||||
{
|
||||
*track = -1;
|
||||
if( ( c = strrchr( path, '/' ) ) &&
|
||||
! strncmp( c, "/track", 6 ) )
|
||||
{
|
||||
*track = strtol( c + 6, &end, 10 );
|
||||
if( ! *end ) len -= strlen( c );
|
||||
}
|
||||
}
|
||||
|
||||
if( len > 1 && path[len - 1] == '/' ) --len;
|
||||
|
||||
if( base )
|
||||
{
|
||||
strncpy( base, path, len );
|
||||
base[len] = 0;
|
||||
}
|
||||
|
||||
return (struct rtsp_location *)node_find_location(
|
||||
(struct loc_node *)rtsp_loc_list, path, len );
|
||||
}
|
||||
|
||||
static void init_location( struct loc_node *node, const char *path,
|
||||
struct loc_node **list )
|
||||
{
|
||||
node->next = *list;
|
||||
node->prev = NULL;
|
||||
if( node->next ) node->next->prev = node;
|
||||
*list = node;
|
||||
strcpy( node->path, path );
|
||||
}
|
||||
|
||||
void new_rtsp_location( const char *path, char *realm, char *username, char *password,
|
||||
open_func open, void *private )
|
||||
{
|
||||
struct rtsp_location *loc;
|
||||
|
||||
loc = (struct rtsp_location *)malloc( sizeof( struct rtsp_location ) );
|
||||
init_location( (struct loc_node *)loc, path,
|
||||
(struct loc_node **)&rtsp_loc_list );
|
||||
if( realm ) strcpy( loc->realm, realm );
|
||||
else loc->realm[0] = 0;
|
||||
if( username ) strcpy( loc->username, username );
|
||||
else loc->username[0] = 0;
|
||||
if( password ) strcpy( loc->password, password );
|
||||
else loc->password[0] = 0;
|
||||
loc->open = open;
|
||||
loc->private = private;
|
||||
}
|
||||
|
||||
static void rtsp_session_close( struct session *sess )
|
||||
{
|
||||
struct rtsp_session *rs = (struct rtsp_session *)sess->control_private;
|
||||
|
||||
spook_log( SL_DEBUG, "freeing session %s", rs->id );
|
||||
if( rs->next ) rs->next->prev = rs->prev;
|
||||
if( rs->prev ) rs->prev->next = rs->next;
|
||||
else sess_list = rs->next;
|
||||
free( rs );
|
||||
}
|
||||
|
||||
static struct rtsp_session *new_rtsp_session( struct session *sess )
|
||||
{
|
||||
struct rtsp_session *rs;
|
||||
|
||||
rs = (struct rtsp_session *)malloc( sizeof( struct rtsp_session ) );
|
||||
rs->next = sess_list;
|
||||
rs->prev = NULL;
|
||||
if( rs->next ) rs->next->prev = rs;
|
||||
sess_list = rs;
|
||||
random_id( (unsigned char *)rs->id, 30 );
|
||||
rs->sess = sess;
|
||||
sess->control_private = rs;
|
||||
sess->control_close = rtsp_session_close;
|
||||
return rs;
|
||||
}
|
||||
|
||||
static struct rtsp_session *get_session( char *id )
|
||||
{
|
||||
struct rtsp_session *rs;
|
||||
|
||||
if( ! id ) return NULL;
|
||||
for( rs = sess_list; rs; rs = rs->next )
|
||||
if( ! strcmp( rs->id, id ) ) break;
|
||||
return rs;
|
||||
}
|
||||
|
||||
void rtsp_conn_disconnect( struct conn *c )
|
||||
{
|
||||
struct rtsp_conn *rc = (struct rtsp_conn *)c->proto_state;
|
||||
int i;
|
||||
|
||||
for( i = 0; i < MAX_INTERLEAVE_CHANNELS; ++i )
|
||||
if( rc->ichan[i].ep && ! rc->ichan[i].rtcp )
|
||||
rc->ichan[i].ep->session->closed(
|
||||
rc->ichan[i].ep->session,
|
||||
rc->ichan[i].ep );
|
||||
free( rc );
|
||||
}
|
||||
|
||||
void interleave_disconnect( struct conn *c, int chan )
|
||||
{
|
||||
struct rtsp_conn *rc = (struct rtsp_conn *)c->proto_state;
|
||||
|
||||
rc->ichan[chan].ep = NULL;
|
||||
}
|
||||
|
||||
int interleave_recv( struct conn *c, int chan, unsigned char *d, int len )
|
||||
{
|
||||
struct rtsp_conn *rc = (struct rtsp_conn *)c->proto_state;
|
||||
|
||||
if( chan >= MAX_INTERLEAVE_CHANNELS || ! rc->ichan[chan].ep ) return -1;
|
||||
if( rc->ichan[chan].rtcp )
|
||||
interleave_recv_rtcp( rc->ichan[chan].ep, d, len );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int interleave_send( struct conn *c, int chan, struct iovec *v, int count )
|
||||
{
|
||||
unsigned char head[4];
|
||||
int len = 0, i;
|
||||
|
||||
for( i = 0; i < count; ++i ) len += v[i].iov_len;
|
||||
if( avail_send_buf( c ) < len + 4 ) return 1;
|
||||
head[0] = '$';
|
||||
head[1] = chan;
|
||||
PUT_16( head + 2, len );
|
||||
send_data( c, head, 4 );
|
||||
for( i = 0; i < count; ++i )
|
||||
send_data( c, v[i].iov_base, v[i].iov_len );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void log_request( struct req *req, int code, int length )
|
||||
{
|
||||
char *ref, *ua;
|
||||
|
||||
ref = get_header( req->req, "referer" );
|
||||
ua = get_header( req->req, "user-agent" );
|
||||
write_access_log( NULL, (struct sockaddr *)&req->conn->client_addr,
|
||||
code, (char *)req->conn->req_buf, length,
|
||||
ref ? ref : "-", ua ? ua : "-" );
|
||||
}
|
||||
|
||||
static int rtsp_create_reply( struct req *req, int code, char *reason )
|
||||
{
|
||||
if( ! ( req->resp = new_pmsg( 512 ) ) ) return -1;
|
||||
req->resp->type = PMSG_RESP;
|
||||
req->resp->proto_id = add_pmsg_string( req->resp, "RTSP/1.0" );
|
||||
req->resp->sl.stat.code = code;
|
||||
req->resp->sl.stat.reason = add_pmsg_string( req->resp, reason );
|
||||
copy_headers( req->resp, req->req, "CSeq" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rtsp_send_error( struct req *req, int code, char *reason )
|
||||
{
|
||||
log_request( req, code, 0 );
|
||||
rtsp_create_reply( req, code, reason );
|
||||
tcp_send_pmsg( req->conn, req->resp, -1 );
|
||||
}
|
||||
/*
|
||||
static int rtsp_verify_auth( struct req *req, char *realm,
|
||||
char *username, char *password )
|
||||
{
|
||||
int ret = check_digest_response( req->req, realm, username, password );
|
||||
|
||||
if( ret > 0 ) return 0;
|
||||
|
||||
log_request( req, 401, 0 );
|
||||
rtsp_create_reply( req, 401, "Unauthorized" );
|
||||
add_digest_challenge( req->resp, realm, ret == 0 ? 1 : 0 );
|
||||
tcp_send_pmsg( req->conn, req->resp, -1 );
|
||||
return -1;
|
||||
}
|
||||
*/
|
||||
static int handle_OPTIONS( struct req *req )
|
||||
{
|
||||
rtsp_create_reply( req, 200, "OK" );
|
||||
add_header( req->resp, "Public",
|
||||
"DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE" );
|
||||
log_request( req, 200, 0 );
|
||||
tcp_send_pmsg( req->conn, req->resp, -1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_DESCRIBE( struct req *req )
|
||||
{
|
||||
char sdp[512+128], path[64], hdr[128];
|
||||
int sdp_len;
|
||||
struct rtsp_location *loc;
|
||||
struct session *sess;
|
||||
|
||||
spook_log( SL_DEBUG, "describing streams under '%s'", req->req->sl.req.uri );
|
||||
//_os_printf("%s:%d url:%s\n",__FUNCTION__,__LINE__,req->req->sl.req.uri);
|
||||
|
||||
if( ! ( loc = find_rtsp_location( req->req->sl.req.uri,
|
||||
path, NULL ) ) ||
|
||||
! ( sess = loc->open( path, loc->private ) ) )
|
||||
{
|
||||
_os_printf("no found uri:%s\tloc:%X\n",req->req->sl.req.uri,(int)loc);
|
||||
rtsp_send_error( req, 404, "Not Found" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
if( loc->realm[0] && rtsp_verify_auth( req, loc->realm,
|
||||
loc->username, loc->password ) < 0 )
|
||||
return 0;*/
|
||||
|
||||
sdp_len = sizeof( sdp ) - 2;
|
||||
if( sess->get_sdp( sess, sdp, &sdp_len, path ) > 0 )
|
||||
{
|
||||
|
||||
rtsp_create_reply( req, 200, "OK" );
|
||||
sprintf( hdr, "%s/", req->req->sl.req.uri );
|
||||
//add_header( req->resp, "Content-Base", hdr );
|
||||
add_header( req->resp, "Content-Type", "application/sdp" );
|
||||
add_header( req->resp, "Cache-Control", "no-cache" );
|
||||
add_header( req->resp, "Server", "Hisilicon RTSP Streaming Media Server/1.0.0" );
|
||||
|
||||
log_request( req, 200, sdp_len );
|
||||
|
||||
|
||||
if( tcp_send_pmsg( req->conn, req->resp, sdp_len ) >= 0 )
|
||||
send_data( req->conn, (unsigned char *)sdp, sdp_len );
|
||||
} else
|
||||
{
|
||||
_os_printf("sdp:%x %x\n",(int)sess->get_sdp,(int)sess->get_sdp( sess, sdp, &sdp_len, path));
|
||||
rtsp_send_error( req, 404, "Not Found" );
|
||||
}
|
||||
|
||||
sess->select_close(sess, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtsp_udp_setup( struct session *s, int track,
|
||||
struct req *req, char *t )
|
||||
{
|
||||
char *p, *end, trans[128];
|
||||
int cport, server_port;
|
||||
|
||||
if( ! ( p = strstr( t, "client_port" ) ) || *(p + 11) != '=' )
|
||||
return -1;
|
||||
cport = strtol( p + 12, &end, 10 );
|
||||
if( end == p + 12 ) return -1;
|
||||
spook_log( SL_DEBUG, "client requested UDP port %d", cport );
|
||||
|
||||
if( connect_udp_endpoint( s->ep[track], req->conn->client_addr.sin_addr,
|
||||
cport, &server_port ) < 0 )
|
||||
return -1;
|
||||
|
||||
spook_log( SL_VERBOSE, "our port is %d, client port is %d",
|
||||
server_port, cport );
|
||||
|
||||
sprintf( trans, "RTP/AVP;unicast;client_port=%d-%d;server_port=%d-%d",
|
||||
cport, cport + 1, server_port, server_port + 1 );
|
||||
add_header( req->resp, "Transport", trans );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtsp_interleave_setup( struct session *s, int track,
|
||||
struct req *req, char *t )
|
||||
{
|
||||
struct rtsp_conn *rc = (struct rtsp_conn *)req->conn->proto_state;
|
||||
char *p, *end, trans[128];
|
||||
int rtp_chan = -1, rtcp_chan = -1, i;
|
||||
|
||||
if( ( p = strstr( t, "interleaved" ) ) )
|
||||
{
|
||||
if( *(p + 11) != '=' ) return -1;
|
||||
rtp_chan = strtol( p + 12, &end, 10 );
|
||||
rtcp_chan = rtp_chan + 1; // XXX make better parser
|
||||
if( end == p + 12 ) return -1;
|
||||
if( rtp_chan < 0 || rtcp_chan < 0 ||
|
||||
rtp_chan >= MAX_INTERLEAVE_CHANNELS ||
|
||||
rtcp_chan >= MAX_INTERLEAVE_CHANNELS )
|
||||
return -1;
|
||||
spook_log( SL_VERBOSE, "requested interleave channels %d-%d",
|
||||
rtp_chan, rtcp_chan );
|
||||
if( rc && ( rc->ichan[rtp_chan].ep ||
|
||||
rc->ichan[rtcp_chan].ep ) )
|
||||
return -1;
|
||||
} else
|
||||
{
|
||||
spook_log( SL_VERBOSE, "requested any interleave channel" );
|
||||
if( rc )
|
||||
{
|
||||
for( i = 0; i < MAX_INTERLEAVE_CHANNELS; i += 2 )
|
||||
if( ! rc->ichan[i].ep && ! rc->ichan[i + 1].ep )
|
||||
break;
|
||||
if( i >= MAX_INTERLEAVE_CHANNELS ) return -1;
|
||||
rtp_chan = i;
|
||||
rtcp_chan = i + 1;
|
||||
} else
|
||||
{
|
||||
rtp_chan = 0;
|
||||
rtcp_chan = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( ! rc )
|
||||
{
|
||||
rc = (struct rtsp_conn *)malloc( sizeof( struct rtsp_conn ) );
|
||||
if( ! rc )
|
||||
{
|
||||
spook_log( SL_ERR,
|
||||
"out of memory on malloc rtsp_conn" );
|
||||
return -1;
|
||||
}
|
||||
for( i = 0; i < MAX_INTERLEAVE_CHANNELS; ++i )
|
||||
rc->ichan[i].ep = NULL;
|
||||
req->conn->proto_state = rc;
|
||||
}
|
||||
|
||||
rc->ichan[rtp_chan].ep = s->ep[track];
|
||||
rc->ichan[rtp_chan].rtcp = 0;
|
||||
rc->ichan[rtcp_chan].ep = s->ep[track];
|
||||
rc->ichan[rtcp_chan].rtcp = 1;
|
||||
|
||||
connect_interleaved_endpoint( s->ep[track], req->conn,
|
||||
rtp_chan, rtcp_chan );
|
||||
|
||||
sprintf( trans, "RTP/AVP/TCP;unicast;interleaved=%d-%d",
|
||||
rtp_chan, rtcp_chan );
|
||||
add_header( req->resp, "Transport", trans );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_SETUP( struct req *req )
|
||||
{
|
||||
char *t, *sh, path[256];
|
||||
int track, ret;
|
||||
struct session *s;
|
||||
struct rtsp_session *rs = NULL;
|
||||
struct rtsp_location *loc;
|
||||
|
||||
if( ! ( loc = find_rtsp_location( req->req->sl.req.uri,
|
||||
path, &track ) ) ||
|
||||
track < 0 || track >= MAX_TRACKS )
|
||||
{
|
||||
rtsp_send_error( req, 404, "Not Found" );
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
if( loc->realm[0] && rtsp_verify_auth( req, loc->realm,
|
||||
loc->username, loc->password ) < 0 )
|
||||
return 0;*/
|
||||
|
||||
if( ! ( t = get_header( req->req, "Transport" ) ) )
|
||||
{
|
||||
_os_printf("%s %d\r\n",__func__,__LINE__);
|
||||
// XXX better error reply
|
||||
rtsp_send_error( req, 461, "Unspecified Transport" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( ! ( sh = get_header( req->req, "Session" ) ) )
|
||||
{
|
||||
/*callback: live_open */
|
||||
if( ! ( s = loc->open( path, loc->private ) ) )
|
||||
{
|
||||
rtsp_send_error( req, 404, "Not Found" );
|
||||
return 0;
|
||||
}
|
||||
sprintf( s->addr, "IP4 %s",
|
||||
inet_ntoa( req->conn->client_addr.sin_addr ) );
|
||||
} else if( ! ( rs = get_session( sh ) ) )
|
||||
{
|
||||
rtsp_send_error( req, 454, "Session Not Found" );
|
||||
return 0;
|
||||
} else s = rs->sess;
|
||||
|
||||
if( s->ep[track] )
|
||||
{
|
||||
_os_printf("%s %d\r\n",__func__,__LINE__);
|
||||
// XXX better error reply
|
||||
rtsp_send_error( req, 461, "Unsupported Transport" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* callback: live_setup */
|
||||
if( s->setup( s, track ) < 0 )
|
||||
{
|
||||
rtsp_send_error( req, 404, "Not Found" );
|
||||
if( ! rs ) s->select_close(s, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
spook_log( SL_DEBUG, "setting up RTP (%s)", t );
|
||||
|
||||
rtsp_create_reply( req, 200, "OK" );
|
||||
|
||||
if( ! strncasecmp( t, "RTP/AVP/TCP", 11 ) )
|
||||
{
|
||||
// ret = -1;
|
||||
ret = rtsp_interleave_setup( s, track, req, t );
|
||||
}
|
||||
else if( ( ! strncasecmp( t, "RTP/AVP", 7 ) && t[7] != '/' ) ||
|
||||
! strncasecmp( t, "RTP/AVP/UDP", 11 ) )
|
||||
ret = rtsp_udp_setup( s, track, req, t );
|
||||
else ret = -1;
|
||||
|
||||
if( ret < 0 )
|
||||
{
|
||||
free_pmsg( req->resp );
|
||||
_os_printf("%s %d\r\n",__func__,__LINE__);
|
||||
rtsp_send_error( req, 461, "Unsupported Transport" );
|
||||
s->select_close(s, s->ep[track]);
|
||||
} else
|
||||
{
|
||||
if( ! rs ) rs = new_rtsp_session( s );
|
||||
add_header( req->resp, "Session", rs->id );
|
||||
log_request( req, 200, 0 );
|
||||
tcp_send_pmsg( req->conn, req->resp, -1 );
|
||||
|
||||
/* add by jornny */
|
||||
rs->sess->conn = req->conn;
|
||||
req->conn->sess = s;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_PLAY( struct req *req )
|
||||
{
|
||||
struct rtsp_session *rs;
|
||||
double start;
|
||||
int have_start = 0, i, p, ret;
|
||||
char *range, hdr[512];
|
||||
if( ! ( rs = get_session( get_header( req->req, "Session" ) ) ) )
|
||||
{
|
||||
rtsp_send_error( req, 454, "Session Not Found" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
range = get_header( req->req, "Range" );
|
||||
if( range && sscanf( range, "npt=%lf-", &start ) == 1 )
|
||||
{
|
||||
have_start = 1;
|
||||
spook_log( SL_VERBOSE,
|
||||
"starting streaming for session %s at position %.4f",
|
||||
rs->id, start );
|
||||
} else spook_log( SL_VERBOSE,
|
||||
"starting streaming for session %s", rs->id );
|
||||
|
||||
/* callback:live_play( struct session *s, double *start ) */
|
||||
rs->sess->play( rs->sess, have_start ? &start : NULL );
|
||||
|
||||
rtsp_create_reply( req, 200, "OK" );
|
||||
add_header( req->resp, "Session", rs->id );
|
||||
if( have_start && start >= 0 )
|
||||
{
|
||||
spook_log( SL_DEBUG, "backend seeked to %.4f", start );
|
||||
sprintf( hdr, "npt=%.4f-", start );
|
||||
add_header( req->resp, "Range", hdr );
|
||||
p = 0;
|
||||
for( i = 0; i < MAX_TRACKS; ++i )
|
||||
{
|
||||
if( ! rs->sess->ep[i] ) continue;
|
||||
ret = snprintf( hdr + p, sizeof( hdr ) - p,
|
||||
"url=%s/track%d;seq=%d;rtptime=%u,",
|
||||
req->req->sl.req.uri, i,
|
||||
rs->sess->ep[i]->seqnum,
|
||||
rs->sess->ep[i]->last_timestamp );
|
||||
if( ret >= sizeof( hdr ) - p )
|
||||
{
|
||||
p = -1;
|
||||
break;
|
||||
}
|
||||
p += ret;
|
||||
}
|
||||
if( p > 0 )
|
||||
{
|
||||
hdr[p - 1] = 0; /* Kill last comma */
|
||||
add_header( req->resp, "RTP-Info", hdr );
|
||||
}
|
||||
}
|
||||
log_request( req, 200, 0 );
|
||||
tcp_send_pmsg( req->conn, req->resp, -1 );
|
||||
#ifdef FLY_CONTROL_RECEIVE
|
||||
#endif
|
||||
/* to test audio adc when transporting picture */
|
||||
// extern void auadc_init(void);
|
||||
// auadc_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_PAUSE( struct req *req )
|
||||
{
|
||||
struct rtsp_session *rs;
|
||||
|
||||
if( ! ( rs = get_session( get_header( req->req, "Session" ) ) ) )
|
||||
{
|
||||
rtsp_send_error( req, 454, "Session Not Found" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( ! rs->sess->pause )
|
||||
{
|
||||
rtsp_send_error( req, 501, "Not Implemented" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
spook_log( SL_VERBOSE, "pausing session %s", rs->id );
|
||||
|
||||
rs->sess->pause( rs->sess );
|
||||
|
||||
rtsp_create_reply( req, 200, "OK" );
|
||||
add_header( req->resp, "Session", rs->id );
|
||||
log_request( req, 200, 0 );
|
||||
tcp_send_pmsg( req->conn, req->resp, -1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_TEARDOWN( struct req *req )
|
||||
{
|
||||
struct rtsp_location *loc;
|
||||
struct rtsp_session *rs;
|
||||
int track;
|
||||
_os_printf("handle_teardown\r\n");
|
||||
if( ! ( loc = find_rtsp_location( req->req->sl.req.uri,
|
||||
NULL, &track ) ) ||
|
||||
track >= MAX_TRACKS )
|
||||
{
|
||||
rtsp_send_error( req, 404, "Not Found" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( ! ( rs = get_session( get_header( req->req, "Session" ) ) ) )
|
||||
{
|
||||
rtsp_send_error( req, 454, "Session Not Found" );
|
||||
return 0;
|
||||
}
|
||||
spook_log( SL_VERBOSE, "tearing down %s in session %s",
|
||||
req->req->sl.req.uri, rs->id );
|
||||
|
||||
rtsp_create_reply( req, 200, "OK" );
|
||||
add_header( req->resp, "Session", rs->id );
|
||||
|
||||
/* This might destroy the session, so do it after creating the reply
|
||||
* callback function: live_teardown */
|
||||
rs->sess->closed( rs->sess, track < 0 ? NULL : rs->sess->ep[track] );
|
||||
|
||||
/* add by jornny */
|
||||
//req->conn->sess = NULL;
|
||||
|
||||
log_request( req, 200, 0 );
|
||||
tcp_send_pmsg( req->conn, req->resp, -1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_unknown( struct req *req )
|
||||
{
|
||||
rtsp_send_error( req, 501, "Not Implemented" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8 spook_handle_open = 1;
|
||||
|
||||
int rtsp_handle_msg( struct req *req )
|
||||
{
|
||||
int ret;
|
||||
_os_printf("===========%s=============\r\n",req->req->sl.req.method);
|
||||
|
||||
if(spook_handle_open == 0){
|
||||
ret = handle_unknown( req );
|
||||
return ret;
|
||||
}
|
||||
|
||||
if( ! os_strcasecmp( req->req->sl.req.method, "OPTIONS" ) )
|
||||
ret = handle_OPTIONS( req );
|
||||
else if( ! os_strcasecmp( req->req->sl.req.method, "DESCRIBE" ) )
|
||||
ret = handle_DESCRIBE( req );
|
||||
else if( ! os_strcasecmp( req->req->sl.req.method, "SETUP" ) )
|
||||
ret = handle_SETUP( req );
|
||||
else if( ! os_strcasecmp( req->req->sl.req.method, "PLAY" ) )
|
||||
ret = handle_PLAY( req );
|
||||
else if( ! os_strcasecmp( req->req->sl.req.method, "PAUSE" ) )
|
||||
ret = handle_PAUSE( req );
|
||||
else if( ! os_strcasecmp( req->req->sl.req.method, "TEARDOWN" ) )
|
||||
ret = handle_TEARDOWN( req );
|
||||
else ret = handle_unknown( req );
|
||||
return ret;
|
||||
}
|
||||
|
||||
513
sdk/app/spook/rtsp_common.c
Normal file
513
sdk/app/spook/rtsp_common.c
Normal file
@@ -0,0 +1,513 @@
|
||||
#include <stdio.h>
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
#include "lwip\api.h"
|
||||
#include "lwip\tcp.h"
|
||||
#include "log.h"
|
||||
#include "sys_config.h"
|
||||
#include "osal/string.h"
|
||||
#include "rtsp_common.h"
|
||||
#include "event.h"
|
||||
|
||||
|
||||
#ifdef send
|
||||
#undef send
|
||||
#endif
|
||||
|
||||
|
||||
static int rtsp_get_sdp( struct session *s, char *dest, int *len,
|
||||
char *path )
|
||||
{
|
||||
struct rtsp_session *ls = (struct rtsp_session *)s->private;
|
||||
int i = 0, t;
|
||||
char *addr = "IP4 0.0.0.0";
|
||||
_os_printf("%s:%d\tpath:%s\n",__FUNCTION__,__LINE__,path);
|
||||
|
||||
if( s->ep[0] && s->ep[0]->trans_type == RTP_TRANS_UDP )
|
||||
addr = s->ep[0]->trans.udp.sdp_addr;
|
||||
|
||||
i = snprintf( dest, *len,
|
||||
"v=0\r\no=- 1 1 IN IP4 127.0.0.1\r\ns=Test\r\na=type:broadcast\r\nt=0 0\r\nc=IN %s\r\n", addr );
|
||||
|
||||
_os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
|
||||
for( t = 0; t < MAX_TRACKS && ls->source->track[t].rtp; ++t )
|
||||
{
|
||||
int port;
|
||||
|
||||
//if( ! ls->source->track[t].ready ) return 0;
|
||||
|
||||
if( s->ep[t] && s->ep[t]->trans_type == RTP_TRANS_UDP )
|
||||
port = s->ep[t]->trans.udp.sdp_port;
|
||||
else
|
||||
port = 0;
|
||||
|
||||
if(ls->source->track[t].rtp->type == 0)
|
||||
{
|
||||
i += ls->source->track[t].rtp->get_sdp( dest + i, *len - i,96 + t, port,ls->source->track[t].rtp->private );
|
||||
}
|
||||
else
|
||||
{
|
||||
i += ls->source->track[t].rtp->get_sdp( dest + i, *len - i,96 + t, port,NULL);
|
||||
}
|
||||
if( port == 0 ) // XXX What's a better way to do this?
|
||||
i += sprintf( dest + i, "a=control:track%d\r\n", t );
|
||||
}
|
||||
|
||||
*len = i;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int rtsp_setup( struct session *s, int t )
|
||||
{
|
||||
struct rtsp_session *ls = (struct rtsp_session *)s->private;
|
||||
int payload = 96 + t;
|
||||
|
||||
//判断source是否已经创建了线程,如果没有创建线程,则在这里创建线程
|
||||
//线程创建成功后,实际内部不会进行太多操作,主要是读取视频和音频数据
|
||||
//直到play执行后,对应位被置位后,才会进行发送
|
||||
//如果teardown后,如果没有ls链表,则将线程删除,释放空间
|
||||
//删除线程,就要同时考虑将对应视频和音频接入的框架流disable掉,所以是不是应该有一个释放资源的回调函数呢
|
||||
//所以source是不是应该有一个创建线程和释放线程的回调函数,用的是自身的句柄
|
||||
//线程传入的参数可以通过source中的track查找,不用管t的值,而是看初始化的时候创建多少个track决定,配合创建线程
|
||||
|
||||
|
||||
//线程创建
|
||||
if(!ls->source->handle.hdl)
|
||||
{
|
||||
if(ls->source->creat)
|
||||
{
|
||||
ls->source->creat(ls->source,ls->path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_os_printf("live thread already run:%X\t%X\n",(int)ls->source->handle.hdl,(int)ls->source);
|
||||
}
|
||||
|
||||
if( ! ls->source->track[t].rtp ) return -1;
|
||||
|
||||
if( ls->source->track[t].rtp->get_payload )
|
||||
payload = ls->source->track[t].rtp->get_payload( payload,
|
||||
ls->source->track[t].rtp->private );
|
||||
s->ep[t] = new_rtp_endpoint( payload );
|
||||
s->ep[t]->session = s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void rtsp_play( struct session *s, double *start )
|
||||
{
|
||||
struct rtsp_session *ls = (struct rtsp_session *)s->private;
|
||||
int t;
|
||||
|
||||
if( start ) *start = -1;
|
||||
ls->playing = 1;
|
||||
for( t = 0; t < MAX_TRACKS && ls->source->track[t].rtp; ++t )
|
||||
{
|
||||
//对应位置位,然后就线程会自动发送数据
|
||||
struct rtsp_track *track = &ls->source->track[t];
|
||||
track->ready = 1;
|
||||
//clear_init_done(track->rtp->private);
|
||||
if( s->ep[t] ) set_waiting( ls->source->track[t].stream, 1 );
|
||||
}
|
||||
if(ls->source->play)
|
||||
{
|
||||
ls->source->play(ls->source);
|
||||
}
|
||||
}
|
||||
|
||||
static void track_check_running( struct rtsp_source *source, int t )
|
||||
{
|
||||
struct rtsp_session *ls;
|
||||
|
||||
for( ls = source->sess_list; ls; ls = ls->next )
|
||||
if( ls->playing && ls->sess->ep[t] ) return;
|
||||
|
||||
set_waiting( source->track[t].stream, 0 );
|
||||
}
|
||||
extern void drop_conn( struct conn *c );
|
||||
void rtsp_release_event(void *ei, void *d)
|
||||
{
|
||||
_os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
struct session *s = (struct session*)d;
|
||||
struct rtsp_session *ls = (struct rtsp_session *)s->private;
|
||||
struct conn *c = s->conn;
|
||||
if(c)
|
||||
{
|
||||
drop_conn( c );
|
||||
}
|
||||
os_free( ls );
|
||||
del_session( s );
|
||||
}
|
||||
|
||||
|
||||
static void rtsp_teardown( struct session *s, struct rtp_endpoint *ep )
|
||||
{
|
||||
_os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
struct rtsp_session *ls = (struct rtsp_session *)s->private;
|
||||
//struct rtsp_source *source = ls->source;
|
||||
int remaining = 0;
|
||||
|
||||
for( int i = 0; i < MAX_TRACKS && ls->source->track[i].rtp; ++i )
|
||||
{
|
||||
if( ! s->ep[i] ) continue;
|
||||
if( ! ep || s->ep[i] == ep )
|
||||
{
|
||||
del_rtp_endpoint( s->ep[i] );
|
||||
s->ep[i] = NULL;
|
||||
track_check_running( ls->source, i );
|
||||
} else ++remaining;
|
||||
}
|
||||
|
||||
if( remaining == 0 )
|
||||
{
|
||||
//解锁ls链表
|
||||
if( ls->next ) ls->next->prev = ls->prev;
|
||||
if( ls->prev ) ls->prev->next = ls->next;
|
||||
else ls->source->sess_list = ls->next;
|
||||
//启动event将事件移除
|
||||
//断开链表后,开启eloop的alarm事件,清除对应的资源
|
||||
eloop_add_alarm(os_jiffies(),EVENT_F_ENABLED,rtsp_release_event,(void*)s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void rtsp_closed( struct session *s, struct rtp_endpoint *ep )
|
||||
{
|
||||
_os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
struct rtsp_session *ls = (struct rtsp_session *)s->private;
|
||||
ls->closed = 1;
|
||||
}
|
||||
|
||||
static void rtsp_select_close( struct session *s, struct rtp_endpoint *ep)
|
||||
{
|
||||
struct rtsp_session *ls = (struct rtsp_session *)s->private;
|
||||
if(ls->source->handle.hdl)
|
||||
{
|
||||
_os_printf("%s: %d, set closed\r\n", __FUNCTION__, __LINE__);
|
||||
s->closed(s, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
_os_printf("%s: %d, set teardown\r\n", __FUNCTION__, __LINE__);
|
||||
s->teardown(s, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
struct session *rtsp_open( char *path, void *d )
|
||||
{
|
||||
struct rtsp_source *source = (struct rtsp_source *)d;
|
||||
struct rtsp_session *ls;
|
||||
ls = (struct rtsp_session *)os_malloc( sizeof( struct rtsp_session ) );
|
||||
ls->next = source->sess_list;
|
||||
if( ls->next ) ls->next->prev = ls;
|
||||
source->sess_list = ls;
|
||||
ls->prev = NULL;
|
||||
ls->sess = new_session();
|
||||
ls->source = source;
|
||||
ls->playing = 0;
|
||||
ls->sess->get_sdp = rtsp_get_sdp;
|
||||
ls->sess->setup = rtsp_setup;
|
||||
ls->sess->play = rtsp_play;
|
||||
ls->sess->teardown = rtsp_teardown;
|
||||
ls->sess->closed = rtsp_closed;
|
||||
ls->sess->select_close = rtsp_select_close;
|
||||
ls->sess->private = ls;
|
||||
ls->closed = 0;
|
||||
if(strlen(path)<sizeof(ls->path))
|
||||
{
|
||||
memcpy(ls->path,path,strlen(path)+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
_os_printf("%s path too long:%d\n",__FUNCTION__,strlen(path));
|
||||
}
|
||||
return ls->sess;
|
||||
}
|
||||
|
||||
static void *loop_search_ep(void *in_head,void *in_track,void **ep)
|
||||
{
|
||||
struct rtsp_session *ls;
|
||||
struct rtsp_session *head = (struct rtsp_session *)in_head;
|
||||
struct rtsp_track *track = (struct rtsp_track *)in_track;
|
||||
for( ls = head; ls; ls = ls->next )
|
||||
{
|
||||
|
||||
if( ls->playing && track->ready && ls->sess->ep[track->index] )
|
||||
{
|
||||
*ep = ls->sess->ep[track->index];
|
||||
return ls->next;
|
||||
}
|
||||
}
|
||||
*ep = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void rtsp_common_send( struct frame *f, void *d )
|
||||
{
|
||||
struct rtsp_track *track = (struct rtsp_track *)d;/*d: source->track[t]*/
|
||||
struct rtsp_session *ls;
|
||||
if(!f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(!track->ready)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//dest_status_msg(track,track->ready);
|
||||
/*struct rtsp_session *next;*/
|
||||
//这里添加获取jpeg的节点头,发送完一帧后,再循环到这里,看是否有下一帧数据,如果有,那就继续发送,省去多return一次的时间
|
||||
/*callback: jpeg_process_frame, track->rtp->private: rtp_jpeg *out*/
|
||||
|
||||
if( ! track->rtp->frame( f, track->rtp ) ) //这个按理说不用一直运行的,只运行一次则可以
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(track->rtp->send_more)
|
||||
{
|
||||
ls = track->source->sess_list;
|
||||
if(!ls->closed){
|
||||
track->rtp->send_more( loop_search_ep,ls, track,track->rtp->private);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for( ls = track->source->sess_list; ls; ls = ls->next )
|
||||
{
|
||||
if( ls->playing && track->ready && ls->sess->ep[track->index] )
|
||||
{
|
||||
/*callback: jpeg_send;ls->sess->ep[track->index], track->rtp->private: out*/
|
||||
//track->rtp->send( ls->sess->ep[track->index], track->rtp->private );
|
||||
if(track->rtp && track->rtp->rtcp_send)
|
||||
{
|
||||
track->rtp->rtcp_send( ls->sess->ep[track->index], track->rtp );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void rtsp_frame_end( struct frame *f, void *d ){
|
||||
struct rtsp_track *track = (struct rtsp_track *)d;/*d: source->track[t]*/
|
||||
struct rtsp_session *ls;
|
||||
struct rtsp_source *source;
|
||||
|
||||
for( ls = track->source->sess_list; ls; ls = ls->next )
|
||||
{
|
||||
source = ls->source;
|
||||
if( !f || ls->closed)
|
||||
{
|
||||
/*callback: jpeg_send;ls->sess->ep[track->index], track->rtp->private: out*/
|
||||
//track->rtp->send( ls->sess->ep[track->index], track->rtp->private );
|
||||
ls->sess->teardown( ls->sess, ls->sess->ep[track->index] );
|
||||
|
||||
//如果有teardown后,则需要重新去轮询,因为之前的ls被释放了,虽然效率低,但考虑到设备数不多,这样应该还好
|
||||
ls = track->source->sess_list;
|
||||
if(!ls)
|
||||
{
|
||||
if(f)
|
||||
{
|
||||
//FREE_JPG_NODE(f->get_f);
|
||||
unref_frame( f );
|
||||
}
|
||||
//因为所有的东西被释放,所以就要将线程和资源释放
|
||||
if(source->release)
|
||||
{
|
||||
source->release(source);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void *rtsp_start_block(void)
|
||||
{
|
||||
struct rtsp_source *source;
|
||||
int i;
|
||||
|
||||
spook_log (SL_DEBUG, "live start_block");
|
||||
|
||||
source = (struct rtsp_source *)os_malloc( sizeof( struct rtsp_source ) );
|
||||
//初始化source
|
||||
memset(source,0,sizeof( struct rtsp_source ));
|
||||
source->head_len = SPOOK_CACHE_BUF_HEAD_LEN;
|
||||
//配置默认值
|
||||
for( i = 0; i < MAX_TRACKS; ++i )
|
||||
{
|
||||
source->track[i].index = i;
|
||||
source->track[i].source = source;
|
||||
source->track[i].stream = NULL;
|
||||
source->track[i].ready = 0;
|
||||
source->track[i].rtp = NULL;
|
||||
|
||||
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
int rtsp_end_block( void *d )
|
||||
{
|
||||
struct rtsp_source *source = (struct rtsp_source *)d;
|
||||
|
||||
spook_log (SL_DEBUG, "live end_block");
|
||||
|
||||
if( ! source->track[0].rtp )
|
||||
{
|
||||
spook_log( SL_ERR, "live: no media sources specified!" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "osal/string.h"
|
||||
//视频注册
|
||||
int set_video_track( const char *name, void *d )
|
||||
{
|
||||
struct rtsp_source *source = (struct rtsp_source *)d;
|
||||
int t;
|
||||
os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
spook_log (SL_DEBUG, "live set_track %s", name);
|
||||
|
||||
for( t = 0; t < MAX_TRACKS && source->track[t].rtp; ++t );
|
||||
spook_log (SL_DEBUG, "live set_track %s", name);
|
||||
if( t == MAX_TRACKS )
|
||||
{
|
||||
spook_log( SL_ERR, "live: exceeded maximum number of tracks" );
|
||||
return -1;
|
||||
}
|
||||
spook_log (SL_DEBUG, "live set_track %s", name);
|
||||
if( ! ( source->track[t].stream = connect_to_stream( name,rtsp_common_send, &source->track[t] ) ) )
|
||||
{
|
||||
spook_log( SL_ERR,"live: unable to connect to stream \"%s\"", name );
|
||||
return -2;
|
||||
}
|
||||
|
||||
spook_log (SL_DEBUG, "live set_track %s", name);
|
||||
source->live_node.video_ex = get_video_ex(source->track[t].stream->stream->private);
|
||||
|
||||
|
||||
disconnect_stream(source->track[t].stream,rtsp_frame_end);
|
||||
|
||||
source->track[t].rtp = new_rtp_media_jpeg_stream(source->track[t].stream->stream );
|
||||
os_printf("%s source->track[t]:%X\trtp:%X\n",__FUNCTION__,source->track[t],source->track[t].rtp);
|
||||
if( ! source->track[t].rtp ) return -3;
|
||||
|
||||
set_waiting( source->track[t].stream, 0 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//视频注册
|
||||
int set_video_h264_track( const char *name, void *d )
|
||||
{
|
||||
struct rtsp_source *source = (struct rtsp_source *)d;
|
||||
int t;
|
||||
os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
spook_log (SL_DEBUG, "live set_track %s", name);
|
||||
|
||||
for( t = 0; t < MAX_TRACKS && source->track[t].rtp; ++t );
|
||||
spook_log (SL_DEBUG, "live set_track %s", name);
|
||||
if( t == MAX_TRACKS )
|
||||
{
|
||||
spook_log( SL_ERR, "live: exceeded maximum number of tracks" );
|
||||
return -1;
|
||||
}
|
||||
spook_log (SL_DEBUG, "live set_track %s", name);
|
||||
if( ! ( source->track[t].stream = connect_to_stream( name,rtsp_common_send, &source->track[t] ) ) )
|
||||
{
|
||||
spook_log( SL_ERR,"live: unable to connect to stream \"%s\"", name );
|
||||
return -2;
|
||||
}
|
||||
|
||||
spook_log (SL_DEBUG, "live set_track %s", name);
|
||||
source->live_node.video_ex = get_video_ex(source->track[t].stream->stream->private);
|
||||
|
||||
|
||||
disconnect_stream(source->track[t].stream,rtsp_frame_end);
|
||||
|
||||
source->track[t].rtp = new_rtp_media_h264_stream(source->track[t].stream->stream );
|
||||
os_printf("%s source->track[t]:%X\trtp:%X\n",__FUNCTION__,source->track[t],source->track[t].rtp);
|
||||
if( ! source->track[t].rtp ) return -3;
|
||||
|
||||
set_waiting( source->track[t].stream, 0 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//音频注册
|
||||
int set_audio_track( char *name, void *d )
|
||||
{
|
||||
struct rtsp_source *source = (struct rtsp_source *)d;
|
||||
int t;
|
||||
|
||||
spook_log (SL_DEBUG, "live set_track %s", name);
|
||||
|
||||
for( t = 0; t < MAX_TRACKS && source->track[t].rtp; ++t );
|
||||
|
||||
if( t == MAX_TRACKS )
|
||||
{
|
||||
spook_log( SL_ERR, "live: exceeded maximum number of tracks" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( ! ( source->track[t].stream = connect_to_stream(name,rtsp_common_send, &source->track[t] ) ) )
|
||||
{
|
||||
spook_log( SL_ERR,"live: unable to connect to stream \"%s\"", name );
|
||||
return -1;
|
||||
}
|
||||
|
||||
source->live_node.audio_ex = get_audio_ex(source->track[t].stream->stream->private);
|
||||
os_printf("live audio_ex:%X\n",source->live_node.audio_ex);
|
||||
disconnect_stream(source->track[t].stream,rtsp_frame_end);
|
||||
|
||||
//要重新新建
|
||||
source->track[t].rtp = new_rtp_media_audio_stream(source->track[t].stream->stream );
|
||||
|
||||
|
||||
//默认打开
|
||||
source->track[t].ready = 0;
|
||||
|
||||
if( ! source->track[t].rtp ) return -1;
|
||||
|
||||
set_waiting( source->track[t].stream, 0 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int rtsp_set_path( const char *path, void *d,rtsp_path_register register_fn )
|
||||
{
|
||||
spook_log (SL_DEBUG, "live set_path %s", path);
|
||||
|
||||
/*if( num_tokens == 2 )*/
|
||||
{
|
||||
new_rtsp_location( path, NULL, NULL, NULL,
|
||||
register_fn, d );
|
||||
return 0;
|
||||
}
|
||||
spook_log( SL_ERR, "rtsp-handler: syntax: Path <path> [<realm> <username> <password>]" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
void register_live_fn(struct rtsp_source *source,rtsp_creat creat,rtsp_release release,rtsp_play_fn play_fn)
|
||||
{
|
||||
source->creat = creat;
|
||||
source->release = release;
|
||||
source->play = play_fn;
|
||||
}
|
||||
66
sdk/app/spook/rtsp_common.h
Normal file
66
sdk/app/spook/rtsp_common.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef __RTSP_COMMON_H
|
||||
#define __RTSP_COMMON_H
|
||||
#include "spook_config.h"
|
||||
#include "frame.h"
|
||||
#include "encoder-audio.h"
|
||||
#include "encoder-jpeg.h"
|
||||
#include "rtp-audio.h"
|
||||
#include "rtp.h"
|
||||
#include "stream.h"
|
||||
#include "rtp_media.h"
|
||||
struct rtsp_session;
|
||||
struct rtsp_track;
|
||||
struct rtsp_source;
|
||||
|
||||
|
||||
typedef void (*rtsp_creat)(struct rtsp_source *source,void *priv );
|
||||
typedef void (*rtsp_release)(struct rtsp_source *source );
|
||||
typedef void (*rtsp_play_fn)(struct rtsp_source *source );
|
||||
typedef struct session *(*rtsp_path_register)( char *path, void *d );
|
||||
|
||||
|
||||
struct rtsp_session {
|
||||
struct rtsp_session *next;
|
||||
struct rtsp_session *prev;
|
||||
struct session *sess;
|
||||
struct rtsp_source *source;
|
||||
char path[64];
|
||||
int playing;
|
||||
int closed;
|
||||
};
|
||||
|
||||
struct rtsp_track {
|
||||
int index;
|
||||
struct rtsp_source *source;
|
||||
struct stream_destination *stream;
|
||||
int ready;
|
||||
struct rtp_media *rtp;
|
||||
};
|
||||
|
||||
|
||||
struct rtsp_source {
|
||||
struct rtsp_session *sess_list;
|
||||
struct rtsp_track track[MAX_TRACKS];
|
||||
struct os_task handle;
|
||||
void *signal;
|
||||
struct rtp_node live_node;
|
||||
int head_len; //保留头部的长度
|
||||
//创建线程以及资源的回调函数,参数就是source
|
||||
rtsp_creat creat;
|
||||
//释放线程以及资源的回调函数,参数就是source
|
||||
rtsp_release release;
|
||||
rtsp_play_fn play;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
|
||||
void *rtsp_start_block(void);
|
||||
int rtsp_end_block( void *d );
|
||||
int set_video_track( const char *name, void *d );
|
||||
int set_video_h264_track( const char *name, void *d );
|
||||
int set_audio_track( char *name, void *d );
|
||||
struct session *rtsp_open( char *path, void *d );
|
||||
void register_live_fn(struct rtsp_source *source,rtsp_creat creat,rtsp_release release,rtsp_play_fn play);
|
||||
int rtsp_set_path( const char *path, void *d,rtsp_path_register register_fn );
|
||||
|
||||
#endif
|
||||
251
sdk/app/spook/rtsp_h264_live.c
Normal file
251
sdk/app/spook/rtsp_h264_live.c
Normal file
@@ -0,0 +1,251 @@
|
||||
#include "sys_config.h"
|
||||
#include "tx_platform.h"
|
||||
#include <csi_kernel.h>
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
#include "lwip\api.h"
|
||||
#include "lwip\tcp.h"
|
||||
|
||||
#include "rtsp_common.h"
|
||||
#include "osal/string.h"
|
||||
#include "stream_define.h"
|
||||
#include "video_app/video_app.h"
|
||||
#include "video_app/video_app_h264_msi.h"
|
||||
#include "stream_frame.h"
|
||||
#include "log.h"
|
||||
#include "lib/video/dvp/jpeg/jpg.h"
|
||||
#include "frame.h"
|
||||
#include "audio_media_ctrl/audio_code_ctrl.h"
|
||||
#include "audio_msi/audio_adc.h"
|
||||
#include "app/video_app/file_thumb.h"
|
||||
|
||||
extern struct msi *mp4_demux_msi_init(const char *msi_name, const char *filename);
|
||||
extern struct msi *rtsp_msi_init(const char *name, uint8_t filter_type,uint8_t srcID);
|
||||
|
||||
extern void spook_send_thread_stream(struct rtsp_priv *r);
|
||||
static void self_thread(void *d)
|
||||
{
|
||||
struct rtsp_priv *r = (struct rtsp_priv *) d;
|
||||
spook_send_thread_stream(r);
|
||||
}
|
||||
// 创建实时预览的的线程
|
||||
static void self_creat(struct rtsp_source *source, void *priv)
|
||||
{
|
||||
source->priv = (struct rtsp_priv *) os_zalloc(sizeof(struct rtsp_priv));
|
||||
struct rtsp_priv *r = (struct rtsp_priv *) source->priv;
|
||||
uint8_t *path = (uint8_t *) priv;
|
||||
const char *match_path = (const char *) priv;
|
||||
uint8_t default_value = 0;
|
||||
AUENC_INIT auenc_init;
|
||||
os_printf("path:%s\n", path);
|
||||
|
||||
while((*match_path) && (*match_path != '?'))
|
||||
{
|
||||
match_path++;
|
||||
}
|
||||
|
||||
if(*match_path == '?')
|
||||
{
|
||||
match_path++;
|
||||
}
|
||||
|
||||
os_printf("match_path:%s\n",match_path);
|
||||
//匹配?的后缀(仅仅特定匹配)
|
||||
if(*match_path)
|
||||
{
|
||||
default_value = *match_path - '0';
|
||||
if(default_value > 1)
|
||||
{
|
||||
default_value = 0;
|
||||
}
|
||||
}
|
||||
os_printf("default_value:%d\n",default_value);
|
||||
uint8_t mp4_flag = 0;
|
||||
if (source->priv)
|
||||
{
|
||||
r->live_node = &source->live_node;
|
||||
if (memcmp(path, "/loop/RECA/", strlen("/loop/RECA/")) == 0)
|
||||
{
|
||||
uint8_t filepath[64] = {0};
|
||||
os_sprintf((char *) filepath, REC_PATH "/%s", path + strlen("/loop/RECA/"));
|
||||
// 尝试打开文件
|
||||
os_printf("filepath:%s\n", filepath);
|
||||
mp4_flag = 1;
|
||||
r->video_msi = mp4_demux_msi_init("demux_mp4", (char *) filepath);
|
||||
#if AUDIO_EN == 1
|
||||
if (r->video_msi)
|
||||
{
|
||||
r->a_msi = rtsp_audio_msi_init(R_RTP_AUDIO);
|
||||
msi_add_output(r->video_msi, NULL, R_RTP_AUDIO);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if AUDIO_EN == 1
|
||||
r->a_msi = rtsp_audio_msi_init(R_RTP_AUDIO);
|
||||
auenc_init.destroy_self = 0;
|
||||
auenc_init.src_msi = get_auadc_msi(AUSYS_AUAD);
|
||||
r->audio_msi = audio_encode_init(AAC_ENC, audio_adc_get_samplerate(AUSYS_AUAD), &auenc_init);
|
||||
if (r->audio_msi)
|
||||
{
|
||||
audio_code_add_output(r->audio_msi, R_RTP_AUDIO);
|
||||
}
|
||||
#endif
|
||||
r->video_msi = msi_find(AUTO_H264, 1);
|
||||
}
|
||||
if (r->video_msi)
|
||||
{
|
||||
if (mp4_flag)
|
||||
{
|
||||
r->v_msi = rtsp_msi_init(R_RTP_JPEG_H264, FSTYPE_H264_FILE,FRAMEBUFF_SOURCE_NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(default_value == 0)
|
||||
{
|
||||
#ifndef FORCE_SCALE_TO_H264
|
||||
r->v_msi = rtsp_msi_init(R_RTP_JPEG_H264, FSTYPE_H264_VPP_DATA0,FRAMEBUFF_SOURCE_CAMERA0);
|
||||
#else
|
||||
r->v_msi = rtsp_msi_init(R_RTP_JPEG_H264, FSTYPE_H264_SCALER_DATA,FRAMEBUFF_SOURCE_CAMERA0);
|
||||
#endif
|
||||
}
|
||||
else if(default_value == 1)
|
||||
{
|
||||
r->v_msi = rtsp_msi_init(R_RTP_JPEG_H264, FSTYPE_H264_GEN420_DATA,FRAMEBUFF_SOURCE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
// r->write_test_sd_msi = mp4_encode_msi_init("mp4_write");
|
||||
if (r->v_msi)
|
||||
{
|
||||
msi_add_output(r->video_msi, NULL, R_RTP_JPEG_H264); // 将video_msi的数据输出到R_RTP_JPEG_H264的msi,即v_msi
|
||||
msi_do_cmd(r->video_msi, MSI_CMD_VIDEO_DEMUX_CTRL, MSI_VIDEO_DEMUX_START, 0);
|
||||
OS_TASK_INIT("live_rtsp_h264", &source->handle, self_thread, r, OS_TASK_PRIORITY_NORMAL + 2, NULL, 1024);
|
||||
}
|
||||
}
|
||||
// 这里没有增加容错
|
||||
else
|
||||
{
|
||||
os_printf("%s h264 msi fail\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
os_printf("%s not enough space\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void self_destory(struct rtsp_source *source)
|
||||
{
|
||||
// stop_jpeg();
|
||||
void *tmp = source->handle.hdl;
|
||||
struct rtsp_priv *r = source->priv;
|
||||
if (r)
|
||||
{
|
||||
if (r->video_msi)
|
||||
{
|
||||
msi_del_output(r->video_msi, NULL, R_RTP_JPEG_H264);
|
||||
msi_put(r->video_msi);
|
||||
r->video_msi = NULL;
|
||||
}
|
||||
rtsp_msi_deinit((void *) r->v_msi);
|
||||
#if AUDIO_EN == 1
|
||||
if (r->audio_msi)
|
||||
{
|
||||
audio_code_del_output(r->audio_msi, R_RTP_AUDIO);
|
||||
audio_encode_deinit(r->audio_msi);
|
||||
}
|
||||
rtsp_audio_msi_deinit((void *) r->a_msi);
|
||||
#endif
|
||||
os_free(source->priv);
|
||||
source->priv = NULL;
|
||||
}
|
||||
if (tmp)
|
||||
{
|
||||
os_task_del(&source->handle);
|
||||
}
|
||||
}
|
||||
|
||||
static void self_play(struct rtsp_source *source)
|
||||
{
|
||||
struct rtsp_priv *r = (struct rtsp_priv *) source->priv;
|
||||
if (r->v_msi)
|
||||
{
|
||||
msi_do_cmd(r->video_msi, MSI_CMD_VIDEO_DEMUX_CTRL, MSI_VIDEO_DEMUX_START, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int self_get_sdp(struct session *s, char *dest, int *len, char *path)
|
||||
{
|
||||
struct rtsp_session *ls = (struct rtsp_session *) s->private;
|
||||
int i = 0;
|
||||
int t = 0;
|
||||
char *addr = "IP4 0.0.0.0";
|
||||
os_printf("%s:%d\tpath:%s\n", __FUNCTION__, __LINE__, path);
|
||||
|
||||
if (s->ep[0] && s->ep[0]->trans_type == RTP_TRANS_UDP)
|
||||
{
|
||||
addr = s->ep[0]->trans.udp.sdp_addr;
|
||||
}
|
||||
|
||||
i = snprintf(dest, *len, "v=0\r\no=- 1 1 IN IP4 127.0.0.1\r\ns=Test\r\na=type:broadcast\r\nt=0 0\r\nc=IN %s\r\n", addr);
|
||||
for (t = 0; t < MAX_TRACKS && ls->source->track[t].rtp; ++t)
|
||||
{
|
||||
int port;
|
||||
|
||||
if (s->ep[t] && s->ep[t]->trans_type == RTP_TRANS_UDP)
|
||||
{
|
||||
port = s->ep[t]->trans.udp.sdp_port;
|
||||
}
|
||||
else
|
||||
{
|
||||
port = 0;
|
||||
}
|
||||
|
||||
if (ls->source->track[t].rtp->type == 0)
|
||||
{
|
||||
i += ls->source->track[t].rtp->get_sdp(dest + i, *len - i, 96 + t, port, ls->source->track[t].rtp->private);
|
||||
}
|
||||
else
|
||||
{
|
||||
i += ls->source->track[t].rtp->get_sdp(dest + i, *len - i, 96 + t, port, NULL);
|
||||
}
|
||||
|
||||
if (port == 0) // XXX What's a better way to do this?
|
||||
{
|
||||
i += sprintf(dest + i, "a=control:track%d\r\n", t);
|
||||
}
|
||||
}
|
||||
*len = i;
|
||||
return t;
|
||||
}
|
||||
|
||||
static struct session *self_rtsp_open(char *path, void *d)
|
||||
{
|
||||
// 默认的,各自模式可以各自去修改
|
||||
struct session *sess = rtsp_open(path, d);
|
||||
if (sess)
|
||||
{
|
||||
sess->get_sdp = self_get_sdp;
|
||||
}
|
||||
return sess;
|
||||
}
|
||||
|
||||
void rtsp_h264_live_init(const rtp_name *rtsp)
|
||||
{
|
||||
struct rtsp_source *source;
|
||||
source = rtsp_start_block();
|
||||
rtsp_set_path(rtsp->path, source, self_rtsp_open);
|
||||
set_video_h264_track((char *) rtsp->video_encode_name, source);
|
||||
#if AUDIO_EN == 1
|
||||
set_audio_track((char *) rtsp->audio_encode_name, source);
|
||||
#endif
|
||||
register_live_fn(source, self_creat, self_destory, self_play);
|
||||
rtsp_end_block(source);
|
||||
return;
|
||||
}
|
||||
168
sdk/app/spook/rtsp_mjpeg_live.c
Normal file
168
sdk/app/spook/rtsp_mjpeg_live.c
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "sys_config.h"
|
||||
#include "tx_platform.h"
|
||||
#include <csi_kernel.h>
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
#include "lwip\api.h"
|
||||
#include "lwip\tcp.h"
|
||||
|
||||
#include "rtsp_common.h"
|
||||
#include "osal/string.h"
|
||||
#include "stream_define.h"
|
||||
#include "video_app/video_app.h"
|
||||
#include "stream_frame.h"
|
||||
#include "log.h"
|
||||
#include "lib/video/dvp/jpeg/jpg.h"
|
||||
#include "frame.h"
|
||||
#include "audio_media_ctrl/audio_code_ctrl.h"
|
||||
#include "audio_msi/audio_adc.h"
|
||||
#include "jpg_concat_msi.h"
|
||||
#include "gen420_hardware_msi.h"
|
||||
|
||||
extern void spook_send_thread_stream(struct rtsp_priv *r);
|
||||
static void self_thread(void *d)
|
||||
{
|
||||
struct rtsp_priv *r = (struct rtsp_priv*)d;
|
||||
spook_send_thread_stream(r);
|
||||
}
|
||||
//创建实时预览的的线程
|
||||
static void self_creat(struct rtsp_source *source,void *priv)
|
||||
{
|
||||
source->priv = (struct rtsp_priv*)os_zalloc(sizeof(struct rtsp_priv));
|
||||
struct rtsp_priv *r = (struct rtsp_priv*)source->priv;
|
||||
AUENC_INIT auenc_init;
|
||||
if(source->priv)
|
||||
{
|
||||
r->live_node = &source->live_node;
|
||||
r->video_msi = msi_find(AUTO_JPG, 1); //jpg_concat_msi_init_start(JPGID0,1280, 720, NULL, VPP_DATA0,1);
|
||||
if(r->video_msi)
|
||||
{
|
||||
r->v_msi = rtsp_msi_init(R_RTP_JPEG,~0,0);
|
||||
#if AUDIO_EN == 1
|
||||
r->a_msi = rtsp_audio_msi_init(R_RTP_AUDIO2);
|
||||
auenc_init.destroy_self = 0;
|
||||
auenc_init.src_msi = get_auadc_msi(AUSYS_AUAD);
|
||||
r->audio_msi = audio_encode_init(AAC_ENC, audio_adc_get_samplerate(AUSYS_AUAD), &auenc_init);
|
||||
if(r->audio_msi) {
|
||||
audio_code_add_output(r->audio_msi, R_RTP_AUDIO2);
|
||||
}
|
||||
#endif
|
||||
//r->write_test_sd_msi = mp4_encode_msi_init("mp4_write");
|
||||
if(r->v_msi)
|
||||
{
|
||||
msi_add_output(r->video_msi, NULL, R_RTP_JPEG);
|
||||
OS_TASK_INIT("live_rtsp_mjpeg", &source->handle, self_thread, r, OS_TASK_PRIORITY_NORMAL + 2, NULL, 1024);
|
||||
}
|
||||
}
|
||||
//这里没有增加容错
|
||||
else
|
||||
{
|
||||
os_printf("%s jpg_concat_msi_init_start fail\n",__FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
os_printf("%s not enough space\n",__FUNCTION__);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void self_destory(struct rtsp_source *source)
|
||||
{
|
||||
//stop_jpeg();
|
||||
void *tmp = source->handle.hdl;
|
||||
struct rtsp_priv *r = source->priv;
|
||||
if(r)
|
||||
{
|
||||
if(r->video_msi)
|
||||
{
|
||||
msi_del_output(r->video_msi,NULL,R_RTP_JPEG);
|
||||
//msi_destroy(r->video_msi);
|
||||
msi_put(r->video_msi);
|
||||
r->video_msi = NULL;
|
||||
}
|
||||
rtsp_msi_deinit((void*)r->v_msi);
|
||||
#if AUDIO_EN == 1
|
||||
if(r->audio_msi) {
|
||||
audio_code_del_output(r->audio_msi, R_RTP_AUDIO2);
|
||||
audio_encode_deinit(r->audio_msi);
|
||||
}
|
||||
rtsp_audio_msi_deinit((void*)r->a_msi);
|
||||
#endif
|
||||
os_free(source->priv);
|
||||
source->priv = NULL;
|
||||
}
|
||||
if(tmp)
|
||||
{
|
||||
os_task_del(&source->handle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int self_get_sdp( struct session *s, char *dest, int *len,
|
||||
char *path )
|
||||
{
|
||||
struct rtsp_session *ls = (struct rtsp_session *)s->private;
|
||||
int i = 0;
|
||||
int t = 0;
|
||||
char *addr = "IP4 0.0.0.0";
|
||||
os_printf("%s:%d\tpath:%s\n",__FUNCTION__,__LINE__,path);
|
||||
|
||||
if( s->ep[0] && s->ep[0]->trans_type == RTP_TRANS_UDP )
|
||||
addr = s->ep[0]->trans.udp.sdp_addr;
|
||||
|
||||
i = snprintf( dest, *len,"v=0\r\no=- 1 1 IN IP4 127.0.0.1\r\ns=Test\r\na=type:broadcast\r\nt=0 0\r\nc=IN %s\r\n", addr );
|
||||
for( t = 0; t < MAX_TRACKS && ls->source->track[t].rtp; ++t )
|
||||
{
|
||||
int port;
|
||||
|
||||
if( s->ep[t] && s->ep[t]->trans_type == RTP_TRANS_UDP )
|
||||
port = s->ep[t]->trans.udp.sdp_port;
|
||||
else
|
||||
port = 0;
|
||||
|
||||
if(ls->source->track[t].rtp->type == 0)
|
||||
{
|
||||
i += ls->source->track[t].rtp->get_sdp( dest + i, *len - i,96 + t, port,ls->source->track[t].rtp->private );
|
||||
}
|
||||
else
|
||||
{
|
||||
i += ls->source->track[t].rtp->get_sdp( dest + i, *len - i,96 + t, port,NULL);
|
||||
}
|
||||
|
||||
if( port == 0 ) // XXX What's a better way to do this?
|
||||
i += sprintf( dest + i, "a=control:track%d\r\n", t );
|
||||
}
|
||||
*len = i;
|
||||
return t;
|
||||
}
|
||||
|
||||
static struct session *self_rtsp_open( char *path, void *d )
|
||||
{
|
||||
//默认的,各自模式可以各自去修改
|
||||
struct session *sess = rtsp_open(path,d);
|
||||
if(sess)
|
||||
{
|
||||
sess->get_sdp = self_get_sdp;
|
||||
}
|
||||
return sess;
|
||||
}
|
||||
|
||||
void rtsp_mjpeg_live_init(const rtp_name *rtsp)
|
||||
{
|
||||
struct rtsp_source *source;
|
||||
source = rtsp_start_block();
|
||||
rtsp_set_path( rtsp->path, source,self_rtsp_open );
|
||||
set_video_track( (char*)rtsp->video_encode_name, source );
|
||||
#if AUDIO_EN == 1
|
||||
set_audio_track( (char*)rtsp->audio_encode_name, source );
|
||||
#endif
|
||||
register_live_fn(source,self_creat,self_destory,NULL);
|
||||
rtsp_end_block(source);
|
||||
return ;
|
||||
}
|
||||
134
sdk/app/spook/session.c
Normal file
134
sdk/app/spook/session.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
//#include "sys_config.h"
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
#include "lwip\api.h"
|
||||
|
||||
#include "lwip\tcp.h"
|
||||
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
#include <rtp.h>
|
||||
#include <rtp_media.h>
|
||||
|
||||
static struct session *sess_list = NULL;
|
||||
|
||||
struct rtp_media *new_rtp_media( rtp_media_get_sdp_func get_sdp,
|
||||
rtp_media_get_payload_func get_payload, rtp_media_frame_func frame,
|
||||
rtp_media_send_func send, void *private )
|
||||
{
|
||||
struct rtp_media *m;
|
||||
|
||||
if( ! ( m = (struct rtp_media *)malloc( sizeof( *m ) ) ) )
|
||||
return NULL;
|
||||
m->get_sdp = get_sdp;
|
||||
m->get_payload = get_payload;
|
||||
m->frame = frame;
|
||||
m->send = send;
|
||||
m->private = private;
|
||||
m->rtcp_send = NULL;
|
||||
return m;
|
||||
}
|
||||
|
||||
struct rtp_media *new_rtp_rtcp_media( rtp_media_get_sdp_func get_sdp,
|
||||
rtp_media_get_payload_func get_payload, rtp_media_frame_func frame,
|
||||
rtp_media_send_func send,rtp_media_send_func rtcp_send, void *private )
|
||||
{
|
||||
struct rtp_media *m;
|
||||
|
||||
if( ! ( m = (struct rtp_media *)malloc( sizeof( *m ) ) ) )
|
||||
return NULL;
|
||||
m->get_sdp = get_sdp;
|
||||
m->get_payload = get_payload;
|
||||
m->frame = frame;
|
||||
m->send = send;
|
||||
m->rtcp_send = rtcp_send;
|
||||
m->private = private;
|
||||
return m;
|
||||
}
|
||||
|
||||
void del_session( struct session *sess )
|
||||
{
|
||||
if( sess->next ) sess->next->prev = sess->prev;
|
||||
if( sess->prev ) sess->prev->next = sess->next;
|
||||
else sess_list = sess->next;
|
||||
if( sess->control_close ) sess->control_close( sess ); /* callback: rtsp_session_close */
|
||||
if(sess->conn)
|
||||
{
|
||||
//有点绕,判断conn是否正常,正常情况下,两个sess是一样的,则清空,说明这个不是从do_read去关闭,是异常关闭
|
||||
_os_printf("%s:%d had conn\n",__FUNCTION__,__LINE__);
|
||||
if(sess->conn->sess == sess)
|
||||
{
|
||||
sess->conn->sess = NULL;
|
||||
}
|
||||
}
|
||||
_os_printf("%s session:%X\n",__FUNCTION__,(int)sess);
|
||||
|
||||
free( sess );
|
||||
}
|
||||
|
||||
void resetnet_delsession(void)
|
||||
{
|
||||
struct session *sess;
|
||||
for(sess = sess_list; sess != NULL; sess = sess->next) {
|
||||
sess->conn->sess = NULL;
|
||||
sess->teardown( sess, NULL );//live_teardown
|
||||
}
|
||||
}
|
||||
|
||||
struct session *new_session(void)
|
||||
{
|
||||
struct session *sess;
|
||||
int i;
|
||||
|
||||
sess = (struct session *)malloc( sizeof( struct session ) );
|
||||
sess->next = sess_list;
|
||||
sess->prev = NULL;
|
||||
if( sess->next ) sess->next->prev = sess;
|
||||
sess_list = sess;
|
||||
sess->get_sdp = NULL;
|
||||
sess->setup = NULL;
|
||||
sess->play = NULL;
|
||||
sess->pause = NULL;
|
||||
sess->private = NULL;
|
||||
sess->control_private = NULL;
|
||||
sess->control_close = NULL;
|
||||
sess->conn = NULL;
|
||||
gettimeofday( &sess->open_time, NULL );
|
||||
strcpy( sess->addr, "(unconnected)" );
|
||||
for( i = 0; i < MAX_TRACKS; ++i ) sess->ep[i] = NULL;
|
||||
return sess;
|
||||
}
|
||||
|
||||
int print_session_list( char *s, int maxlen )
|
||||
{
|
||||
struct session *sess;
|
||||
int i = 0;
|
||||
|
||||
for( sess = sess_list; sess; sess = sess->next )
|
||||
i += sprintf( s + i, "%s %ld\n",
|
||||
sess->addr, sess->open_time.tv_sec );
|
||||
return i;
|
||||
}
|
||||
1
sdk/app/spook/session.h
Normal file
1
sdk/app/spook/session.h
Normal file
@@ -0,0 +1 @@
|
||||
#ifndef __SESSION_H
|
||||
145
sdk/app/spook/spook.c
Normal file
145
sdk/app/spook/spook.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
|
||||
#include "lwip\api.h"
|
||||
|
||||
#include "lwip\tcp.h"
|
||||
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
#include <rtp.h>
|
||||
#include "spook_config.h"
|
||||
#include "spook.h"
|
||||
#include "sys_config.h"
|
||||
#include "encoder-audio.h"
|
||||
#include "encoder-jpeg.h"
|
||||
|
||||
|
||||
unsigned long random_key;
|
||||
static int init_random(void)
|
||||
{
|
||||
random_key = 0x12345678;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t get_random(void)
|
||||
{
|
||||
return random_key;
|
||||
}
|
||||
|
||||
void random_bytes( unsigned char *dest, int len )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < len; ++i )
|
||||
dest[i] = (random_key++) & 0xff;
|
||||
}
|
||||
|
||||
void random_id( unsigned char *dest, int len )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < len / 2; ++i )
|
||||
sprintf( (char *)(dest + i * 2), "%02X",
|
||||
(unsigned int)( (random_key++) & 0xff ) );
|
||||
dest[len] = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void global_init(void)
|
||||
{
|
||||
config_port(SPOOK_PORT);
|
||||
}
|
||||
extern void spook_send_thread(void *d);
|
||||
|
||||
|
||||
|
||||
extern int web_init(void);
|
||||
extern int live_init(const rtp_name *jpg_name);
|
||||
|
||||
const rtp_name live_dvp = {
|
||||
.video_encode_name = JPG_ENCODER_NAME,
|
||||
.audio_encode_name = AUDIO_AAC_ENCODER_NAME,
|
||||
.path = "/webcam",
|
||||
};
|
||||
|
||||
|
||||
const rtp_name live_h264 = {
|
||||
.video_encode_name = H264_ENCODER_NAME,
|
||||
.audio_encode_name = AUDIO_AAC_ENCODER_NAME2,
|
||||
.path = "/h264",
|
||||
|
||||
};
|
||||
|
||||
|
||||
const rtp_name live_h264_2 = {
|
||||
.video_encode_name = H264_ENCODER_NAME,
|
||||
.audio_encode_name = AUDIO_AAC_ENCODER_NAME,
|
||||
.path = "/loop/RECA/",
|
||||
};
|
||||
|
||||
const rtp_name live_custom = {
|
||||
.video_encode_name = JPG_ENCODER_NAME,
|
||||
.audio_encode_name = AUDIO_AAC_ENCODER_NAME,
|
||||
.path = "/custom",
|
||||
};
|
||||
|
||||
|
||||
|
||||
extern void live_rtsp_init(const rtp_name *rtsp);
|
||||
extern void live_h264_rtsp_init(const rtp_name *rtsp);
|
||||
extern void webfile_rtsp_init(const rtp_name *rtsp);
|
||||
extern void rtsp_mjpeg_live_init(const rtp_name *rtsp);
|
||||
extern void rtsp_h264_live_init(const rtp_name *rtsp);
|
||||
|
||||
void *jpeg_encode_init(const char *encode_name);
|
||||
void *h264_encode_init(const char *encode_name);
|
||||
void *rtsp_audio_encode_init(const char *encode_name);
|
||||
extern void custom_rtsp_jpeg_init(const rtp_name *rtsp);
|
||||
static void spook_thread(void *d)
|
||||
{
|
||||
init_random();
|
||||
global_init();
|
||||
|
||||
jpeg_encode_init(JPG_ENCODER_NAME);
|
||||
h264_encode_init(H264_ENCODER_NAME);
|
||||
rtsp_audio_encode_init(AUDIO_AAC_ENCODER_NAME);
|
||||
rtsp_audio_encode_init(AUDIO_AAC_ENCODER_NAME2);
|
||||
|
||||
rtsp_mjpeg_live_init(&live_dvp);
|
||||
rtsp_h264_live_init(&live_h264);
|
||||
custom_rtsp_jpeg_init(&live_custom);
|
||||
rtsp_h264_live_init(&live_h264_2);
|
||||
//webfile_rtsp_init(&webfile);
|
||||
}
|
||||
|
||||
void spook_init(void)
|
||||
{
|
||||
//thread_create(spook_thread, 0, TASK_SPOOK_PRIO, 0, 110*1024, "spook");
|
||||
spook_thread(0);
|
||||
}
|
||||
|
||||
|
||||
10
sdk/app/spook/spook.h
Normal file
10
sdk/app/spook/spook.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __SPOOK_H__
|
||||
#define __SPOOK_H__
|
||||
#include "spook_config.h"
|
||||
int config_port( int port );
|
||||
int live_init(const rtp_name *jpg_name);
|
||||
void spook_init(void);
|
||||
void * jpeg_init(const rtp_name *jpg_name);
|
||||
|
||||
#endif
|
||||
|
||||
73
sdk/app/spook/spook_config.h
Normal file
73
sdk/app/spook/spook_config.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef __SPOOK_CONFGIG_H__
|
||||
#define __SPOOK_CONFGIG_H__
|
||||
|
||||
//#include <board_config.h>
|
||||
|
||||
#define FRAME_HEAP 20
|
||||
#define SPOOK_MAX_FRAME_SIZE 0/*352*288*3*/
|
||||
#define SPOOK_PORT 554//7070//
|
||||
|
||||
//#define JPEG_ENCODER_NAME "jpeg_dvp"
|
||||
//#define JPEG_ENCODER2_NAME "jpeg_usb"
|
||||
|
||||
#define FILE_JPEG_ENCODER_NAME "jpeg_file"
|
||||
|
||||
#define FILE_H264_ENCODER_NAME "h264_file"
|
||||
|
||||
|
||||
#define JPEG_FRAMEINC 1
|
||||
#define H264_FRAMEINC 1
|
||||
|
||||
#define AUDIO_ENCODER_NAME "audio"
|
||||
#define FILE_AUDIO_ENCODER_NAME "audio"
|
||||
|
||||
|
||||
#define EXCHANGER_SLOT_SIZE 16
|
||||
|
||||
//#define RTSP_LIVE_PATH "/webcam" /* /webcam */
|
||||
//#define RTSP_LIVE_USB_PATH "/webcam2" /* /webcam */
|
||||
#define RTSP_WEB_PATH "/file" /* /webcam */
|
||||
|
||||
|
||||
//#define RTSP_LIVE_TRACK "jpeg_dvp"
|
||||
//#define RTSP_LIVE2_TRACK "jpeg_usb"
|
||||
#define RTSP_WEBFILE_TRACK "jpeg_file"
|
||||
|
||||
|
||||
#define RTSP_LIVE_AUDIO_TRACK "audio"
|
||||
#define RTSP_WEBFILE_AUDIO_TRACK "audio_file"
|
||||
|
||||
|
||||
|
||||
#define HTTP_PATTH "/webcam" /* /webcam */
|
||||
#define HTTP_INPUT_NAME "jpeg"
|
||||
#define HTTP_MODE "stream"
|
||||
|
||||
|
||||
#define JPEG_FIFO_SIZE 3
|
||||
|
||||
#define SCAN_DATA_OFFSET 0x253//0X265
|
||||
|
||||
#define MAX_DATA_PACKET_SIZE 1440
|
||||
|
||||
#define SPOOK_CACHE_BUF_LEN 1600
|
||||
#define SPOOK_CACHE_BUF_HEAD_LEN (4)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *video_encode_name; //视频编码的名称
|
||||
const char *audio_encode_name; //音频编码的名称
|
||||
const char* path;
|
||||
}rtp_name;
|
||||
|
||||
|
||||
#define H264_ENCODER_NAME "h264_encoder"
|
||||
#define JPG_ENCODER_NAME "jpg_encoder"
|
||||
#define AUDIO_AAC_ENCODER_NAME "audio_aac_encoder"
|
||||
#define AUDIO_AAC_ENCODER_NAME2 "audio_aac_encoder2"
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
160
sdk/app/spook/stream.c
Normal file
160
sdk/app/spook/stream.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
|
||||
struct converter {
|
||||
struct stream_destination *input;
|
||||
struct stream *output;
|
||||
};
|
||||
|
||||
static struct stream *stream_list = NULL;
|
||||
/*
|
||||
static void get_framerate( struct stream *s, int *fincr, int *fbase )
|
||||
{
|
||||
struct stream_destination *dest =
|
||||
(struct stream_destination *)s->private;
|
||||
|
||||
dest->stream->get_framerate( dest->stream, fincr, fbase );
|
||||
}
|
||||
|
||||
static void set_running( struct stream *s, int running )
|
||||
{
|
||||
set_waiting( (struct stream_destination *)s->private, running );
|
||||
}
|
||||
*/
|
||||
void deliver_frame_to_stream( struct frame *f, void *d )
|
||||
{
|
||||
struct stream *s = (struct stream *)d; /* d: jpeg_encoder->output */
|
||||
struct stream_destination *dest;
|
||||
|
||||
for( dest = s->dest_list; dest; dest = dest->next )
|
||||
{
|
||||
/* destination is waitint for frame ?*/
|
||||
if( ! dest->waiting ) {
|
||||
if(dest->disconnect_frame){
|
||||
dest->disconnect_frame( f, dest->d );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/*rtsp callback: next_live_frame, dest->d: source->track[t]*/
|
||||
/*http callback: jpeg_next_frame, dest->d: source->track[t]*/
|
||||
dest->process_frame( f, dest->d );
|
||||
if(dest->disconnect_frame){
|
||||
dest->disconnect_frame( f, dest->d );
|
||||
}
|
||||
}
|
||||
unref_frame( f );
|
||||
}
|
||||
|
||||
static struct stream_destination *new_dest( struct stream *s,
|
||||
frame_deliver_func process_frame, void *d )
|
||||
{
|
||||
struct stream_destination *dest;
|
||||
|
||||
dest = (struct stream_destination *)
|
||||
malloc( sizeof( struct stream_destination ) );
|
||||
dest->next = s->dest_list;
|
||||
dest->prev = NULL;
|
||||
if( dest->next ) dest->next->prev = dest;
|
||||
s->dest_list = dest;
|
||||
dest->stream = s;
|
||||
dest->waiting = 0;
|
||||
dest->process_frame = process_frame;
|
||||
dest->disconnect_frame = NULL;
|
||||
dest->d = d;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
void disconnect_stream(struct stream_destination *dest,frame_disconnect_func disconnect_frame){
|
||||
dest->disconnect_frame = disconnect_frame;
|
||||
}
|
||||
|
||||
struct stream_destination *connect_to_stream( const char *name,
|
||||
frame_deliver_func process_frame, void *d )
|
||||
{
|
||||
struct stream *s;
|
||||
int found_one = 0;
|
||||
|
||||
for( s = stream_list; s; s = s->next )
|
||||
if( ! strcmp( s->name, name ) )
|
||||
return new_dest( s, process_frame, d );
|
||||
if( found_one )
|
||||
spook_log( SL_ERR, "unable to convert stream %s", name );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void del_stream( struct stream *s )
|
||||
{
|
||||
if( s->next ) s->next->prev = s->prev;
|
||||
if( s->prev ) s->prev->next = s->next;
|
||||
else stream_list = s->next;
|
||||
free( s );
|
||||
}
|
||||
|
||||
struct stream *new_stream( const char *name, int format, void *d )
|
||||
{
|
||||
struct stream *s;
|
||||
|
||||
s = (struct stream *)malloc( sizeof( struct stream ) );
|
||||
|
||||
s->next = stream_list;
|
||||
s->prev = NULL;
|
||||
if( s->next ) s->next->prev = s;
|
||||
stream_list = s;
|
||||
s->name = name;
|
||||
s->format = format;
|
||||
s->dest_list = NULL;
|
||||
s->get_framerate = NULL;
|
||||
s->set_running = NULL;
|
||||
s->private = d;
|
||||
return s;
|
||||
}
|
||||
|
||||
void set_waiting( struct stream_destination *dest, int waiting )
|
||||
{
|
||||
struct stream *s = dest->stream;
|
||||
|
||||
/* We call set_running every time a destination starts listening,
|
||||
* or when the last destination stops listening. It is good to know
|
||||
* when new listeners come so maybe the source can send a keyframe. */
|
||||
|
||||
if( dest->waiting )
|
||||
{
|
||||
if( waiting ) return; /* no change in status */
|
||||
dest->waiting = 0;
|
||||
|
||||
/* see if anybody else is listening */
|
||||
for( dest = s->dest_list; dest; dest = dest->next )
|
||||
waiting |= dest->waiting;
|
||||
if( waiting ) return; /* others are still listening */
|
||||
} else
|
||||
{
|
||||
if( ! waiting ) return; /* no change in status */
|
||||
dest->waiting = 1;
|
||||
}
|
||||
|
||||
s->set_running( s, waiting );
|
||||
}
|
||||
52
sdk/app/spook/stream.h
Normal file
52
sdk/app/spook/stream.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __STREAM_H
|
||||
#define __STREAM_H
|
||||
#include "frame.h"
|
||||
|
||||
struct stream_destination {
|
||||
struct stream_destination *next;
|
||||
struct stream_destination *prev;
|
||||
struct stream *stream;
|
||||
int waiting;
|
||||
frame_deliver_func process_frame;
|
||||
frame_disconnect_func disconnect_frame;
|
||||
void *d;
|
||||
};
|
||||
|
||||
struct stream {
|
||||
struct stream *next;
|
||||
struct stream *prev;
|
||||
const char *name;
|
||||
int format;
|
||||
struct stream_destination *dest_list;
|
||||
void (*get_framerate)( struct stream *s, int *fincr, int *fbase );
|
||||
void (*set_running)( struct stream *s, int running );
|
||||
void *private;
|
||||
};
|
||||
|
||||
struct stream *new_stream( const char *name, int format, void *d );
|
||||
void deliver_frame_to_stream( struct frame *f, void *d );
|
||||
struct stream_destination *connect_to_stream( const char *name,
|
||||
frame_deliver_func process_frame, void *d );
|
||||
void del_stream( struct stream *s );
|
||||
void set_waiting( struct stream_destination *dest, int waiting );
|
||||
void disconnect_stream(struct stream_destination *dest,frame_disconnect_func disconnect_frame);
|
||||
|
||||
|
||||
#endif
|
||||
722
sdk/app/spook/tcp.c
Normal file
722
sdk/app/spook/tcp.c
Normal file
@@ -0,0 +1,722 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
// #include "lwip\fcntl.h"
|
||||
#include "lwip\api.h"
|
||||
|
||||
#include "typesdef.h"
|
||||
// #include <linux/linux_mutex.h>
|
||||
#include <event.h>
|
||||
#include <log.h>
|
||||
#include <frame.h>
|
||||
#include <stream.h>
|
||||
#include <pmsg.h>
|
||||
#include <rtp.h>
|
||||
#include "lwip/api.h"
|
||||
// #include "leases.h"
|
||||
// #include "dhcpd.h"
|
||||
|
||||
static int base64[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20 */
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, /* 30 */
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40 */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50 */
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60 */
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* F0 */
|
||||
};
|
||||
|
||||
void write_access_log(char *path, struct sockaddr *addr, int code, char *req, int length, char *referer, char *user_agent);
|
||||
|
||||
struct listener
|
||||
{
|
||||
int fd;
|
||||
};
|
||||
|
||||
/* all of tcp connetctions loop linked list */
|
||||
static struct conn *conn_list = NULL;
|
||||
|
||||
static void do_read(void *ei, void *d);
|
||||
|
||||
void drop_conn(struct conn *c)
|
||||
{
|
||||
if (c->read_event)
|
||||
{
|
||||
eloop_remove_event(c->read_event);
|
||||
c->read_event = NULL;
|
||||
}
|
||||
|
||||
if (c->write_event)
|
||||
{
|
||||
eloop_remove_event(c->write_event);
|
||||
c->write_event = NULL;
|
||||
}
|
||||
if (c->fd >= 0)
|
||||
{
|
||||
close(c->fd);
|
||||
}
|
||||
c->fd = -1;
|
||||
os_event_del(&c->evt);
|
||||
if (c->next)
|
||||
{
|
||||
c->next->prev = c->prev;
|
||||
}
|
||||
if (c->prev)
|
||||
{
|
||||
c->prev->next = c->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
conn_list = c->next;
|
||||
}
|
||||
|
||||
if (c->proto_state)
|
||||
{
|
||||
free(c->proto_state);
|
||||
c->proto_state = NULL;
|
||||
}
|
||||
free(c);
|
||||
_os_printf("%s finish\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
static void conn_write(void *ei, void *d)
|
||||
{
|
||||
spook_log(SL_DEBUG, "tcp_w");
|
||||
struct conn *c = (struct conn *) d;
|
||||
int ret, len;
|
||||
struct session *sess;
|
||||
|
||||
while (c->send_buf_r != c->send_buf_w)
|
||||
{
|
||||
if (c->send_buf_w < c->send_buf_r)
|
||||
{
|
||||
len = sizeof(c->send_buf) - c->send_buf_r;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = c->send_buf_w - c->send_buf_r;
|
||||
}
|
||||
|
||||
// 等待1ms,没有完成就退出,等待下一次
|
||||
int evt_err = os_event_wait(&c->evt, RTSP_TCP_READ_MUTEX|RTSP_TCP_WRITE_MUTEX, NULL, OS_EVENT_WMODE_AND | OS_EVENT_WMODE_CLEAR, 1);
|
||||
if (!evt_err)
|
||||
{
|
||||
ret = write(c->fd, c->send_buf + c->send_buf_r, len);
|
||||
os_event_set(&c->evt, RTSP_TCP_READ_MUTEX | RTSP_TCP_WRITE_MUTEX, NULL);
|
||||
}
|
||||
//没有成功,则直接退出,下次重新尝试发送
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret <= 0)
|
||||
{
|
||||
if (ret < 0 && get_errno() == EAGAIN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// drop_conn( c );
|
||||
|
||||
goto conn_write_exit;
|
||||
}
|
||||
c->send_buf_r += ret;
|
||||
if (c->send_buf_r == sizeof(c->send_buf))
|
||||
{
|
||||
c->send_buf_r = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (c->drop_after)
|
||||
{
|
||||
goto conn_write_exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
eloop_set_event_enabled(c->write_event, 0);
|
||||
}
|
||||
|
||||
// 缓冲buf已经发送完成,所以将读和写重置(这种设置方式,不能存在异步,所以只能全部在event_loop,event_loop相当于单线程)
|
||||
c->send_buf_r = 0;
|
||||
c->send_buf_w = 0;
|
||||
return;
|
||||
conn_write_exit:
|
||||
sess = c->sess;
|
||||
_os_printf("close sess:%x\r\n", (int) sess);
|
||||
if (sess)
|
||||
{
|
||||
// sess->conn = NULL;
|
||||
_os_printf("close sess->close:%x\r\n", (int) sess->closed);
|
||||
sess->closed(sess, NULL);
|
||||
}
|
||||
|
||||
// 关闭自己的事件和socket
|
||||
if (c->read_event)
|
||||
{
|
||||
eloop_remove_event(c->read_event);
|
||||
c->read_event = NULL;
|
||||
}
|
||||
if (c->write_event)
|
||||
{
|
||||
eloop_remove_event(c->write_event);
|
||||
c->write_event = NULL;
|
||||
}
|
||||
|
||||
if (c->fd >= 0)
|
||||
{
|
||||
close(c->fd);
|
||||
c->fd = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int avail_send_buf(struct conn *c)
|
||||
{
|
||||
if (c->send_buf_r > c->send_buf_w)
|
||||
{
|
||||
return c->send_buf_r - c->send_buf_w - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return sizeof(c->send_buf) - c->send_buf_w + c->send_buf_r - 1;
|
||||
}
|
||||
}
|
||||
|
||||
int send_data(struct conn *c, unsigned char *d, int len)
|
||||
{
|
||||
if (avail_send_buf(c) < len)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (--len >= 0)
|
||||
{
|
||||
c->send_buf[c->send_buf_w++] = *(d++);
|
||||
if (c->send_buf_w == sizeof(c->send_buf))
|
||||
{
|
||||
c->send_buf_w = 0;
|
||||
}
|
||||
}
|
||||
|
||||
eloop_set_event_enabled(c->write_event, EVENT_F_ENABLED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_send_pmsg(struct conn *c, struct pmsg *msg, int len)
|
||||
{
|
||||
char buf[512];
|
||||
int i, f, totlen;
|
||||
|
||||
if (msg->type != PMSG_RESP)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
i = sprintf(buf, "%s %d %s\r\n", msg->proto_id, msg->sl.stat.code, msg->sl.stat.reason);
|
||||
for (f = 0; f < msg->header_count; ++f)
|
||||
{
|
||||
i += sprintf(buf + i, "%s: %s\r\n", msg->fields[f].name, msg->fields[f].value);
|
||||
}
|
||||
if (len >= 0)
|
||||
{
|
||||
i += sprintf(buf + i, "Content-Length: %d\r\n\r\n", len);
|
||||
totlen = i + len;
|
||||
}
|
||||
else
|
||||
{
|
||||
i += sprintf(buf + i, "\r\n");
|
||||
totlen = i;
|
||||
}
|
||||
if (avail_send_buf(c) < totlen)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
_os_printf("%s\r\n", buf);
|
||||
send_data(c, (unsigned char *) buf, i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void log_request(struct req *req, int code, int length)
|
||||
{
|
||||
char *ref, *ua;
|
||||
|
||||
ref = get_header(req->req, "referer");
|
||||
ua = get_header(req->req, "user-agent");
|
||||
write_access_log(NULL, (struct sockaddr *) &req->conn->client_addr, code, (char *) req->conn->req_buf, length, ref ? ref : "-", ua ? ua : "-");
|
||||
}
|
||||
|
||||
static int handle_unknown(struct req *req)
|
||||
{
|
||||
log_request(req, 501, 0);
|
||||
req->conn->drop_after = 1;
|
||||
req->resp = new_pmsg(256);
|
||||
req->resp->type = PMSG_RESP;
|
||||
req->resp->proto_id = add_pmsg_string(req->resp, req->req->proto_id);
|
||||
req->resp->sl.stat.code = 501;
|
||||
req->resp->sl.stat.reason = add_pmsg_string(req->resp, "Not Implemented");
|
||||
copy_headers(req->resp, req->req, "CSeq");
|
||||
tcp_send_pmsg(req->conn, req->resp, -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_request(struct conn *c)
|
||||
{
|
||||
int ret;
|
||||
struct req *req;
|
||||
|
||||
if (c->req_buf[0] == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((req = malloc(sizeof(struct req))) == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
req->conn = c;
|
||||
req->resp = NULL;
|
||||
req->req = new_pmsg(c->req_len);
|
||||
req->req->msg_len = c->req_len;
|
||||
memcpy(req->req->msg, c->req_buf, req->req->msg_len);
|
||||
if (parse_pmsg(req->req) < 0)
|
||||
{
|
||||
free_pmsg(req->req);
|
||||
free(req);
|
||||
return -1;
|
||||
}
|
||||
spook_log(SL_VERBOSE, "client request: '%s' '%s' '%s'", req->req->sl.req.method, req->req->sl.req.uri, req->req->proto_id);
|
||||
|
||||
switch (c->proto)
|
||||
{
|
||||
case CONN_PROTO_RTSP:
|
||||
ret = rtsp_handle_msg(req);
|
||||
break;
|
||||
default:
|
||||
ret = handle_unknown(req);
|
||||
break;
|
||||
}
|
||||
if (ret <= 0)
|
||||
{
|
||||
free_pmsg(req->req);
|
||||
if (req->resp)
|
||||
{
|
||||
free_pmsg(req->resp);
|
||||
}
|
||||
free(req);
|
||||
}
|
||||
return ret < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
static int parse_client_data(struct conn *c)
|
||||
{
|
||||
char *a, *b;
|
||||
|
||||
switch (c->proto)
|
||||
{
|
||||
case CONN_PROTO_START:
|
||||
if ((a = strchr((const char *) c->req_buf, '\n')))
|
||||
{
|
||||
*a = 0;
|
||||
if (!(b = strrchr((const char *) c->req_buf, ' ')))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (!strncmp(b + 1, "HTTP/", 5))
|
||||
{
|
||||
c->proto = CONN_PROTO_HTTP;
|
||||
}
|
||||
else if (!strncmp(b + 1, "RTSP/", 5))
|
||||
{
|
||||
c->proto = CONN_PROTO_RTSP;
|
||||
}
|
||||
else if (!strncmp(b + 1, "APPO/", 5))
|
||||
{
|
||||
c->proto = CONN_PROTO_APPO;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
*a = '\n';
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case CONN_PROTO_RTSP:
|
||||
if (c->req_len < 4)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (c->req_buf[0] == '$')
|
||||
{
|
||||
spook_log(SL_DEBUG, "interleave_recv\n");
|
||||
if (c->req_len < GET_16(c->req_buf + 2) + 4)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#if 0
|
||||
interleave_recv( c, c->req_buf[1], c->req_buf + 4,
|
||||
GET_16( c->req_buf + 2 ) );
|
||||
#else
|
||||
c->req_len -= (GET_16(c->req_buf + 2) + 4);
|
||||
|
||||
#endif
|
||||
/* should really just delete the packet from the
|
||||
* buffer, not the entire buffer */
|
||||
// c->req_len = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!strstr((const char *) c->req_buf, (const char *) "\r\n\r\n"))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (handle_request(c) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
/* should really just delete the request from the
|
||||
* buffer, not the entire buffer */
|
||||
c->req_len = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unbase64(unsigned char *d, int len, int *remain)
|
||||
{
|
||||
int src = 0, dest = 0, i;
|
||||
unsigned char enc[4];
|
||||
|
||||
for (;;)
|
||||
{
|
||||
for (i = 0; i < 4; ++src)
|
||||
{
|
||||
if (src >= len)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
memcpy(d + dest, enc, i);
|
||||
}
|
||||
*remain = i;
|
||||
return dest + i;
|
||||
}
|
||||
if (base64[d[src]] < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
enc[i++] = d[src];
|
||||
}
|
||||
d[dest] = ((base64[enc[0]] << 2) & 0xFC) | (base64[enc[1]] >> 4);
|
||||
d[dest + 1] = ((base64[enc[1]] << 4) & 0xF0) | (base64[enc[2]] >> 2);
|
||||
d[dest + 2] = ((base64[enc[2]] << 6) & 0xC0) | base64[enc[3]];
|
||||
if (enc[3] == '=')
|
||||
{
|
||||
if (enc[2] == '=')
|
||||
{
|
||||
dest += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest += 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dest += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static unsigned char s_ptemp[300] = {"TEARDOWN rtsp://192.168.1.1:7070/webcam/ RTSP/1.0\r\nCSeq: 6\r\nUser-Agent: Lavf56.40.101\r\nSession: 9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9\r\n\r\n4A5A6A7A8A9\r\n\r\n"};
|
||||
#endif
|
||||
|
||||
static void do_read(void *ei, void *d)
|
||||
{
|
||||
_os_printf("%s:%d\n", __FUNCTION__, __LINE__);
|
||||
spook_log(SL_DEBUG, "tcp do_read");
|
||||
struct conn *c = (struct conn *) d;
|
||||
int ret;
|
||||
struct session *sess = NULL;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
os_event_wait(&c->evt, RTSP_TCP_READ_MUTEX, NULL, OS_EVENT_WMODE_AND | OS_EVENT_WMODE_CLEAR, -1);
|
||||
ret = read(c->fd, c->req_buf + c->req_len, sizeof(c->req_buf) - c->req_len - 1);
|
||||
os_event_set(&c->evt, RTSP_TCP_READ_MUTEX, NULL);
|
||||
|
||||
if (ret <= 0)
|
||||
{
|
||||
if (ret < 0)
|
||||
{
|
||||
if (get_errno() == EAGAIN)
|
||||
{
|
||||
spook_log(SL_VERBOSE, "get_errno() === %x", get_errno());
|
||||
return;
|
||||
}
|
||||
spook_log(SL_VERBOSE, "get_errno() = %x", get_errno());
|
||||
spook_log(SL_VERBOSE, "closing TCP connection to client due to read error: %s ret:%d", strerror(get_errno()), ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
spook_log(SL_VERBOSE, "client closed TCP connection %x c:%X", c->sess, c);
|
||||
/* add by jornny */
|
||||
/* should be initialized by NULL */
|
||||
}
|
||||
|
||||
spook_log(SL_DEBUG, "free conn");
|
||||
// drop_conn( c );
|
||||
|
||||
/* add by jornny */
|
||||
/* drop_conn has free c, so can not use c->sess !!!*/
|
||||
sess = c->sess;
|
||||
|
||||
// 关闭自己的事件和socket
|
||||
if (c->read_event)
|
||||
{
|
||||
eloop_remove_event(c->read_event);
|
||||
c->read_event = NULL;
|
||||
}
|
||||
if (c->write_event)
|
||||
{
|
||||
eloop_remove_event(c->write_event);
|
||||
c->write_event = NULL;
|
||||
}
|
||||
c->running = 0;
|
||||
_os_printf("close sess:%x\r\n", (int) sess);
|
||||
if (sess)
|
||||
{
|
||||
// sess->conn = NULL;
|
||||
_os_printf("close sess->close:%x\r\n", (int) sess->closed);
|
||||
sess->closed(sess, NULL);
|
||||
}
|
||||
// 没有sess,说明连接还没有正式
|
||||
else
|
||||
{
|
||||
_os_printf("%s:%d\n", __FUNCTION__, __LINE__);
|
||||
drop_conn(c);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
c->req_len += ret;
|
||||
|
||||
if (c->req_len == sizeof(c->req_buf) - 1)
|
||||
{
|
||||
spook_log(SL_VERBOSE, "malformed request from client; exceeded maximum size");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
c->req_buf[c->req_len] = 0;
|
||||
while ((ret = parse_client_data(c)) > 0)
|
||||
;
|
||||
if (ret < 0)
|
||||
{
|
||||
_os_printf("%s:%d\n", __FUNCTION__, __LINE__);
|
||||
// drop_conn( c );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern int g_timeout;
|
||||
uint8_t sta_mac[8];
|
||||
|
||||
extern uint8_t keep_send;
|
||||
static void do_accept(void *ei, void *d)
|
||||
{
|
||||
spook_log(SL_DEBUG, "tcp do_accept");
|
||||
struct listener *listener = (struct listener *) d;
|
||||
int fd;
|
||||
unsigned int i;
|
||||
struct sockaddr_in addr;
|
||||
struct conn *c;
|
||||
|
||||
_os_printf("%s %d\n", __func__, __LINE__);
|
||||
i = sizeof(addr);
|
||||
if ((fd = accept(listener->fd, (struct sockaddr *) &addr, &i)) < 0)
|
||||
{
|
||||
spook_log(SL_WARN, "error accepting TCP connection: %s", strerror(get_errno()));
|
||||
return;
|
||||
}
|
||||
spook_log(SL_VERBOSE, "accepted connection from %s:%d", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
|
||||
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
|
||||
{
|
||||
spook_log(SL_INFO, "error setting O_NONBLOCK on socket: %s", strerror(get_errno()));
|
||||
}
|
||||
|
||||
i = 0;
|
||||
if (setsockopt(fd, IPPROTO_TCP /*SOL_SOCKET*/ /*SOL_TCP*/, TCP_NODELAY, &i, sizeof(i)) < 0)
|
||||
{
|
||||
spook_log(SL_INFO, "error setting TCP_NODELAY on socket: %s", strerror(get_errno()));
|
||||
}
|
||||
|
||||
// int tos = 0xe0;
|
||||
// setsockopt(fd, IPPROTO_IP, IP_TOS, (void *)&tos , sizeof(tos));
|
||||
|
||||
#if 0
|
||||
/* Find the dhcp Offered Addr based the IP Addr of this Connection */
|
||||
p_dhcp_addr = find_lease_by_yiaddr(addr.sin_addr.s_addr);
|
||||
|
||||
_os_printf("%s %d\n",__func__,__LINE__);
|
||||
if (NULL != p_dhcp_addr)
|
||||
{
|
||||
for (c = conn_list; c != NULL; c = c->next)
|
||||
{
|
||||
ret = memcmp(c->chaddr, p_dhcp_addr->chaddr, 6);
|
||||
if (0 == ret)
|
||||
{
|
||||
/* If the same Connection Client Mac Addr has existed, delete the previous Connection */
|
||||
if (g_timeout == 0)
|
||||
{
|
||||
//parse_client_data(c);
|
||||
if(c->sess)
|
||||
c->sess->closed( c->sess, NULL);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
g_timeout = 0;
|
||||
}
|
||||
|
||||
drop_conn(c);
|
||||
break;//只有一个ip是一样的,直接退出,否则drop_conn会更改conn_list链表,导致这里for出错
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
int keepalive = 1;
|
||||
int keepidle = 10;
|
||||
int keepintvl = 3;
|
||||
int keepcnt = 3;
|
||||
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *) &keepalive, sizeof(keepalive));
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof(keepidle));
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, (void *) &keepintvl, sizeof(keepintvl));
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, (void *) &keepcnt, sizeof(keepcnt));
|
||||
|
||||
c = (struct conn *) malloc(sizeof(struct conn));
|
||||
_os_printf("%s c:%X\n", __FUNCTION__, (int) c);
|
||||
c->next = conn_list;
|
||||
if (c->next)
|
||||
{
|
||||
c->next->prev = c;
|
||||
}
|
||||
c->prev = NULL;
|
||||
c->fd = fd;
|
||||
c->client_addr = addr;
|
||||
c->proto = CONN_PROTO_START;
|
||||
c->req_len = 0;
|
||||
c->req_list = NULL;
|
||||
c->read_event = eloop_add_fd(fd, EVENT_READ, EVENT_F_ENABLED, do_read, c);
|
||||
c->write_event = eloop_add_fd(fd, EVENT_WRITE, EVENT_F_DISABLED, conn_write, c);
|
||||
eloop_set_event_enabled(c->write_event, EVENT_F_DISABLED);
|
||||
c->send_buf_r = c->send_buf_w = 0;
|
||||
c->drop_after = 0;
|
||||
c->proto_state = NULL;
|
||||
c->sess = NULL; /* should be initialized by NULL */
|
||||
c->running = 1;
|
||||
os_event_init(&c->evt);
|
||||
os_event_set(&c->evt, RTSP_TCP_READ_MUTEX | RTSP_TCP_WRITE_MUTEX, NULL);
|
||||
conn_list = c;
|
||||
|
||||
/* Store the Client MAC Addr */
|
||||
// memcpy(c->chaddr, p_dhcp_addr->chaddr, 6);
|
||||
|
||||
// memcpy(sta_mac, p_dhcp_addr->chaddr, 6);
|
||||
//_os_printf("STA con[%x %x %x %x %x %x]\n",sta_mac[0],sta_mac[1],sta_mac[2],sta_mac[3],sta_mac[4],sta_mac[5]);
|
||||
|
||||
spook_log(SL_DEBUG, "TCP do_accept %d", fd);
|
||||
}
|
||||
|
||||
static int tcp_listen(int port)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
struct listener *listener;
|
||||
int opt, fd;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = 0;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
spook_log(SL_ERR, "error creating listen socket: %s", strerror(get_errno()));
|
||||
return -1;
|
||||
}
|
||||
opt = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
||||
{
|
||||
spook_log(SL_WARN, "ignoring error on setsockopt: %s", strerror(get_errno()));
|
||||
}
|
||||
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
|
||||
{
|
||||
spook_log(SL_ERR, "unable to bind to tcp socket: %s", strerror(get_errno()));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (listen(fd, 5) < 0)
|
||||
{
|
||||
spook_log(SL_ERR, "error when attempting to listen on tcp socket: %s", strerror(get_errno()));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
listener = (struct listener *) malloc(sizeof(struct listener));
|
||||
listener->fd = fd;
|
||||
|
||||
eloop_add_fd(fd, EVENT_READ, EVENT_F_ENABLED, do_accept, listener);
|
||||
|
||||
spook_log(SL_INFO, "listening on tcp port %d", port);
|
||||
_os_printf("port:%d\tfd:%d\n", port, fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************* GLOBAL CONFIGURATION DIRECTIVES ********************/
|
||||
|
||||
int config_port(int port)
|
||||
{
|
||||
if (port <= 0 || port > 65535)
|
||||
{
|
||||
spook_log(SL_ERR, "invalid listen port %d", port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return tcp_listen(port); // listen the 7070 port everytime
|
||||
}
|
||||
241
sdk/app/spook/webfile.c
Normal file
241
sdk/app/spook/webfile.c
Normal file
@@ -0,0 +1,241 @@
|
||||
#include "sys_config.h"
|
||||
#include "tx_platform.h"
|
||||
#include <csi_kernel.h>
|
||||
#include "lwip\sockets.h"
|
||||
#include "lwip\netif.h"
|
||||
#include "lwip\dns.h"
|
||||
#include "lwip\api.h"
|
||||
#include "lwip\tcp.h"
|
||||
|
||||
#include "rtsp_common.h"
|
||||
#include "osal/string.h"
|
||||
#include "stream_define.h"
|
||||
#include "video_app/video_app.h"
|
||||
#include "stream_frame.h"
|
||||
#include "log.h"
|
||||
#include "avidemux.h"
|
||||
#define WEBFILE_BASE_PATH "DCIM"
|
||||
|
||||
extern void spook_send_thread_stream(struct rtsp_priv *r);
|
||||
extern uint32_t creat_stream(struct rtsp_priv *r,const char *video_name,const char *audio_name);
|
||||
extern uint32_t destory_stream(struct rtsp_priv *r);
|
||||
extern uint32_t stop_play_avi_stream(stream* s);
|
||||
|
||||
|
||||
void rtsp_webfile_thread(void *d)
|
||||
{
|
||||
struct rtsp_priv *r = (struct rtsp_priv*)d;
|
||||
os_printf("%s:%d\n",__FUNCTION__,__LINE__);
|
||||
spook_send_thread_stream(r);
|
||||
}
|
||||
|
||||
|
||||
stream* creat_play_avi_stream(const char *name,char *filepath,char *recv_name);
|
||||
//创建实时预览的的线程
|
||||
void webfile_rtsp_creat(struct rtsp_source *source,void *priv)
|
||||
{
|
||||
char *path = (char*)priv;
|
||||
//void *fp = NULL;
|
||||
//struct aviinfo *info = NULL;
|
||||
char filePath[64] = {0};
|
||||
if(path)
|
||||
{
|
||||
_os_printf("%s path:%s\n",__FUNCTION__,path);
|
||||
char *base = path;
|
||||
char *c;
|
||||
int filePathLen = strlen(base);
|
||||
if(filePathLen > 1 && path[filePathLen - 1] == '/')
|
||||
{
|
||||
--filePathLen;
|
||||
}
|
||||
|
||||
c = strchr (base+1, '/');
|
||||
filePathLen -= c - base;
|
||||
|
||||
filePath[0] = '0';
|
||||
filePath[1] = ':';
|
||||
memcpy(filePath+2,WEBFILE_BASE_PATH,strlen(WEBFILE_BASE_PATH));
|
||||
strncpy (filePath+2+strlen(WEBFILE_BASE_PATH), c, filePathLen);
|
||||
filePath[2+filePathLen+strlen(WEBFILE_BASE_PATH)] = '\0';
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
goto webfile_rtsp_creat_end;
|
||||
}
|
||||
|
||||
|
||||
//先去打开一下文件?然后判断一下?
|
||||
uint32_t res;
|
||||
source->priv = (struct rtsp_priv*)os_malloc(sizeof(struct rtsp_priv));
|
||||
struct rtsp_priv *r = (struct rtsp_priv*)source->priv;
|
||||
if(source->priv)
|
||||
{
|
||||
r->live_node = &source->live_node;
|
||||
res = creat_stream(r,R_RTP_JPEG,NULL);
|
||||
if(!res)
|
||||
{
|
||||
csi_kernel_task_new((k_task_entry_t)rtsp_webfile_thread, "rtsp", r, 9, 0, NULL, 1024,&source->thread_handle);
|
||||
|
||||
|
||||
//创建播放文件的流
|
||||
r->webfile_s = creat_play_avi_stream(S_WEBJPEG,filePath,R_RTP_JPEG);
|
||||
void *webfile_handle;
|
||||
extern void play_web_avi_thread(void *d);
|
||||
if(r->webfile_s)
|
||||
{
|
||||
_os_printf("webfile_s1:%X\n",(unsigned int)r->webfile_s);
|
||||
//如果创建任务失败,需要close一次
|
||||
open_stream_again(r->webfile_s);
|
||||
//打开对应的文件
|
||||
csi_kernel_task_new((k_task_entry_t)play_web_avi_thread, "play_avi", r->webfile_s, 9, 0, NULL, 1024,&webfile_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_os_printf("%s not enough space\n",__FUNCTION__);
|
||||
}
|
||||
webfile_rtsp_creat_end:
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//创建实时预览的的线程
|
||||
void webfile_rtsp_destory(struct rtsp_source *source)
|
||||
{
|
||||
void *tmp = source->thread_handle;
|
||||
struct rtsp_priv *r = source->priv;
|
||||
if(r)
|
||||
{
|
||||
if(r->webfile_s)
|
||||
{
|
||||
stop_avi_stream(r->webfile_s);
|
||||
}
|
||||
destory_stream(r);
|
||||
os_free(source->priv);
|
||||
source->priv = NULL;
|
||||
}
|
||||
source->thread_handle = NULL;
|
||||
if(tmp)
|
||||
{
|
||||
csi_kernel_task_del(tmp);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int webfile_get_sdp( struct session *s, char *dest, int *len,
|
||||
char *path )
|
||||
{
|
||||
struct rtsp_session *ls = (struct rtsp_session *)s->private;
|
||||
int i = 0, t;
|
||||
char *addr = "IP4 0.0.0.0";
|
||||
_os_printf("%s:%d\tpath:%s\n",__FUNCTION__,__LINE__,path);
|
||||
struct avi_msg msg;
|
||||
char filePath[64] = {0};
|
||||
uint32_t msk;
|
||||
if(path)
|
||||
{
|
||||
_os_printf("%s path:%s\n",__FUNCTION__,path);
|
||||
char *base = path;
|
||||
char *c;
|
||||
int filePathLen = strlen(base);
|
||||
if(filePathLen > 1 && path[filePathLen - 1] == '/')
|
||||
{
|
||||
--filePathLen;
|
||||
}
|
||||
|
||||
c = strchr (base+1, '/');
|
||||
filePathLen -= c - base;
|
||||
|
||||
filePath[0] = '0';
|
||||
filePath[1] = ':';
|
||||
memcpy(filePath+2,WEBFILE_BASE_PATH,strlen(WEBFILE_BASE_PATH));
|
||||
strncpy (filePath+2+strlen(WEBFILE_BASE_PATH), c, filePathLen);
|
||||
filePath[2+filePathLen+strlen(WEBFILE_BASE_PATH)] = '\0';
|
||||
}
|
||||
|
||||
_os_printf("filePath:%s\n",filePath);
|
||||
msk = get_avidemux_parse_stream(filePath,&msg);
|
||||
|
||||
|
||||
if( s->ep[0] && s->ep[0]->trans_type == RTP_TRANS_UDP )
|
||||
addr = s->ep[0]->trans.udp.sdp_addr;
|
||||
|
||||
i = snprintf( dest, *len,"v=0\r\no=- 1 1 IN IP4 127.0.0.1\r\ns=Test\r\na=type:broadcast\r\nt=0 0\r\nc=IN %s\r\n", addr );
|
||||
|
||||
|
||||
for( t = 0; t < MAX_TRACKS && ls->source->track[t].rtp; ++t )
|
||||
{
|
||||
int port;
|
||||
|
||||
//if( ! ls->source->track[t].ready ) return 0;
|
||||
|
||||
if( s->ep[t] && s->ep[t]->trans_type == RTP_TRANS_UDP )
|
||||
port = s->ep[t]->trans.udp.sdp_port;
|
||||
else
|
||||
port = 0;
|
||||
|
||||
|
||||
os_printf("sdp type:%d\n",ls->source->track[t].rtp->type);
|
||||
if(ls->source->track[t].rtp->type == 0)
|
||||
{
|
||||
i += ls->source->track[t].rtp->get_sdp( dest + i, *len - i,96 + t, port,ls->source->track[t].rtp->private );
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!(msk&BIT(1)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ls->source->track[t].rtp->sample_rate = msg.audio_sample_rate;
|
||||
i += ls->source->track[t].rtp->get_sdp( dest + i, *len - i,96 + t, port,(void*)msg.audio_sample_rate);
|
||||
}
|
||||
if( port == 0 ) // XXX What's a better way to do this?
|
||||
i += sprintf( dest + i, "a=control:track%d\r\n", t );
|
||||
}
|
||||
|
||||
*len = i;
|
||||
return t;
|
||||
}
|
||||
|
||||
static struct session *webfile_rtsp_open( char *path, void *d )
|
||||
{
|
||||
//默认的,各自模式可以各自去修改
|
||||
struct session *sess = rtsp_open(path,d);
|
||||
if(sess)
|
||||
{
|
||||
sess->get_sdp = webfile_get_sdp;
|
||||
}
|
||||
return sess;
|
||||
}
|
||||
|
||||
static int webfile_set_path( const char *path, void *d )
|
||||
{
|
||||
spook_log (SL_DEBUG, "live set_path %s", path);
|
||||
|
||||
/*if( num_tokens == 2 )*/
|
||||
{
|
||||
new_rtsp_location( path, NULL, NULL, NULL,webfile_rtsp_open, d );
|
||||
return 0;
|
||||
}
|
||||
spook_log( SL_ERR, "rtsp-handler: syntax: Path <path> [<realm> <username> <password>]" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
void webfile_rtsp_init(const rtp_name *rtsp)
|
||||
{
|
||||
struct rtsp_source *source;
|
||||
source = rtsp_start_block();
|
||||
webfile_set_path( rtsp->path, source );
|
||||
set_video_track( (char*)rtsp->jpg_name, source );
|
||||
//set_audio_track( (char*)rtsp->audio_name, source );
|
||||
register_live_fn(source,webfile_rtsp_creat,webfile_rtsp_destory);
|
||||
rtsp_end_block(source);
|
||||
return ;
|
||||
}
|
||||
Reference in New Issue
Block a user