Browse Source

obs-outputs: Add output error messages for RTMP

Richard Stanway 8 years ago
parent
commit
92a258690e

+ 8 - 0
plugins/obs-outputs/data/locale/en-US.ini

@@ -3,3 +3,11 @@ RTMPStream.DropThreshold="Drop Threshold (milliseconds)"
 FLVOutput="FLV File Output"
 FLVOutput.FilePath="File Path"
 Default="Default"
+
+ConnectionTimedOut="The connection timed out. Make sure you've configured a valid streaming service and no firewall is blocking the connection."
+PermissionDenied="The connection was blocked. Check your firewall / anti-virus settings to make sure OBS is allowed full internet access."
+ConnectionAborted="The connection was aborted. This usually indicates internet connection problems between you and the streaming service."
+ConnectionReset="The connection was reset by the peer. This usually indicates internet connection problems between you and the streaming service."
+HostNotFound="Hostname not found. Make sure you entered a valid streaming server and your internet connection / DNS are working correctly."
+NoData="Hostname found, but no data of the requested type. This can occur if you have bound to an IPv6 address and your streaming service only has IPv4 addresses (see  Settings / Advanced)."
+AddressNotAvailable="Address not available. You may have tried to bind to an invalid IP address (see  Settings / Advanced)."

+ 37 - 15
plugins/obs-outputs/librtmp/rtmp.c

@@ -652,7 +652,7 @@ int RTMP_AddStream(RTMP *r, const char *playpath)
 }
 
 static int
-add_addr_info(struct sockaddr_storage *service, socklen_t *addrlen, AVal *host, int port, socklen_t addrlen_hint)
+add_addr_info(struct sockaddr_storage *service, socklen_t *addrlen, AVal *host, int port, socklen_t addrlen_hint, int *socket_error)
 {
     char *hostname;
     int ret = TRUE;
@@ -693,6 +693,7 @@ add_addr_info(struct sockaddr_storage *service, socklen_t *addrlen, AVal *host,
 #define gai_strerrorA gai_strerror
 #endif
         RTMP_Log(RTMP_LOGERROR, "Could not resolve %s: %s (%d)", hostname, gai_strerrorA(GetSockError()), GetSockError());
+        *socket_error = GetSockError();
         ret = FALSE;
         goto finish;
     }
@@ -708,23 +709,30 @@ add_addr_info(struct sockaddr_storage *service, socklen_t *addrlen, AVal *host,
         }
     }
 
-	if (!*addrlen)
-	{
-		for (ptr = result; ptr != NULL; ptr = ptr->ai_next)
-		{
+    if (!*addrlen)
+    {
+        for (ptr = result; ptr != NULL; ptr = ptr->ai_next)
+        {
             if (ptr->ai_family == AF_INET6 && (!addrlen_hint || ptr->ai_addrlen == addrlen_hint))
-			{
-				memcpy(service, ptr->ai_addr, ptr->ai_addrlen);
-				*addrlen = (socklen_t)ptr->ai_addrlen;
-				break;
-			}
-		}
-	}
+            {
+                memcpy(service, ptr->ai_addr, ptr->ai_addrlen);
+                *addrlen = (socklen_t)ptr->ai_addrlen;
+                break;
+            }
+        }
+    }
 
     freeaddrinfo(result);
 
     if (service->ss_family == AF_UNSPEC || *addrlen == 0)
     {
+        // since we're handling multiple addresses internally, fake the correct error response
+#ifdef _WIN32
+        *socket_error = WSANO_DATA;
+#else
+        *socket_error = ENODATA;
+#endif
+
         RTMP_Log(RTMP_LOGERROR, "Could not resolve server '%s': no valid address found", hostname);
         ret = FALSE;
         goto finish;
@@ -770,6 +778,7 @@ RTMP_Connect0(RTMP *r, struct sockaddr * service, socklen_t addrlen)
                 int err = GetSockError();
                 RTMP_Log(RTMP_LOGERROR, "%s, failed to bind socket: %s (%d)",
                          __FUNCTION__, socketerror(err), err);
+                r->last_error_code = err;
                 RTMP_Close(r);
                 return FALSE;
             }
