From a1725ad334205e230fbd686ea4dad81cf4a0bfa5 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 19 Apr 2007 21:40:50 +0000 Subject: [PATCH] modest core framework for video stuff git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4977 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/include/switch_core.h | 3 + src/include/switch_core_event_hook.h | 38 +++ src/include/switch_frame.h | 7 +- src/include/switch_ivr.h | 1 + src/include/switch_module_interfaces.h | 4 + src/include/switch_types.h | 12 +- src/mod/codecs/mod_h26x/Makefile | 2 + src/mod/codecs/mod_h26x/mod_h26x.c | 154 ++++++++++ src/mod/codecs/mod_h26x/mod_h26x.vcproj | 211 ++++++++++++++ src/mod/endpoints/mod_sofia/mod_sofia.c | 144 ++++++++-- src/mod/endpoints/mod_sofia/mod_sofia.h | 23 +- src/mod/endpoints/mod_sofia/sofia.c | 16 +- src/mod/endpoints/mod_sofia/sofia_glue.c | 351 +++++++++++++++++++++-- src/switch_core_event_hook.c | 44 +++ src/switch_core_io.c | 89 ++++++ src/switch_core_state_machine.c | 11 +- src/switch_ivr_async.c | 65 +++++ src/switch_rtp.c | 30 +- 18 files changed, 1128 insertions(+), 77 deletions(-) create mode 100644 src/mod/codecs/mod_h26x/Makefile create mode 100644 src/mod/codecs/mod_h26x/mod_h26x.c create mode 100644 src/mod/codecs/mod_h26x/mod_h26x.vcproj diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 7b9fd7cdfa..efb28017a5 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -714,6 +714,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_dequeue_private_event(switch */ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout, int stream_id); +SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout, int stream_id); +SWITCH_DECLARE(switch_status_t) switch_core_session_write_video_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout, int stream_id); + /*! \brief Reset the buffers and resampler on a session \param session the session to reset diff --git a/src/include/switch_core_event_hook.h b/src/include/switch_core_event_hook.h index ac2e5ac678..b9bab028f9 100644 --- a/src/include/switch_core_event_hook.h +++ b/src/include/switch_core_event_hook.h @@ -38,7 +38,9 @@ typedef struct switch_io_event_hook_outgoing_channel switch_io_event_hook_outgoi typedef struct switch_io_event_hook_receive_message switch_io_event_hook_receive_message_t; typedef struct switch_io_event_hook_receive_event switch_io_event_hook_receive_event_t; typedef struct switch_io_event_hook_read_frame switch_io_event_hook_read_frame_t; +typedef struct switch_io_event_hook_video_read_frame switch_io_event_hook_video_read_frame_t; typedef struct switch_io_event_hook_write_frame switch_io_event_hook_write_frame_t; +typedef struct switch_io_event_hook_video_write_frame switch_io_event_hook_video_write_frame_t; typedef struct switch_io_event_hook_kill_channel switch_io_event_hook_kill_channel_t; typedef struct switch_io_event_hook_waitfor_read switch_io_event_hook_waitfor_read_t; typedef struct switch_io_event_hook_waitfor_write switch_io_event_hook_waitfor_write_t; @@ -50,7 +52,9 @@ typedef switch_status_t (*switch_outgoing_channel_hook_t) (switch_core_session_t typedef switch_status_t (*switch_receive_message_hook_t) (switch_core_session_t *, switch_core_session_message_t *); typedef switch_status_t (*switch_receive_event_hook_t) (switch_core_session_t *, switch_event_t *); typedef switch_status_t (*switch_read_frame_hook_t) (switch_core_session_t *, switch_frame_t **, int, switch_io_flag_t, int); +typedef switch_status_t (*switch_video_read_frame_hook_t) (switch_core_session_t *, switch_frame_t **, int, switch_io_flag_t, int); typedef switch_status_t (*switch_write_frame_hook_t) (switch_core_session_t *, switch_frame_t *, int, switch_io_flag_t, int); +typedef switch_status_t (*switch_video_write_frame_hook_t) (switch_core_session_t *, switch_frame_t *, int, switch_io_flag_t, int); typedef switch_status_t (*switch_kill_channel_hook_t) (switch_core_session_t *, int); typedef switch_status_t (*switch_waitfor_read_hook_t) (switch_core_session_t *, int, int); typedef switch_status_t (*switch_waitfor_write_hook_t) (switch_core_session_t *, int, int); @@ -86,6 +90,13 @@ struct switch_io_event_hook_read_frame { struct switch_io_event_hook_read_frame *next; }; +/*! \brief Node in which to store custom read frame channel callback hooks */ +struct switch_io_event_hook_video_read_frame { + /*! the read frame channel callback hook */ + switch_read_frame_hook_t video_read_frame; + struct switch_io_event_hook_video_read_frame *next; +}; + /*! \brief Node in which to store custom write_frame channel callback hooks */ struct switch_io_event_hook_write_frame { /*! the write_frame channel callback hook */ @@ -93,6 +104,13 @@ struct switch_io_event_hook_write_frame { struct switch_io_event_hook_write_frame *next; }; +/*! \brief Node in which to store custom video_write_frame channel callback hooks */ +struct switch_io_event_hook_video_write_frame { + /*! the video_write_frame channel callback hook */ + switch_video_write_frame_hook_t video_write_frame; + struct switch_io_event_hook_video_write_frame *next; +}; + /*! \brief Node in which to store custom kill channel callback hooks */ struct switch_io_event_hook_kill_channel { /*! the kill channel callback hook */ @@ -138,8 +156,12 @@ struct switch_io_event_hooks { switch_io_event_hook_receive_event_t *receive_event; /*! a list of read frame hooks */ switch_io_event_hook_read_frame_t *read_frame; + /*! a list of video read frame hooks */ + switch_io_event_hook_video_read_frame_t *video_read_frame; /*! a list of write frame hooks */ switch_io_event_hook_write_frame_t *write_frame; + /*! a list of video write frame hooks */ + switch_io_event_hook_video_write_frame_t *video_write_frame; /*! a list of kill channel hooks */ switch_io_event_hook_kill_channel_t *kill_channel; /*! a list of wait for read hooks */ @@ -185,6 +207,14 @@ SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_receive_message(switc */ SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_read_frame(switch_core_session_t *session, switch_read_frame_hook_t read_frame); +/*! + \brief Add an event hook to be executed when a session reads a frame + \param session session to bind hook to + \param video_read_frame hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_video_read_frame(switch_core_session_t *session, switch_read_frame_hook_t video_read_frame); + /*! \brief Add an event hook to be executed when a session writes a frame \param session session to bind hook to @@ -193,6 +223,14 @@ SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_read_frame(switch_cor */ SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_write_frame(switch_core_session_t *session, switch_write_frame_hook_t write_frame); +/*! + \brief Add an event hook to be executed when a session writes a video frame + \param session session to bind hook to + \param write_frame hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_video_write_frame(switch_core_session_t *session, switch_video_write_frame_hook_t video_write_frame); + /*! \brief Add an event hook to be executed when a session kills a channel \param session session to bind hook to diff --git a/src/include/switch_frame.h b/src/include/switch_frame.h index 0da90a23fe..4c1cb3d27f 100644 --- a/src/include/switch_frame.h +++ b/src/include/switch_frame.h @@ -30,7 +30,7 @@ * */ /*! \file switch_frame.h - \brief Media Frame Structure + \brief Media Frame Structure */ #ifndef SWITCH_FRAME_H @@ -40,7 +40,7 @@ SWITCH_BEGIN_EXTERN_C /*! \brief An abstraction of a data frame */ - struct switch_frame { +struct switch_frame { /*! a pointer to the codec information */ switch_codec_t *codec; /*! the originating source of the frame */ @@ -63,6 +63,9 @@ SWITCH_BEGIN_EXTERN_C switch_payload_t payload; /*! the timestamp of the frame */ switch_size_t timestamp; + uint16_t seq; + uint32_t ssrc; + switch_bool_t m; /*! frame flags */ switch_frame_flag_t flags; }; diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 788300bdd5..f06b47b0a3 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -203,6 +203,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_stop_record_session(switch_core_sessi SWITCH_DECLARE(switch_status_t) switch_ivr_inband_dtmf_session(switch_core_session_t *session); SWITCH_DECLARE(switch_status_t) switch_ivr_stop_inband_dtmf_session(switch_core_session_t *session); +SWITCH_DECLARE(void) switch_ivr_session_echo(switch_core_session_t *session); /*! \brief play a file from the disk to the session diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index e018de34ad..34392f077e 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -99,6 +99,10 @@ struct switch_io_routines { switch_status_t (*receive_event) (switch_core_session_t *, switch_event_t *); /*! change a sessions channel state */ switch_status_t (*state_change) (switch_core_session_t *); + /*! read a video frame from a session */ + switch_status_t (*read_video_frame) (switch_core_session_t *, switch_frame_t **, int, switch_io_flag_t, int); + /*! write a video frame to a session */ + switch_status_t (*write_video_frame) (switch_core_session_t *, switch_frame_t *, int, switch_io_flag_t, int); }; /*! \brief Abstraction of an module endpoint interface diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 0ec0c0827f..7a7f6c0285 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -110,6 +110,10 @@ SWITCH_BEGIN_EXTERN_C #define SWITCH_LOCAL_MEDIA_PORT_VARIABLE "local_media_port" #define SWITCH_REMOTE_MEDIA_IP_VARIABLE "remote_media_ip" #define SWITCH_REMOTE_MEDIA_PORT_VARIABLE "remote_media_port" +#define SWITCH_REMOTE_VIDEO_IP_VARIABLE "remote_video_ip" +#define SWITCH_REMOTE_VIDEO_PORT_VARIABLE "remote_video_port" +#define SWITCH_LOCAL_VIDEO_IP_VARIABLE "local_video_ip" +#define SWITCH_LOCAL_VIDEO_PORT_VARIABLE "local_video_port" #define SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE "hangup_after_bridge" #define SWITCH_MAX_FORWARDS_VARIABLE "max_forwards" #define SWITCH_SPEECH_KEY "speech" @@ -542,6 +546,7 @@ CF_RING_READY = (1 << 18) - Channel is ready to send ringback CF_BREAK = (1 << 19) - Channel should stop what it's doing CF_BROADCAST = (1 << 20) - Channel is broadcasting CF_UNICAST = (1 << 21) - Channel has a unicast connection +CF_VIDEO = (1 << 22) - Channel has video */ @@ -567,7 +572,8 @@ typedef enum { CF_RING_READY = (1 << 18), CF_BREAK = (1 << 19), CF_BROADCAST = (1 << 20), - CF_UNICAST = (1 << 21) + CF_UNICAST = (1 << 21), + CF_VIDEO = (1 << 22) } switch_channel_flag_t; @@ -578,12 +584,14 @@ typedef enum {
 SFF_CNG       = (1 <<  0) - Frame represents comfort noise
 SFF_RAW_RTP   = (1 <<  1) - Frame has raw rtp accessible
+SFF_RTP_HEADER = (1 << 2) - Get the rtp header from the frame header
 
*/ typedef enum { SFF_NONE = 0, SFF_CNG = (1 << 0), - SFF_RAW_RTP = (1 << 1) + SFF_RAW_RTP = (1 << 1), + SFF_RTP_HEADER = (1 << 2) } switch_frame_flag_t; diff --git a/src/mod/codecs/mod_h26x/Makefile b/src/mod/codecs/mod_h26x/Makefile new file mode 100644 index 0000000000..c6d645e6aa --- /dev/null +++ b/src/mod/codecs/mod_h26x/Makefile @@ -0,0 +1,2 @@ +BASE=../../../.. +include /usr/src/freeswitch.trunk/build/modmake.rules diff --git a/src/mod/codecs/mod_h26x/mod_h26x.c b/src/mod/codecs/mod_h26x/mod_h26x.c new file mode 100644 index 0000000000..c18420db78 --- /dev/null +++ b/src/mod/codecs/mod_h26x/mod_h26x.c @@ -0,0 +1,154 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_h26x.c -- H26X Signed Linear Codec + * + */ +#include + +static const char modname[] = "mod_h26x"; + + +static switch_status_t switch_h26x_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) +{ + int encoding, decoding; + + encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); + decoding = (flags & SWITCH_CODEC_FLAG_DECODE); + + if (!(encoding || decoding)) { + return SWITCH_STATUS_FALSE; + } else { + return SWITCH_STATUS_SUCCESS; + } +} + +static switch_status_t switch_h26x_encode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *decoded_data, + uint32_t decoded_data_len, + uint32_t decoded_rate, void *encoded_data, uint32_t * encoded_data_len, uint32_t * encoded_rate, + unsigned int *flag) +{ + return SWITCH_STATUS_FALSE; +} + +static switch_status_t switch_h26x_decode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *encoded_data, + uint32_t encoded_data_len, + uint32_t encoded_rate, void *decoded_data, uint32_t * decoded_data_len, uint32_t * decoded_rate, + unsigned int *flag) +{ + return SWITCH_STATUS_FALSE; +} + + +static switch_status_t switch_h26x_destroy(switch_codec_t *codec) +{ + + return SWITCH_STATUS_SUCCESS; +} + +static const switch_codec_implementation_t h264_90000_implementation = { + /*.codec_type */ SWITCH_CODEC_TYPE_VIDEO, + /*.ianacode */ 99, + /*.iananame */ "H264", + /*.fmtp */ NULL, + /*.samples_per_second = */ 90000, + /*.bits_per_second = */ 0, + /*.microseconds_per_frame = */ 0, + /*.samples_per_frame = */ 0, + /*.bytes_per_frame = */ 0, + /*.encoded_bytes_per_frame = */ 0, + /*.number_of_channels = */ 1, + /*.pref_frames_per_packet = */ 1, + /*.max_frames_per_packet = */ 1, + /*.init = */ switch_h26x_init, + /*.encode = */ switch_h26x_encode, + /*.decode = */ switch_h26x_decode, + /*.destroy = */ switch_h26x_destroy + /*.next = */ +}; + +static const switch_codec_implementation_t h263_90000_implementation = { + /*.codec_type */ SWITCH_CODEC_TYPE_VIDEO, + /*.ianacode */ 34, + /*.iananame */ "H263", + /*.fmtp */ NULL, + /*.samples_per_second = */ 90000, + /*.bits_per_second = */ 0, + /*.microseconds_per_frame = */ 0, + /*.samples_per_frame = */ 0, + /*.bytes_per_frame = */ 0, + /*.encoded_bytes_per_frame = */ 0, + /*.number_of_channels = */ 1, + /*.pref_frames_per_packet = */ 1, + /*.max_frames_per_packet = */ 1, + /*.init = */ switch_h26x_init, + /*.encode = */ switch_h26x_encode, + /*.decode = */ switch_h26x_decode, + /*.destroy = */ switch_h26x_destroy, + /*.next = */&h264_90000_implementation +}; + +static const switch_codec_interface_t h26x_codec_interface = { + /*.interface_name */ "h26x video (passthru)", + /*.implementations */ &h263_90000_implementation +}; + +static switch_loadable_module_interface_t h26x_module_interface = { + /*.module_name */ modname, + /*.endpoint_interface */ NULL, + /*.timer_interface */ NULL, + /*.dialplan_interface */ NULL, + /*.codec_interface */ &h26x_codec_interface, + /*.application_interface */ NULL, + /*.api_interface */ NULL, +}; + + +SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename) +{ + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = &h26x_module_interface; + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: + */ diff --git a/src/mod/codecs/mod_h26x/mod_h26x.vcproj b/src/mod/codecs/mod_h26x/mod_h26x.vcproj new file mode 100644 index 0000000000..2e66ff5a31 --- /dev/null +++ b/src/mod/codecs/mod_h26x/mod_h26x.vcproj @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index c4d3d98837..c893a0f98f 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -58,6 +58,8 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session switch_memory_pool_t **pool); static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout, switch_io_flag_t flags, int stream_id); static switch_status_t sofia_write_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout, switch_io_flag_t flags, int stream_id); +static switch_status_t sofia_read_video_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout, switch_io_flag_t flags, int stream_id); +static switch_status_t sofia_write_video_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout, switch_io_flag_t flags, int stream_id); static switch_status_t sofia_kill_channel(switch_core_session_t *session, int sig); @@ -321,12 +323,117 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session) } + +static switch_status_t sofia_read_video_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout, switch_io_flag_t flags, int stream_id) +{ + private_object_t *tech_pvt = NULL; + switch_channel_t *channel = NULL; + int payload = 0; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_object_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_HUP)) { + return SWITCH_STATUS_FALSE; + } + + while (!(tech_pvt->video_read_codec.implementation && switch_rtp_ready(tech_pvt->video_rtp_session))) { + if (switch_channel_ready(channel)) { + switch_yield(10000); + } else { + return SWITCH_STATUS_GENERR; + } + } + + + tech_pvt->video_read_frame.datalen = 0; + + + if (switch_test_flag(tech_pvt, TFLAG_IO)) { + switch_status_t status; + + if (!switch_test_flag(tech_pvt, TFLAG_RTP)) { + return SWITCH_STATUS_GENERR; + } + + assert(tech_pvt->rtp_session != NULL); + tech_pvt->video_read_frame.datalen = 0; + + while (switch_test_flag(tech_pvt, TFLAG_IO) && tech_pvt->video_read_frame.datalen == 0) { + tech_pvt->video_read_frame.flags = SFF_NONE; + + status = switch_rtp_zerocopy_read_frame(tech_pvt->video_rtp_session, &tech_pvt->video_read_frame); + if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { + return SWITCH_STATUS_FALSE; + } + + payload = tech_pvt->video_read_frame.payload; + + if (tech_pvt->video_read_frame.datalen > 0) { + break; + } + } + } + + if (tech_pvt->video_read_frame.datalen == 0) { + *frame = NULL; + return SWITCH_STATUS_GENERR; + } + + *frame = &tech_pvt->video_read_frame; + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t sofia_write_video_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout, switch_io_flag_t flags, int stream_id) +{ + private_object_t *tech_pvt; + switch_channel_t *channel = NULL; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_object_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + while (!(tech_pvt->video_read_codec.implementation && switch_rtp_ready(tech_pvt->video_rtp_session))) { + if (switch_channel_ready(channel)) { + switch_yield(10000); + } else { + return SWITCH_STATUS_GENERR; + } + } + + if (switch_test_flag(tech_pvt, TFLAG_HUP)) { + return SWITCH_STATUS_FALSE; + } + + if (!switch_test_flag(tech_pvt, TFLAG_RTP)) { + return SWITCH_STATUS_GENERR; + } + + if (!switch_test_flag(tech_pvt, TFLAG_IO)) { + return SWITCH_STATUS_SUCCESS; + } + + if (!switch_test_flag(frame, SFF_CNG)) { + switch_rtp_write_frame(tech_pvt->video_rtp_session, frame, 0); + } + + return status; +} + + static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout, switch_io_flag_t flags, int stream_id) { private_object_t *tech_pvt = NULL; switch_channel_t *channel = NULL; int payload = 0; - + channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -349,16 +456,6 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f tech_pvt->read_frame.datalen = 0; switch_set_flag_locked(tech_pvt, TFLAG_READING); -#if 0 - if (tech_pvt->last_read) { - elapsed = (unsigned int) ((switch_time_now() - tech_pvt->last_read) / 1000); - if (elapsed > 60000) { - return SWITCH_STATUS_TIMEOUT; - } - } -#endif - - if (switch_test_flag(tech_pvt, TFLAG_IO)) { switch_status_t status; @@ -382,20 +479,6 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f payload = tech_pvt->read_frame.payload; -#if 0 - elapsed = (unsigned int) ((switch_time_now() - started) / 1000); - - if (timeout > -1) { - if (elapsed >= (unsigned int) timeout) { - return SWITCH_STATUS_BREAK; - } - } - - elapsed = (unsigned int) ((switch_time_now() - last_act) / 1000); - if (elapsed >= hard_timeout) { - return SWITCH_STATUS_BREAK; - } -#endif if (switch_rtp_has_dtmf(tech_pvt->rtp_session)) { char dtmf[128]; switch_rtp_dequeue_dtmf(tech_pvt->rtp_session, dtmf, sizeof(dtmf)); @@ -508,6 +591,9 @@ static switch_status_t sofia_kill_channel(switch_core_session_t *session, int si if (switch_rtp_ready(tech_pvt->rtp_session)) { switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_BREAK); } + if (switch_rtp_ready(tech_pvt->video_rtp_session)) { + switch_rtp_set_flag(tech_pvt->video_rtp_session, SWITCH_RTP_FLAG_BREAK); + } break; case SWITCH_SIG_KILL: default: @@ -517,6 +603,9 @@ static switch_status_t sofia_kill_channel(switch_core_session_t *session, int si if (switch_rtp_ready(tech_pvt->rtp_session)) { switch_rtp_kill_socket(tech_pvt->rtp_session); } + if (switch_rtp_ready(tech_pvt->video_rtp_session)) { + switch_rtp_kill_socket(tech_pvt->video_rtp_session); + } break; } @@ -774,7 +863,10 @@ static const switch_io_routines_t sofia_io_routines = { /*.waitfor_read */ sofia_waitfor_write, /*.send_dtmf */ sofia_send_dtmf, /*.receive_message */ sofia_receive_message, - /*.receive_event */ sofia_receive_event + /*.receive_event */ sofia_receive_event, + /*.state_change*/ NULL, + /*.read_video_frame*/ sofia_read_video_frame, + /*.write_video_frame*/ sofia_write_video_frame }; static const switch_state_handler_table_t sofia_event_handlers = { diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index cc308768ef..c332e82bea 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -137,7 +137,9 @@ typedef enum { TFLAG_BUGGY_2833 = (1 << 21), TFLAG_SIP_HOLD = (1 << 22), TFLAG_INB_NOMEDIA = (1 << 23), - TFLAG_LATE_NEGOTIATION = (1 << 24) + TFLAG_LATE_NEGOTIATION = (1 << 24), + TFLAG_SDP = (1 << 25), + TFLAG_VIDEO = (1 << 26) } TFLAGS; struct mod_sofia_globals { @@ -310,7 +312,23 @@ struct private_object { nua_handle_t *nh; nua_handle_t *nh2; sip_contact_t *contact; - int hangup_status; + /** VIDEO **/ + switch_frame_t video_read_frame; + switch_codec_t video_read_codec; + switch_codec_t video_write_codec; + switch_rtp_t *video_rtp_session; + switch_port_t adv_sdp_video_port; + switch_port_t local_sdp_video_port; + char *video_rm_encoding; + switch_payload_t video_pt; + unsigned long video_rm_rate; + uint32_t video_codec_ms; + char *remote_sdp_video_ip; + switch_port_t remote_sdp_video_port; + char *video_rm_fmtp; + switch_payload_t video_agreed_pt; + char *video_fmtp_out; + uint32_t video_count; }; struct callback_t { @@ -428,3 +446,4 @@ switch_bool_t sofia_glue_execute_sql_callback(sofia_profile_t *profile, switch_core_db_callback_func_t callback, void *pdata); char *sofia_glue_execute_sql2str(sofia_profile_t *profile, switch_mutex_t *mutex, char *sql, char *resbuf, size_t len); +void sofia_glue_check_video_codecs(private_object_t *tech_pvt); diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 1993b17ef4..8c3a1ed6e0 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -924,14 +924,8 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, nua_ack(nh, TAG_END()); break; case nua_callstate_received: - - if (session && switch_core_session_running(session)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Re-Entering Call State Received!\n"); - goto done; - } - - if (channel) { - if (r_sdp) { + if (tech_pvt && !switch_test_flag(tech_pvt, TFLAG_SDP)) { + if (r_sdp && !switch_test_flag(tech_pvt, TFLAG_SDP)) { if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOMEDIA"); switch_set_flag_locked(tech_pvt, TFLAG_READY); @@ -1015,7 +1009,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, break; case nua_callstate_completed: if (tech_pvt && r_sdp) { - if (r_sdp) { + if (r_sdp) { // && !switch_test_flag(tech_pvt, TFLAG_SDP)) { if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { goto done; } else { @@ -1078,10 +1072,10 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, goto done; } - if (!r_sdp) { + if (!r_sdp && !switch_test_flag(tech_pvt, TFLAG_SDP)) { r_sdp = (const char *) switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE); } - if (r_sdp) { + if (r_sdp && !switch_test_flag(tech_pvt, TFLAG_SDP)) { if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { switch_set_flag_locked(tech_pvt, TFLAG_ANS); switch_channel_mark_answered(channel); diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index f8e158b6c2..aa8ef79aa0 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -34,6 +34,8 @@ */ #include "mod_sofia.h" +switch_status_t sofia_glue_tech_choose_video_port(private_object_t *tech_pvt); +switch_status_t sofia_glue_tech_set_video_codec(private_object_t *tech_pvt, int force); void sofia_glue_set_local_sdp(private_object_t *tech_pvt, char *ip, uint32_t port, char *sr, int force) { @@ -41,6 +43,7 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, char *ip, uint32_t por switch_time_t now = switch_time_now(); int ptime = 0; int rate = 0; + uint32_t v_port; if (!force && !ip && !sr && switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { return; @@ -56,6 +59,8 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, char *ip, uint32_t por port = tech_pvt->proxy_sdp_audio_port; } } + + if (!sr) { sr = "sendrecv"; } @@ -63,7 +68,10 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, char *ip, uint32_t por snprintf(buf, sizeof(buf), "v=0\n" "o=FreeSWITCH %d%" SWITCH_TIME_T_FMT " %d%" SWITCH_TIME_T_FMT " IN IP4 %s\n" - "s=FreeSWITCH\n" "c=IN IP4 %s\n" "t=0 0\n" "a=%s\n" "m=audio %d RTP/AVP", port, now, port, now, ip, ip, sr, port); + "s=FreeSWITCH\n" + "c=IN IP4 %s\n" "t=0 0\n" + "a=%s\n" + "m=audio %d RTP/AVP", port, now, port, now, ip, ip, sr, port); if (tech_pvt->rm_encoding) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->pt); @@ -72,6 +80,10 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, char *ip, uint32_t por for (i = 0; i < tech_pvt->num_codecs; i++) { const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; + if (imp->codec_type != SWITCH_CODEC_TYPE_AUDIO) { + continue; + } + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", imp->ianacode); if (!ptime) { ptime = imp->microseconds_per_frame / 1000; @@ -105,6 +117,10 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, char *ip, uint32_t por const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; uint32_t rfc_3551_sucks = imp->samples_per_second; + if (imp->codec_type != SWITCH_CODEC_TYPE_AUDIO) { + continue; + } + if (!rate) { rate = imp->samples_per_second; } @@ -136,6 +152,65 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, char *ip, uint32_t por snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=ptime:%d\n", ptime); } + + if (switch_test_flag(tech_pvt, TFLAG_VIDEO)) { + sofia_glue_tech_choose_video_port(tech_pvt); + if ((v_port = tech_pvt->adv_sdp_video_port)) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "m=video %d RTP/AVP", v_port); + sofia_glue_tech_set_video_codec(tech_pvt, 0); + + + /*****************************/ + if (tech_pvt->video_rm_encoding) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->video_pt); + } else if (tech_pvt->num_codecs) { + int i; + for (i = 0; i < tech_pvt->num_codecs; i++) { + const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; + + if (imp->codec_type != SWITCH_CODEC_TYPE_VIDEO) { + continue; + } + + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", imp->ianacode); + if (!ptime) { + ptime = imp->microseconds_per_frame / 1000; + } + } + } + + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "\n"); + + if (tech_pvt->rm_encoding) { + rate = tech_pvt->video_rm_rate; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d %s/%ld\n", tech_pvt->video_pt, tech_pvt->video_rm_encoding, tech_pvt->video_rm_rate); + if (tech_pvt->video_fmtp_out) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=fmtp:%d %s\n", tech_pvt->video_pt, tech_pvt->video_fmtp_out); + } + } else if (tech_pvt->num_codecs) { + int i; + for (i = 0; i < tech_pvt->num_codecs; i++) { + const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; + + if (imp->codec_type != SWITCH_CODEC_TYPE_VIDEO) { + continue; + } + + if (!rate) { + rate = imp->samples_per_second; + } + + + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d %s/%d\n", imp->ianacode, imp->iananame, imp->samples_per_second); + if (imp->fmtp) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=fmtp:%d %s\n", imp->ianacode, imp->fmtp); + } + } + } + } + } + /*****************************/ + tech_pvt->local_sdp_str = switch_core_session_strdup(tech_pvt->session, buf); } @@ -146,11 +221,11 @@ void sofia_glue_tech_prepare_codecs(private_object_t *tech_pvt) char *ocodec = NULL; if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { - return; + goto end; } if (tech_pvt->num_codecs) { - return; + goto end; } assert(tech_pvt->session != NULL); @@ -192,6 +267,26 @@ void sofia_glue_tech_prepare_codecs(private_object_t *tech_pvt) sizeof(tech_pvt->codecs) / sizeof(tech_pvt->codecs[0])); } + end: + + sofia_glue_check_video_codecs(tech_pvt); + +} + +void sofia_glue_check_video_codecs(private_object_t *tech_pvt) +{ + if (tech_pvt->num_codecs && !switch_test_flag(tech_pvt, TFLAG_VIDEO)) { + int i; + + for (i = 0; i < tech_pvt->num_codecs; i++) { + if (tech_pvt->codecs[i]->codec_type == SWITCH_CODEC_TYPE_VIDEO) { + tech_pvt->video_count++; + } + } + if (tech_pvt->video_count) { + switch_set_flag_locked(tech_pvt, TFLAG_VIDEO); + } + } } @@ -297,6 +392,42 @@ switch_status_t sofia_glue_tech_choose_port(private_object_t *tech_pvt) return SWITCH_STATUS_SUCCESS; } + + +switch_status_t sofia_glue_tech_choose_video_port(private_object_t *tech_pvt) +{ + char *ip = tech_pvt->profile->rtpip; + switch_channel_t *channel; + switch_port_t sdp_port; + char tmp[50]; + + channel = switch_core_session_get_channel(tech_pvt->session); + + if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA) || tech_pvt->adv_sdp_video_port) { + return SWITCH_STATUS_SUCCESS; + } + + + tech_pvt->local_sdp_video_port = switch_rtp_request_port(); + sdp_port = tech_pvt->local_sdp_video_port; + + if (tech_pvt->profile->extrtpip) { + if (sofia_glue_ext_address_lookup(&ip, &sdp_port, tech_pvt->profile->extrtpip, switch_core_session_get_pool(tech_pvt->session)) != + SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + } + + tech_pvt->adv_sdp_video_port = sdp_port; + + snprintf(tmp, sizeof(tmp), "%d", sdp_port); + switch_channel_set_variable(channel, SWITCH_LOCAL_VIDEO_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip); + switch_channel_set_variable(channel, SWITCH_LOCAL_VIDEO_PORT_VARIABLE, tmp); + + + return SWITCH_STATUS_SUCCESS; +} + switch_status_t sofia_glue_do_invite(switch_core_session_t *session) { char rpid[1024] = { 0 }; @@ -555,6 +686,77 @@ void sofia_glue_deactivate_rtp(private_object_t *tech_pvt) } switch_rtp_destroy(&tech_pvt->rtp_session); } + if (switch_rtp_ready(tech_pvt->video_rtp_session)) { + switch_rtp_destroy(&tech_pvt->video_rtp_session); + } +} + +switch_status_t sofia_glue_tech_set_video_codec(private_object_t *tech_pvt, int force) +{ + switch_channel_t *channel; + + if (tech_pvt->video_read_codec.implementation) { + if (!force) { + return SWITCH_STATUS_SUCCESS; + } + if (strcasecmp(tech_pvt->video_read_codec.implementation->iananame, tech_pvt->video_rm_encoding) || + tech_pvt->video_read_codec.implementation->samples_per_second != tech_pvt->video_rm_rate) { + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Changing Codec from %s to %s\n", + tech_pvt->video_read_codec.implementation->iananame, tech_pvt->video_rm_encoding); + switch_core_codec_destroy(&tech_pvt->video_read_codec); + switch_core_codec_destroy(&tech_pvt->video_write_codec); + //switch_core_session_reset(tech_pvt->session); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Already using %s\n", tech_pvt->video_read_codec.implementation->iananame); + return SWITCH_STATUS_SUCCESS; + } + } + + channel = switch_core_session_get_channel(tech_pvt->session); + assert(channel != NULL); + + if (!tech_pvt->video_rm_encoding) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec with no name?\n"); + return SWITCH_STATUS_FALSE; + } + + if (switch_core_codec_init(&tech_pvt->video_read_codec, + tech_pvt->video_rm_encoding, + tech_pvt->video_rm_fmtp, + tech_pvt->video_rm_rate, + 0, + //tech_pvt->video_codec_ms, + 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); + return SWITCH_STATUS_FALSE; + } else { + if (switch_core_codec_init(&tech_pvt->video_write_codec, + tech_pvt->video_rm_encoding, + tech_pvt->video_rm_fmtp, + tech_pvt->video_rm_rate, + 0,//tech_pvt->video_codec_ms, + 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); + return SWITCH_STATUS_FALSE; + } else { + int ms; + tech_pvt->video_read_frame.rate = tech_pvt->video_rm_rate; + ms = tech_pvt->video_write_codec.implementation->microseconds_per_frame / 1000; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set VIDEO Codec %s %s/%ld %d ms\n", + switch_channel_get_name(channel), tech_pvt->video_rm_encoding, tech_pvt->video_rm_rate, tech_pvt->video_codec_ms); + tech_pvt->video_read_frame.codec = &tech_pvt->video_read_codec; + + //switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec); + //switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec); + tech_pvt->fmtp_out = switch_core_session_strdup(tech_pvt->session, tech_pvt->video_write_codec.fmtp_out); + } + } + return SWITCH_STATUS_SUCCESS; } switch_status_t sofia_glue_tech_set_codec(private_object_t *tech_pvt, int force) @@ -625,6 +827,9 @@ switch_status_t sofia_glue_tech_set_codec(private_object_t *tech_pvt, int force) } + + + switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt) { int bw, ms; @@ -674,7 +879,7 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt) flags |= SWITCH_RTP_FLAG_AUTO_CNG; } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "RTP [%s] %s:%d->%s:%d codec: %u ms: %d\n", + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "AUDIO RTP [%s] %s:%d->%s:%d codec: %u ms: %d\n", switch_channel_get_name(channel), tech_pvt->local_sdp_audio_ip, tech_pvt->local_sdp_audio_port, @@ -690,9 +895,9 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt) if (switch_rtp_set_remote_address(tech_pvt->rtp_session, tech_pvt->remote_sdp_audio_ip, tech_pvt->remote_sdp_audio_port, &err) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "RTP REPORTS ERROR: [%s]\n", err); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AUDIO RTP REPORTS ERROR: [%s]\n", err); } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RTP CHANGING DEST TO: [%s:%d]\n", + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "AUDIO RTP CHANGING DEST TO: [%s:%d]\n", tech_pvt->remote_sdp_audio_ip, tech_pvt->remote_sdp_audio_port); /* Reactivate the NAT buster flag. */ switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_AUTOADJ); @@ -722,7 +927,7 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt) if ((vad_in && inb) || (vad_out && !inb)) { switch_rtp_enable_vad(tech_pvt->rtp_session, tech_pvt->session, &tech_pvt->read_codec, SWITCH_VAD_FLAG_TALKING); switch_set_flag_locked(tech_pvt, TFLAG_VAD); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RTP Engage VAD for %s ( %s %s )\n", + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "AUDIO RTP Engage VAD for %s ( %s %s )\n", switch_channel_get_name(switch_core_session_get_channel(tech_pvt->session)), vad_in ? "in" : "", vad_out ? "out" : ""); } @@ -732,6 +937,41 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt) if (tech_pvt->cng_pt) { switch_rtp_set_cng_pt(tech_pvt->rtp_session, tech_pvt->cng_pt); } + + sofia_glue_check_video_codecs(tech_pvt); + + if (switch_test_flag(tech_pvt, TFLAG_VIDEO) && tech_pvt->video_rm_encoding) { + flags = (switch_rtp_flag_t) (SWITCH_RTP_FLAG_AUTOADJ | SWITCH_RTP_FLAG_DATAWAIT | SWITCH_RTP_FLAG_NOBLOCK | SWITCH_RTP_FLAG_RAW_WRITE); + + sofia_glue_tech_set_video_codec(tech_pvt, 0); + + tech_pvt->video_rtp_session = switch_rtp_new(tech_pvt->local_sdp_audio_ip, + tech_pvt->local_sdp_video_port, + tech_pvt->remote_sdp_video_ip, + tech_pvt->remote_sdp_video_port, + tech_pvt->video_agreed_pt, + tech_pvt->video_read_codec.implementation->samples_per_frame, + 0,//tech_pvt->video_codec_ms * 1000, + (switch_rtp_flag_t) flags, + NULL, + NULL,//tech_pvt->profile->timer_name, + &err, switch_core_session_get_pool(tech_pvt->session)); + + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "VIDEO RTP [%s] %s:%d->%s:%d codec: %u ms: %d [%s]\n", + switch_channel_get_name(channel), + tech_pvt->local_sdp_audio_ip, + tech_pvt->local_sdp_video_port, + tech_pvt->remote_sdp_video_ip, + tech_pvt->remote_sdp_video_port, tech_pvt->video_agreed_pt, + 0,//tech_pvt->video_read_codec.implementation->microseconds_per_frame / 1000, + switch_rtp_ready(tech_pvt->video_rtp_session) ? "SUCCESS" : err); + + + if (switch_rtp_ready(tech_pvt->video_rtp_session)) { + switch_channel_set_flag(channel, CF_VIDEO); + } + } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "RTP REPORTS ERROR: [%s]\n", err); @@ -783,8 +1023,6 @@ switch_status_t sofia_glue_tech_media(private_object_t *tech_pvt, char *r_sdp) return SWITCH_STATUS_FALSE; } - - uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t *sdp) { uint8_t match = 0; @@ -811,6 +1049,7 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * if (switch_strlen_zero(a->a_name)) { continue; } + if (!strcasecmp(a->a_name, "sendonly")) { if (!switch_test_flag(tech_pvt, TFLAG_SIP_HOLD)) { char *stream; @@ -840,15 +1079,17 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * sdp_connection_t *connection; ptime = dptime; - for (a = m->m_attributes; a; a = a->a_next) { - if (!strcasecmp(a->a_name, "ptime") && a->a_value) { - ptime = atoi(a->a_value); - } - } + if (m->m_type == sdp_media_audio) { sdp_rtpmap_t *map; + for (a = m->m_attributes; a; a = a->a_next) { + if (!strcasecmp(a->a_name, "ptime") && a->a_value) { + ptime = atoi(a->a_value); + } + } + connection = sdp->sdp_connection; if (m->m_connections) { connection = m->m_connections; @@ -896,14 +1137,18 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * for (i = 0; i < tech_pvt->num_codecs; i++) { const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Codec Compare [%s:%d]/[%s:%d]\n", + + if (imp->codec_type != SWITCH_CODEC_TYPE_AUDIO) { + continue; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Audio Codec Compare [%s:%d]/[%s:%d]\n", rm_encoding, map->rm_pt, imp->iananame, imp->ianacode); if (map->rm_pt < 96) { match = (map->rm_pt == imp->ianacode) ? 1 : 0; } else { match = strcasecmp(rm_encoding, imp->iananame) ? 0 : 1; } - + if (match && (map->rm_rate == imp->samples_per_second)) { if (ptime && ptime * 1000 != imp->microseconds_per_frame) { near_match = imp; @@ -963,9 +1208,83 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * } } } + } else if (m->m_type == sdp_media_video) { + sdp_rtpmap_t *map; + const char *rm_encoding; + int framerate = 0; + const switch_codec_implementation_t *mimp = NULL; + int vmatch = 0, i; + + connection = sdp->sdp_connection; + if (m->m_connections) { + connection = m->m_connections; + } + + if (!connection) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find a c= line in the sdp at media or session level!\n"); + match = 0; + break; + } + + for (map = m->m_rtpmaps; map; map = map->rm_next) { + + for (a = m->m_attributes; a; a = a->a_next) { + if (!strcasecmp(a->a_name, "framerate") && a->a_value) { + framerate = atoi(a->a_value); + } + } + if (!(rm_encoding = map->rm_encoding)) { + rm_encoding = ""; + } + + for (i = 0; i < tech_pvt->num_codecs; i++) { + const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; + + if (imp->codec_type != SWITCH_CODEC_TYPE_VIDEO) { + continue; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Video Codec Compare [%s:%d]/[%s:%d]\n", + rm_encoding, map->rm_pt, imp->iananame, imp->ianacode); + if (map->rm_pt < 96) { + vmatch = (map->rm_pt == imp->ianacode) ? 1 : 0; + } else { + vmatch = strcasecmp(rm_encoding, imp->iananame) ? 0 : 1; + } + + + if (vmatch && (map->rm_rate == imp->samples_per_second)) { + mimp = imp; + break; + } else { + vmatch = 0; + } + } + + if (mimp) { + if ((tech_pvt->video_rm_encoding = switch_core_session_strdup(session, (char *) rm_encoding))) { + char tmp[50]; + tech_pvt->video_pt = (switch_payload_t) map->rm_pt; + tech_pvt->video_rm_rate = map->rm_rate; + tech_pvt->video_codec_ms = mimp->microseconds_per_frame / 1000; + tech_pvt->remote_sdp_video_ip = switch_core_session_strdup(session, (char *) connection->c_address); + tech_pvt->video_rm_fmtp = switch_core_session_strdup(session, (char *) map->rm_fmtp); + tech_pvt->remote_sdp_video_port = (switch_port_t) m->m_port; + tech_pvt->video_agreed_pt = (switch_payload_t) map->rm_pt; + snprintf(tmp, sizeof(tmp), "%d", tech_pvt->remote_sdp_video_port); + switch_channel_set_variable(channel, SWITCH_REMOTE_VIDEO_IP_VARIABLE, tech_pvt->remote_sdp_audio_ip); + switch_channel_set_variable(channel, SWITCH_REMOTE_VIDEO_PORT_VARIABLE, tmp); + } else { + vmatch = 0; + } + } + } } + } + switch_set_flag_locked(tech_pvt, TFLAG_SDP); + return match; } diff --git a/src/switch_core_event_hook.c b/src/switch_core_event_hook.c index a4f9aa5d84..bdeea8ab6b 100644 --- a/src/switch_core_event_hook.c +++ b/src/switch_core_event_hook.c @@ -118,6 +118,50 @@ SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_write_frame(switch_co } +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_video_read_frame(switch_core_session_t *session, switch_video_read_frame_hook_t video_read_frame) +{ + switch_io_event_hook_video_read_frame_t *hook, *ptr; + + assert(video_read_frame != NULL); + if ((hook = switch_core_session_alloc(session, sizeof(*hook))) != 0) { + hook->video_read_frame = video_read_frame; + if (!session->event_hooks.video_read_frame) { + session->event_hooks.video_read_frame = hook; + } else { + for (ptr = session->event_hooks.video_read_frame; ptr && ptr->next; ptr = ptr->next); + ptr->next = hook; + + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; + +} + +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_video_write_frame(switch_core_session_t *session, switch_video_write_frame_hook_t video_write_frame) +{ + switch_io_event_hook_video_write_frame_t *hook, *ptr; + + assert(video_write_frame != NULL); + if ((hook = switch_core_session_alloc(session, sizeof(*hook))) != 0) { + hook->video_write_frame = video_write_frame; + if (!session->event_hooks.video_write_frame) { + session->event_hooks.video_write_frame = hook; + } else { + for (ptr = session->event_hooks.video_write_frame; ptr && ptr->next; ptr = ptr->next); + ptr->next = hook; + + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; + +} + SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_kill_channel(switch_core_session_t *session, switch_kill_channel_hook_t kill_channel) { switch_io_event_hook_kill_channel_t *hook, *ptr; diff --git a/src/switch_core_io.c b/src/switch_core_io.c index b42b8e55fb..76db5c29b3 100644 --- a/src/switch_core_io.c +++ b/src/switch_core_io.c @@ -35,6 +35,61 @@ #include "private/switch_core.h" +SWITCH_DECLARE(switch_status_t) switch_core_session_write_video_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout, int stream_id) +{ + switch_io_event_hook_video_write_frame_t *ptr; + switch_status_t status = SWITCH_STATUS_FALSE; + switch_io_flag_t flags = 0; + if (session->endpoint_interface->io_routines->write_video_frame) { + if ((status = session->endpoint_interface->io_routines->write_video_frame(session, frame, timeout, flags, stream_id)) == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.video_write_frame; ptr; ptr = ptr->next) { + if ((status = ptr->video_write_frame(session, frame, timeout, flags, stream_id)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + } + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout, int stream_id) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + switch_io_event_hook_video_read_frame_t *ptr; + + if (session->endpoint_interface->io_routines->read_video_frame) { + if ((status = + session->endpoint_interface->io_routines->read_video_frame(session, frame, timeout, SWITCH_IO_FLAG_NOOP, stream_id)) == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.video_read_frame; ptr; ptr = ptr->next) { + if ((status = ptr->video_read_frame(session, frame, timeout, SWITCH_IO_FLAG_NOOP, stream_id)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + } + + if (status != SWITCH_STATUS_SUCCESS) { + goto done; + } + + if (!(*frame)) { + goto done; + } + + assert(session != NULL); + assert(*frame != NULL); + + if (switch_test_flag(*frame, SFF_CNG)) { + status = SWITCH_STATUS_SUCCESS; + goto done; + } + + done: + + return status; +} + + SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout, int stream_id) { switch_io_event_hook_read_frame_t *ptr; @@ -128,6 +183,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi session->raw_read_frame.samples = session->raw_read_frame.datalen / sizeof(int16_t); session->raw_read_frame.rate = read_frame->rate; session->raw_read_frame.timestamp = read_frame->timestamp; + session->raw_read_frame.ssrc = read_frame->ssrc; + session->raw_read_frame.seq = read_frame->seq; + session->raw_read_frame.m = read_frame->m; + session->raw_read_frame.payload = read_frame->payload; read_frame = &session->raw_read_frame; break; case SWITCH_STATUS_NOOP: @@ -139,6 +198,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi session->raw_read_frame.samples = session->raw_read_frame.datalen / sizeof(int16_t); session->raw_read_frame.timestamp = read_frame->timestamp; session->raw_read_frame.rate = read_frame->rate; + session->raw_read_frame.ssrc = read_frame->ssrc; + session->raw_read_frame.seq = read_frame->seq; + session->raw_read_frame.m = read_frame->m; + session->raw_read_frame.payload = read_frame->payload; read_frame = &session->raw_read_frame; status = SWITCH_STATUS_SUCCESS; break; @@ -244,6 +307,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi session->enc_read_frame.codec = session->read_codec; session->enc_read_frame.samples = session->read_codec->implementation->bytes_per_frame / sizeof(int16_t); session->enc_read_frame.timestamp = read_frame->timestamp; + session->enc_read_frame.rate = read_frame->rate; + session->enc_read_frame.ssrc = read_frame->ssrc; + session->enc_read_frame.seq = read_frame->seq; + session->enc_read_frame.m = read_frame->m; session->enc_read_frame.payload = session->read_codec->implementation->ianacode; *frame = &session->enc_read_frame; break; @@ -252,6 +319,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi session->raw_read_frame.samples = enc_frame->codec->implementation->samples_per_frame; session->raw_read_frame.timestamp = read_frame->timestamp; session->raw_read_frame.payload = enc_frame->codec->implementation->ianacode; + session->raw_read_frame.m = read_frame->m; + session->raw_read_frame.ssrc = read_frame->ssrc; + session->raw_read_frame.seq = read_frame->seq; *frame = &session->raw_read_frame; status = SWITCH_STATUS_SUCCESS; break; @@ -371,6 +441,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess session->raw_write_frame.samples = session->raw_write_frame.datalen / sizeof(int16_t); session->raw_write_frame.timestamp = frame->timestamp; session->raw_write_frame.rate = frame->rate; + session->raw_write_frame.m = frame->m; + session->raw_write_frame.ssrc = frame->ssrc; + session->raw_write_frame.seq = frame->seq; + session->raw_write_frame.payload = frame->payload; write_frame = &session->raw_write_frame; break; case SWITCH_STATUS_BREAK: @@ -500,12 +574,18 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess session->enc_write_frame.samples = enc_frame->datalen / sizeof(int16_t); session->enc_write_frame.timestamp = frame->timestamp; session->enc_write_frame.payload = session->write_codec->implementation->ianacode; + session->enc_write_frame.m = frame->m; + session->enc_write_frame.ssrc = frame->ssrc; + session->enc_write_frame.seq = frame->seq; write_frame = &session->enc_write_frame; break; case SWITCH_STATUS_NOOP: enc_frame->codec = session->write_codec; enc_frame->samples = enc_frame->datalen / sizeof(int16_t); enc_frame->timestamp = frame->timestamp; + enc_frame->m = frame->m; + enc_frame->seq = frame->seq; + enc_frame->ssrc = frame->ssrc; enc_frame->payload = enc_frame->codec->implementation->ianacode; write_frame = enc_frame; status = SWITCH_STATUS_SUCCESS; @@ -553,6 +633,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess session->enc_write_frame.codec = session->write_codec; session->enc_write_frame.samples = enc_frame->datalen / sizeof(int16_t); session->enc_write_frame.timestamp = frame->timestamp; + session->enc_write_frame.m = frame->m; + session->enc_write_frame.ssrc = frame->ssrc; + session->enc_write_frame.seq = frame->seq; session->enc_write_frame.payload = session->write_codec->implementation->ianacode; write_frame = &session->enc_write_frame; if (!session->read_resampler) { @@ -571,6 +654,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess session->enc_write_frame.codec = session->write_codec; session->enc_write_frame.samples = enc_frame->datalen / sizeof(int16_t); session->enc_write_frame.timestamp = frame->timestamp; + session->enc_write_frame.m = frame->m; + session->enc_write_frame.ssrc = frame->ssrc; + session->enc_write_frame.seq = frame->seq; session->enc_write_frame.payload = session->write_codec->implementation->ianacode; write_frame = &session->enc_write_frame; break; @@ -578,6 +664,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess enc_frame->codec = session->write_codec; enc_frame->samples = enc_frame->datalen / sizeof(int16_t); enc_frame->timestamp = frame->timestamp; + enc_frame->m = frame->m; + enc_frame->ssrc = frame->ssrc; + enc_frame->seq = frame->seq; enc_frame->payload = enc_frame->codec->implementation->ianacode; write_frame = enc_frame; status = SWITCH_STATUS_SUCCESS; diff --git a/src/switch_core_state_machine.c b/src/switch_core_state_machine.c index 363899a451..e50e8f473a 100644 --- a/src/switch_core_state_machine.c +++ b/src/switch_core_state_machine.c @@ -181,18 +181,9 @@ static void switch_core_standard_on_execute(switch_core_session_t *session) static void switch_core_standard_on_loopback(switch_core_session_t *session) { - switch_frame_t *frame; - int stream_id; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard LOOPBACK\n"); - - while (switch_channel_get_state(session->channel) == CS_LOOPBACK) { - for (stream_id = 0; stream_id < session->stream_count; stream_id++) { - if (switch_core_session_read_frame(session, &frame, -1, stream_id) == SWITCH_STATUS_SUCCESS) { - switch_core_session_write_frame(session, frame, -1, stream_id); - } - } - } + switch_ivr_session_echo(session); } static void switch_core_standard_on_transmit(switch_core_session_t *session) diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 32cf328acc..dad19053d9 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -31,6 +31,71 @@ */ #include +struct echo_helper { + switch_core_session_t *session; + int up; +}; + +static void *SWITCH_THREAD_FUNC echo_video_thread(switch_thread_t *thread, void *obj) +{ + struct echo_helper *eh = obj; + switch_core_session_t *session = eh->session; + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_status_t status; + switch_frame_t *read_frame; + + eh->up = 1; + while(switch_channel_ready(channel) && switch_channel_get_state(channel) == CS_LOOPBACK) { + status = switch_core_session_read_video_frame(session, &read_frame, -1, 0); + + if (!SWITCH_READ_ACCEPTABLE(status)) { + break; + } + + switch_core_session_write_video_frame(session, read_frame, -1, 0); + + } + eh->up = 0; + return NULL; +} + +SWITCH_DECLARE(void) switch_ivr_session_echo(switch_core_session_t *session) +{ + switch_status_t status; + switch_frame_t *read_frame; + struct echo_helper eh = {0}; + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_thread_t *thread; + switch_threadattr_t *thd_attr = NULL; + + + switch_channel_answer(channel); + + if (switch_channel_test_flag(channel, CF_VIDEO)) { + eh.session = session; + switch_threadattr_create(&thd_attr, switch_core_session_get_pool(session)); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, echo_video_thread, &eh, switch_core_session_get_pool(session)); + } + + while(switch_channel_ready(channel) && switch_channel_get_state(channel) == CS_LOOPBACK) { + status = switch_core_session_read_frame(session, &read_frame, -1, 0); + if (!SWITCH_READ_ACCEPTABLE(status)) { + break; + } + switch_core_session_write_frame(session, read_frame, -1, 0); + } + + if (eh.up) { + while(eh.up) { + switch_yield(1000); + } + } +} + + + static switch_bool_t record_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) { switch_file_handle_t *fh = (switch_file_handle_t *) user_data; diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 0b54c8f925..c18955512b 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -352,8 +352,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s rtp_session->sock = new_sock; new_sock = NULL; - if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_USE_TIMER) - || switch_test_flag(rtp_session, SWITCH_RTP_FLAG_NOBLOCK)) { + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_USE_TIMER) || switch_test_flag(rtp_session, SWITCH_RTP_FLAG_NOBLOCK)) { switch_socket_opt_set(rtp_session->sock, SWITCH_SO_NONBLOCK, TRUE); switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_NOBLOCK); } @@ -803,7 +802,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ while (switch_rtp_ready(rtp_session)) { bytes = sizeof(rtp_msg_t); status = switch_socket_recvfrom(rtp_session->from_addr, rtp_session->sock, 0, (void *) &rtp_session->recv_msg, &bytes); - + if (!SWITCH_STATUS_IS_BREAK(status) && rtp_session->timer.interval) { switch_core_timer_step(&rtp_session->timer); } @@ -900,7 +899,11 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ if (status == SWITCH_STATUS_BREAK || bytes == 0) { if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_DATAWAIT)) { - switch_yield((rtp_session->ms_per_packet / 1000) * 750); + if (rtp_session->ms_per_packet) { + switch_yield((rtp_session->ms_per_packet / 1000) * 750); + } else { + switch_yield(1000); + } continue; } return 0; @@ -1136,13 +1139,16 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read_frame(switch_rtp_t *rtp } bytes = rtp_common_read(rtp_session, &frame->payload, &frame->flags); - + frame->data = rtp_session->recv_msg.body; frame->packet = &rtp_session->recv_msg; frame->packetlen = bytes; frame->source = __FILE__; frame->flags |= SFF_RAW_RTP; frame->timestamp = ntohl(rtp_session->recv_msg.header.ts); + frame->seq = ntohs(rtp_session->recv_msg.header.seq); + frame->ssrc = ntohl(rtp_session->recv_msg.header.ssrc); + frame->m = rtp_session->recv_msg.header.m ? SWITCH_TRUE : SWITCH_FALSE; if (bytes < 0) { frame->datalen = 0; @@ -1153,8 +1159,11 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read_frame(switch_rtp_t *rtp } else { bytes -= rtp_header_len; } - + frame->datalen = bytes; + + + return SWITCH_STATUS_SUCCESS; } @@ -1350,7 +1359,6 @@ static int rtp_common_write(switch_rtp_t *rtp_session, void *data, uint32_t data switch_core_timer_check(&rtp_session->timer); rtp_session->last_write_samplecount = rtp_session->timer.samplecount; } - switch_socket_sendto(rtp_session->sock, rtp_session->remote_addr, 0, (void *) send_msg, &bytes); } @@ -1478,6 +1486,12 @@ SWITCH_DECLARE(int) switch_rtp_write_frame(switch_rtp_t *rtp_session, switch_fra payload = rtp_session->payload; } + if (switch_test_flag(frame, SFF_RTP_HEADER)) { + return switch_rtp_write_manual(rtp_session, frame->data, frame->datalen, frame->m, frame->payload, + frame->timestamp, frame->seq, frame->ssrc, &frame->flags); + } + + if (fwd) { data = frame->packet; len = frame->packetlen; @@ -1551,7 +1565,7 @@ SWITCH_DECLARE(int) switch_rtp_write_manual(switch_rtp_t *rtp_session, } return (int) bytes; - //return rtp_common_write(rtp_session, (void *) &send_msg, rtp_header_len + datalen, m, payload, NULL); + //return rtp_common_write(rtp_session, (void *) &send_msg, rtp_header_len + datalen, m, payload); } SWITCH_DECLARE(uint32_t) switch_rtp_get_ssrc(switch_rtp_t *rtp_session)