]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Added T.38 fax support to mod_opal
authorRobert Jongbloed <robertj@voxlucida.com.au>
Wed, 29 Aug 2012 02:43:46 +0000 (12:43 +1000)
committerRobert Jongbloed <robertj@voxlucida.com.au>
Wed, 19 Sep 2012 02:32:15 +0000 (12:32 +1000)
src/mod/endpoints/mod_opal/mod_opal.cpp
src/mod/endpoints/mod_opal/mod_opal.h

index cf160ba527fd3a969a7c8596b14b94f9abda0f2f..a8b4b70561dad7b4119145938e14855012a231cd 100644 (file)
@@ -743,6 +743,29 @@ void FSConnection::SetCodecs()
 \r
         m_switchMediaFormats += switchFormat;\r
     }\r
+\r
+#if HAVE_T38\r
+    OpalMediaFormat t38 = OpalT38;\r
+\r
+    /* We need to have a T.38 options for TCS, but may be before the\r
+       spandsp_mod has set it us. So, if not, we actually give to spandsp_mod. */\r
+    switch_t38_options_t *t38_options = (switch_t38_options_t *)switch_channel_get_private(m_fsChannel, "t38_options");\r
+    if (t38_options == NULL)\r
+      SetT38OptionsFromMediaFormat(t38);\r
+    else {\r
+      t38.SetOptionInteger("T38FaxVersion", t38_options->T38FaxVersion);\r
+      t38.SetOptionInteger("T38MaxBitRate", t38_options->T38MaxBitRate);\r
+      t38.SetOptionBoolean("T38FaxFillBitRemoval", t38_options->T38FaxFillBitRemoval);\r
+      t38.SetOptionBoolean("T38FaxTranscodingMMR", t38_options->T38FaxTranscodingMMR);\r
+      t38.SetOptionBoolean("T38FaxTranscodingJBIG", t38_options->T38FaxTranscodingJBIG);\r
+      t38.SetOptionValue("T38FaxRateManagement", t38_options->T38FaxRateManagement);\r
+      t38.SetOptionInteger("T38Version", t38_options->T38FaxMaxBuffer);\r
+      t38.SetOptionInteger("T38Version", t38_options->T38FaxMaxDatagram);\r
+      t38.SetOptionValue("T38FaxUdpEC", t38_options->T38FaxUdpEC);\r
+    }\r
+\r
+    m_switchMediaFormats += t38;\r
+#endif // HAVE_T38\r
 }\r
 \r
 \r
@@ -952,6 +975,23 @@ switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg
         ownerCall.Transfer(msg->string_arg, GetOtherPartyConnection());\r
         break;\r
 \r
+#if HAVE_T38\r
+    case SWITCH_MESSAGE_INDICATE_REQUEST_IMAGE_MEDIA:\r
+      {\r
+        PSafePtr<OpalConnection> other = GetOtherPartyConnection();\r
+        if (other == NULL || !other->SwitchT38(true)) {\r
+            PTRACE(1, "mod_opal\tMode change request to T.38 failed");\r
+        }\r
+        break;\r
+      }\r
+\r
+    case SWITCH_MESSAGE_INDICATE_T38_DESCRIPTION:\r
+        break;\r
+\r
+    case SWITCH_MESSAGE_INDICATE_UDPTL_MODE:\r
+        break;\r
+#endif // HAVE_T38\r
+\r
     default:\r
         PTRACE(3, "mod_opal\tReceived unhandled message " << msg->message_id << " on connection " << *this);\r
     }\r
@@ -977,6 +1017,76 @@ bool FSConnection::WaitForMedia()
 }\r
 \r
 \r