@@ -789,6 +798,7 @@ RTMP_Connect0(RTMP *r, struct sockaddr * service, socklen_t addrlen)
             else
                 RTMP_Log(RTMP_LOGERROR, "%s, failed to connect socket: %s (%d)",
                      __FUNCTION__, socketerror(err), err);
+            r->last_error_code = err;
             RTMP_Close(r);
             return FALSE;
         }
@@ -912,6 +922,8 @@ RTMP_Connect(RTMP *r, RTMPPacket *cp)
     struct sockaddr_storage service;
     socklen_t addrlen = 0;
     socklen_t addrlen_hint = 0;
+    int socket_error = 0;
+
     if (!r->Link.hostname.av_len)
         return FALSE;
 
@@ -920,6 +932,7 @@ RTMP_Connect(RTMP *r, RTMPPacket *cp)
     h = gethostbyname("localhost");
     if (!h && GetLastError() == WSAHOST_NOT_FOUND)
     {
+        r->last_error_code = WSAHOST_NOT_FOUND;
         RTMP_Log(RTMP_LOGERROR, "RTMP_Connect: Connection test failed. This error is likely caused by Comodo Internet Security running OBS in sandbox mode. Please add OBS to the Comodo automatic sandbox exclusion list, restart OBS and try again (11001).");
         return FALSE;
     }
@@ -933,14 +946,20 @@ RTMP_Connect(RTMP *r, RTMPPacket *cp)
     if (r->Link.socksport)
     {
         /* Connect via SOCKS */
-        if (!add_addr_info(&service, &addrlen, &r->Link.sockshost, r->Link.socksport, addrlen_hint))
+        if (!add_addr_info(&service, &addrlen, &r->Link.sockshost, r->Link.socksport, addrlen_hint, &socket_error))
+        {
+            r->last_error_code = socket_error;
             return FALSE;
+        }
     }
     else
     {
         /* Connect directly */
-        if (!add_addr_info(&service, &addrlen, &r->Link.hostname, r->Link.port, addrlen_hint))
+        if (!add_addr_info(&service, &addrlen, &r->Link.hostname, r->Link.port, addrlen_hint, &socket_error))
+        {
+            r->last_error_code = socket_error;
             return FALSE;
+        }
     }
 
     if (!RTMP_Connect0(r, (struct sockaddr *)&service, addrlen))
@@ -957,9 +976,10 @@ SocksNegotiate(RTMP *r)
     unsigned long addr;
     struct sockaddr_storage service;
     socklen_t addrlen = 0;
+    int socket_error = 0;
     memset(&service, 0, sizeof(service));
 
-    add_addr_info(&service, &addrlen, &r->Link.hostname, r->Link.port, 0);
+    add_addr_info(&service, &addrlen, &r->Link.hostname, r->Link.port, 0, &socket_error);
 
     // not doing IPv6 socks
     if (service.ss_family == AF_INET6)
@@ -1442,6 +1462,8 @@ WriteN(RTMP *r, const char *buffer, int n)
             if (sockerr == EINTR && !RTMP_ctrlC)
                 continue;
 
+            r->last_error_code = sockerr;
+
             RTMP_Close(r);
             n = 1;
             break;

+ 1 - 0
plugins/obs-outputs/librtmp/rtmp.h

@@ -325,6 +325,7 @@ extern "C"
         RTMPSockBuf m_sb;
         RTMP_LNK Link;
         int connect_time_ms;