+#if HAVE_T38\r
+void FSConnection::SetT38OptionsFromMediaFormat(const OpalMediaFormat & mediaFormat)\r
+{\r
+    switch_t38_options_t *t38_options = (switch_t38_options_t *)switch_channel_get_private(m_fsChannel, "t38_options");\r
+    if (t38_options == NULL)\r
+      t38_options = (switch_t38_options_t *)switch_core_session_alloc(m_fsSession, sizeof(switch_t38_options_t));\r
+\r
+    PString value;\r
+    mediaFormat.GetOptionValue("T38FaxRateManagement", value);\r
+    t38_options->T38FaxRateManagement = switch_core_session_strdup(m_fsSession, value);\r
+\r
+    mediaFormat.GetOptionValue("T38FaxUdpEC", value);\r
+    t38_options->T38FaxUdpEC = switch_core_session_strdup(m_fsSession, value);\r
+\r
+    t38_options->T38MaxBitRate = mediaFormat.GetOptionInteger("T38MaxBitRate", 9600);\r
+    t38_options->T38FaxMaxBuffer = mediaFormat.GetOptionInteger("T38FaxMaxBuffer", 2000);\r
+    t38_options->T38FaxMaxDatagram = mediaFormat.GetOptionInteger("T38FaxMaxDatagram", 528);\r
+\r
+    t38_options->T38FaxFillBitRemoval = mediaFormat.GetOptionBoolean("T38FaxFillBitRemoval") ? SWITCH_TRUE : SWITCH_FALSE;\r
+    t38_options->T38FaxTranscodingMMR = mediaFormat.GetOptionBoolean("T38FaxTranscodingMMR") ? SWITCH_TRUE : SWITCH_FALSE;\r
+    t38_options->T38FaxTranscodingJBIG = mediaFormat.GetOptionBoolean("T38FaxTranscodingJBIG") ? SWITCH_TRUE : SWITCH_FALSE;\r
+\r
+    t38_options->T38VendorInfo = switch_core_session_strdup(m_fsSession, mediaFormat.GetOptionString("T38VendorInfo"));\r
+\r
+    //t38_options->remote_ip = switch_core_session_strdup(session, mediaFormat.something);\r
+    //t38_options->remote_port = mediaFormat.something;\r
+\r
+    switch_channel_set_private(m_fsChannel, "t38_options", t38_options);\r
+}\r
+\r
+\r
+void FSConnection::OnSwitchedT38(bool toT38, bool success)\r
+{\r
+    PTRACE(3, "mod_opal\tMode change request to fax succeeded");\r
+    OnSwitchingT38(toT38 && success);\r
+}\r
+\r
+\r
+void FSConnection::OnSwitchingT38(bool toT38)\r
+{\r
+    if (!toT38) {\r
+        switch_channel_set_private(m_fsChannel, "t38_options", NULL);\r
+        switch_channel_clear_app_flag_key("T38", m_fsChannel, CF_APP_T38);\r
+        switch_channel_clear_app_flag_key("T38", m_fsChannel, CF_APP_T38_REQ);\r
+        switch_channel_set_app_flag_key("T38", m_fsChannel, CF_APP_T38_FAIL);\r
+        return;\r
+    }\r
+\r
+    PSafePtr<OpalConnection> other = GetOtherPartyConnection();\r
+    if (other == NULL)\r
+        return;\r
+\r
+    OpalMediaFormatList otherFormats = other->GetMediaFormats();\r
+    OpalMediaFormatList::const_iterator t38 = otherFormats.FindFormat(OpalT38);\r
+    if (t38 == otherFormats.end())\r
+        return;\r
+\r
+    SetT38OptionsFromMediaFormat(*t38);\r
+\r
+    switch_channel_set_variable(m_fsChannel, "has_t38", "true");\r
+    switch_channel_set_app_flag_key("T38", m_fsChannel, CF_APP_T38);\r
+\r
+    switch_channel_execute_on(m_fsChannel, "opal_execute_on_image");\r
+    switch_channel_api_on(m_fsChannel, "opal_api_on_image");\r
+\r
+    return;\r
+}\r
+#endif // HAVE_T38\r
+\r
+\r
 switch_status_t FSConnection::receive_event(switch_event_t *event)\r
 {\r
     PTRACE(4, "mod_opal\tReceived event " << event->event_id << " on connection " << *this);\r
@@ -1052,11 +1162,19 @@ PBoolean FSMediaStream::Open()
         return false;\r
 \r
     bool isAudio;\r
-    if (mediaFormat.GetMediaType() == OpalMediaType::Audio()) {\r
+    OpalMediaType mediaType = mediaFormat.GetMediaType();\r
+    if (mediaType == OpalMediaType::Audio())\r
         isAudio = true;\r
-    } else if (mediaFormat.GetMediaType() == OpalMediaType::Video()) {\r
+    else if (mediaType == OpalMediaType::Video())\r
         isAudio = false;\r
-    } else {\r
+#if HAVE_T38\r
+    else if (mediaType == OpalMediaType::Fax()) {\r
+        m_readFrame.flags = SFF_UDPTL_PACKET;\r
+        return true;\r
+    }\r
+#endif\r
+    else {\r
+        PTRACE(1, "mod_opal\tUnsupported media type: " << mediaType);\r
         return false;\r
     }\r
 \r
@@ -1083,13 +1201,13 @@ PBoolean FSMediaStream::Open()
                                    switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {\r
             PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel)\r
                    << " cannot initialise " << (IsSink()? "read" : "write") << ' '\r
-                   << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);\r
+                   << mediaType << " codec " << mediaFormat << " for connection " << *this);\r
             switch_channel_hangup(fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);\r
             return false;\r
         }\r
         PTRACE(2, "mod_opal\t" << switch_channel_get_name(fsChannel)\r
                << " unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' '\r
-               << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);\r
+               << mediaType << " codec " << mediaFormat << " for connection " << *this);\r
     }\r
 \r
     if (IsSink()) {\r
@@ -1102,7 +1220,7 @@ PBoolean FSMediaStream::Open()
                                        switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {\r
                 PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel)\r
                        << " timer init failed on " << (IsSink()? "read" : "write") << ' '\r
-                       << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);\r
+                       << mediaType << " codec " << mediaFormat << " for connection " << *this);\r
                 switch_core_codec_destroy(m_switchCodec);\r
                 m_switchCodec = NULL;\r
                 return false;\r