+        int last_error_code;
     } RTMP;
 
     int RTMP_ParseURL(const char *url, int *protocol, AVal *host,

+ 66 - 1
plugins/obs-outputs/rtmp-stream.c

@@ -378,6 +378,64 @@ static inline bool can_shutdown_stream(struct rtmp_stream *stream,
 	return timeout || packet->sys_dts_usec >= (int64_t)stream->stop_ts;
 }
 
+static void set_output_error(struct rtmp_stream *stream)
+{
+	const char *msg = NULL;
+#ifdef _WIN32
+	switch (stream->rtmp.last_error_code)
+	{
+	case WSAETIMEDOUT:
+		msg = obs_module_text("ConnectionTimedOut");
+		break;
+	case WSAEACCES:
+		msg = obs_module_text("PermissionDenied");
+		break;
+	case WSAECONNABORTED:
+		msg = obs_module_text("ConnectionAborted");
+		break;
+	case WSAECONNRESET:
+		msg = obs_module_text("ConnectionReset");
+		break;
+	case WSAHOST_NOT_FOUND:
+		msg = obs_module_text("HostNotFound");
+		break;
+	case WSANO_DATA:
+		msg = obs_module_text("NoData");
+		break;
+	case WSAEADDRNOTAVAIL:
+		msg = obs_module_text("AddressNotAvailable");
+		break;
+	}
+#else
+	switch (stream->rtmp.last_error_code)
+	{
+	case ETIMEDOUT:
+		msg = obs_module_text("ConnectionTimedOut");
+		break;
+	case EACCES:
+		msg = obs_module_text("PermissionDenied");
+		break;
+	case ECONNABORTED:
+		msg = obs_module_text("ConnectionAborted");
+		break;
+	case ECONNRESET:
+		msg = obs_module_text("ConnectionReset");
+		break;
+	case HOST_NOT_FOUND:
+		msg = obs_module_text("HostNotFound");
+		break;
+	case NO_DATA:
+		msg = obs_module_text("NoData");
+		break;
+	case EADDRNOTAVAIL:
+		msg = obs_module_text("AddressNotAvailable");
+		break;
+	}
+#endif
+
+	obs_output_set_last_error(stream->output, msg);
+}
+
 static void *send_thread(void *data)
 {
 	struct rtmp_stream *stream = data;
@@ -428,6 +486,7 @@ static void *send_thread(void *data)
 		stream->rtmp.m_bCustomSend = false;
 	}
 
+	set_output_error(stream);
 	RTMP_Close(&stream->rtmp);
 
 	if (!stopping(stream)) {
@@ -571,8 +630,10 @@ static int init_send(struct rtmp_stream *stream)
 		int one = 1;
 #ifdef _WIN32
 		if (ioctlsocket(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) {
+			stream->rtmp.last_error_code = WSAGetLastError();
 #else
 		if (ioctl(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) {
+			stream->rtmp.last_error_code = errno;
 #endif
 			warn("Failed to set non-blocking socket");
 			return OBS_OUTPUT_ERROR;
@@ -644,6 +705,7 @@ static int init_send(struct rtmp_stream *stream)
 		if (!send_meta_data(stream, idx++, &next)) {
 			warn("Disconnected while attempting to connect to "
 			     "server.");
+			set_output_error(stream);
 			return OBS_OUTPUT_DISCONNECTED;
 		}
 	}
@@ -767,8 +829,11 @@ static int try_connect(struct rtmp_stream *stream)
 	win32_log_interface_type(stream);
 #endif
 
-	if (!RTMP_Connect(&stream->rtmp, NULL))
+	if (!RTMP_Connect(&stream->rtmp, NULL)) {
+		set_output_error(stream);
 		return OBS_OUTPUT_CONNECT_FAILED;
+	}
+
 	if (!RTMP_ConnectStream(&stream->rtmp, 0))
 		return OBS_OUTPUT_INVALID_STREAM;
 

+ 2 - 0
plugins/obs-outputs/rtmp-windows.c

@@ -81,6 +81,7 @@ static bool socket_event(struct rtmp_stream *stream, bool *can_write,
 						"Socket error, recv() returned "
 						"%d, GetLastError() %d",
 						ret, err_code);
+				stream->rtmp.last_error_code = err_code;
 				fatal_sock_shutdown(stream);
 				return false;
 			}
@@ -215,6 +216,7 @@ static enum data_ret write_data(struct rtmp_stream *stream, bool *can_write,
 					ret, err_code);
 
 			pthread_mutex_unlock(&stream->write_buf_mutex);
+			stream->rtmp.last_error_code = err_code;
 			fatal_sock_shutdown(stream);
 			return RET_FATAL;
 		}