@@ -1122,7 +1240,7 @@ PBoolean FSMediaStream::Open()
 \r
     PTRACE(3, "mod_opal\t" << switch_channel_get_name(fsChannel)\r
            << " initialised " << (IsSink()? "read" : "write") << ' '\r
-           << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);\r
+           << mediaType << " codec " << mediaFormat << " for connection " << *this);\r
 \r
     return OpalMediaStream::Open();\r
 }\r
@@ -1207,9 +1325,14 @@ switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag
         }\r
     }\r
 \r
+    *frame = &m_readFrame;\r
+\r
     m_readFrame.packet    = m_readRTP.GetPointer();\r
     m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readFrame.datalen;\r
 \r
+    if ((m_readFrame.flags & (SFF_UDPTL_PACKET|SFF_RAW_RTP)) != 0)\r
+        return SWITCH_STATUS_SUCCESS;\r
+\r
 #if IMPLEMENT_MULTI_FAME_AUDIO\r
     // Repackage frames in incoming packet to agree with what FS expects.\r
     // Not implmented yet!!!!!!!!!\r
@@ -1228,8 +1351,6 @@ switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag
                             m_readFrame.payload == RTP_DataFrame::CN ||\r
                             m_readFrame.payload == RTP_DataFrame::Cisco_CN ? SFF_CNG : 0;\r
 \r
-    *frame = &m_readFrame;\r
-\r
     return SWITCH_STATUS_SUCCESS;\r
 }\r
 \r
@@ -1244,7 +1365,7 @@ switch_status_t FSMediaStream::write_frame(const switch_frame_t *frame, switch_i
         return SWITCH_STATUS_SUCCESS;\r
     }\r
 \r
-    if ((frame->flags & SFF_RAW_RTP) != 0) {\r
+    if ((frame->flags & (SFF_UDPTL_PACKET|SFF_RAW_RTP)) != 0) {\r
         RTP_DataFrame rtp((const BYTE *)frame->packet, frame->packetlen, false);\r
         return mediaPatch->PushFrame(rtp) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;\r
     }\r
@@ -1257,11 +1378,11 @@ switch_status_t FSMediaStream::write_frame(const switch_frame_t *frame, switch_i
     /* Not sure what FS is going to give us!\r
        Suspect it depends on the mod on the other side sending it. */\r
     if (frame->timestamp != 0)\r
-      timestamp = frame->timestamp;\r
+        timestamp = frame->timestamp;\r
     else if (frame->samples != 0)\r
-      timestamp += frame->samples;\r
+        timestamp += frame->samples;\r
     else\r
-      timestamp += m_switchCodec->implementation->samples_per_packet;\r
+        timestamp += m_switchCodec->implementation->samples_per_packet;\r
     rtp.SetTimestamp(timestamp);\r
 \r
     return mediaPatch->PushFrame(rtp) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;\r
index 5edc21f8a0526d709f3e23c37e6013b5b30eb914..2b7a553d51ce63cc16d58746f51cc754a926877e 100644 (file)
 \r
 #define MODNAME "mod_opal"\r
 \r
+#ifndef OPAL_CHECK_VERSION\r
+  #define OPAL_CHECK_VERSION(a,b,c) 0\r
+#endif\r
+\r
+#define HAVE_T38 (OPAL_CHECK_VERSION(3,11,2) && OPAL_T38_CAPABILITY)\r
+\r
 \r
 class FSEndPoint;\r
 class FSManager;\r
@@ -248,6 +254,10 @@ class FSConnection : public OpalLocalConnection
     virtual void OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch);\r
     virtual OpalMediaFormatList GetMediaFormats() const;\r
     virtual PBoolean SendUserInputTone(char tone, unsigned duration);\r
+#if HAVE_T38\r
+    virtual void OnSwitchedT38(bool toT38, bool success);\r
+    virtual void OnSwitchingT38(bool toT38);\r
+#endif\r
 \r
     DECLARE_CALLBACK0(on_init);\r
     DECLARE_CALLBACK0(on_destroy);\r
@@ -294,6 +304,9 @@ class FSConnection : public OpalLocalConnection
   protected:\r
     void SetCodecs();\r
     bool WaitForMedia();\r
+#if HAVE_T38\r
+    void SetT38OptionsFromMediaFormat(const OpalMediaFormat & mediaFormat);\r
+#endif\r
 \r
     switch_status_t read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags);\r
     switch_status_t write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags);\r