提交 0c819da6 authored 作者: Seven Du's avatar Seven Du 提交者: Michael Jerris

FS-7585: add rtmp video support

上级 e1874797
<html>
<head>
<META http-equiv="Content-Type" content="text/html;charset=utf-8"></META>
<TITLE>FreeSWITCH Video Flash Phone Demo</TITLE>
<script type="text/javascript" src="swfobject.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" charset="utf-8">
var flashvars = {
rtmp_url: 'rtmp://' + document.location.hostname + '/phone',
local_loopback: 2, // x pos if local video
buffer_time: 0
};
var params = {
allowScriptAccess: 'always',
wmode: 'window'
};
var current_uuid = "uuid";
var flash;
function log(msg) {
if ('console' in window) console.log(msg);
}
function login() {
flash.login($('#user').val(), '1234');
}
function register() {
flash.register($('#user').val(), 'Test');
}
function unregister() {
flash.register($('#user').val(), 'Test');
}
function makeCall() {
flash.makeCall($('#dest').val(), '', {want_video: "true"});
}
function hangup() {
flash.hangup(current_uuid);
}
function answer() {
flash.answer(current_uuid);
}
function settings() {
flash.showPrivacy();
}
function send_dtmf(digit) {
flash.sendDTMF(digit, 2000);
}
function onDisplayUpdate(uuid, name, number){
log("DisplayUpdate -- uuid: " + uuid + " name: " + name + " number: " + number);
}
function onCallState(uuid, state){
log("CallState -- uuid: " + uuid + " state: " + state);
$('#status').html(state);
}
function onIncomingCall(uuid, name, number, account, evt){
log("IncomingCall -- uuid: " + uuid + " name: " + name + " number: " + number + " account: " + account);
log(evt);
if (current_uuid == "uuid") {
var want_video = "";
current_uuid = uuid;
if (typeof(evt) === "object" && evt.want_video == "true") {
want_video = " (Video)";
}
$('#incoming_call').html("Incoming call " + uuid + " from '" + name + "' <" + number + ">" + want_video);
} else {
$("#flash")[0].hangup(uuid);
}
}
function onDisconnected(){
log("socket disconnected");
$('#status').html('Disconnected');
$('#reconnect').show();
}
function onMakeCall(uuid, number, account){
log("MakeCall -- uuid: " + uuid + " account: " + account + " number: " + number);
current_uuid = uuid;
}
function onHangup(uuid, cause){
log("Hangup -- uuid: " + uuid + " cause: " + cause);
current_uuid = "uuid";
$('#status').html("Hangup " + cause);
}
function onDebug(message){
log("debug -- " + message);
}
function onAttach(uuid){
log("Attach -- " + uuid);
}
function onConnected(session_id){
log("Connected -- sessionid: " + session_id);
$('#session_id').html(session_id);
$('#status').html('Connected');
$('#reconnect').hide();
}
function onLogin(status, user, domain){
log("Login -- status: " + status + " user: " + user + " domain: " + domain);
}
function onLogout(user, domain){
log("Logout -- user: " + user + " domain: " + domain);
}
function onInit(){console.log('initing...');}
</script>
</head>
<body>
<div id="dialpad" style="float:left;width:300px">
<table border="1" cellspacing="5" cellpadding="5" width="200">
<tr>
<th colspan = 3>Flash Phone</th>
</tr><tr>
<td colspan = 3>
<input type="text" name="x" value="9196" id="dest" size = "10">
</td>
</tr><tr>
<td><input type="button" name="k7" value="7" id="k7" onclick="send_dtmf('7')"></td>
<td><input type="button" name="k7" value="8" id="k7" onclick="send_dtmf('8')"></td>
<td><input type="button" name="k7" value="9" id="k7" onclick="send_dtmf('9')"></td>
</tr><tr>
<td><input type="button" name="k7" value="4" id="k7" onclick="send_dtmf('4')"></td>
<td><input type="button" name="k7" value="5" id="k7" onclick="send_dtmf('5')"></td>
<td><input type="button" name="k7" value="6" id="k7" onclick="send_dtmf('6')"></td>
</tr><tr>
<td><input type="button" name="k7" value="1" id="k7" onclick="send_dtmf('1')"></td>
<td><input type="button" name="k7" value="2" id="k7" onclick="send_dtmf('2')"></td>
<td><input type="button" name="k7" value="3" id="k7" onclick="send_dtmf('3')"></td>
</tr><tr>
<td><input type="button" name="k7" value="*" id="k7" onclick="send_dtmf('*')"></td>
<td><input type="button" name="k7" value="0" id="k7" onclick="send_dtmf('0')"></td>
<td><input type="button" name="k7" value="#" id="k7" onclick="send_dtmf('#')"></td>
</tr><tr>
<td colspan="3">
<span id="status">Ready</span>
<span id="reconnect" style="display:none">
<a href='#' onclick="flash.connect();return false;">Reconnect</a>
</span>
</td>
</tr>
</table>
</div>
<div>
SessionID: <span id = "session_id"></span><br>
Incoming: <span id = "incoming_call"></span>
<br>
<input type="text" name="user" value="1019@192.168.7.4" id="user">
<input type="button" name="Call" value="Login" onclick = "login()" id="Login">
<input type="button" name="Register" value="Register" onclick = "register()" id="Register">
<input type="button" name="Register" value="UnRegister" onclick = "unregister()" id="UnRegister">
<br>
<input type="button" name="Call" value="Call" onclick = "makeCall()" id="Call">
<input type="button" name="Call" value="Hangup" onclick = "hangup()" id="Hangup">
<input type="button" name="some_name" value="Answer" id="some_name" onclick="answer()">
<input type="button" name="some_name" value="Settings" id="some_name" onclick="settings()">
</div>
<div style="border:2px solid blue;float:left;padding:5px">
<div id="flash">
<h1>Alternative content</h1>
<p><a href="http://www.adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" /></a></p>
</div>
</div>
<br style="clear:both">
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
swfobject.embedSWF("freeswitch-h264.swf", "flash", "360", "296", "11.2.202.229", "expressInstall.swf", flashvars, params, []);
flash = $('#flash')[0];
$('#user').val("1000@" + window.location.hostname);
});
</script>
</body>
</html>
include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_rtmp
mod_LTLIBRARIES = mod_rtmp.la
mod_rtmp_la_SOURCES = mod_rtmp.c rtmp_sig.c rtmp.c rtmp_tcp.c
mod_rtmp_la_SOURCES = mod_rtmp.c rtmp_sig.c rtmp.c rtmp_tcp.c rtmp_video.c
mod_rtmp_la_SOURCES += libamf/src/amf0.c libamf/src/hash.c libamf/src/io.c libamf/src/ptrarray.c libamf/src/types.c
mod_rtmp_la_CFLAGS = $(AM_CFLAGS) -Ilibamf/src -I$(abs_srcdir)/libamf/src
mod_rtmp_la_LIBADD = $(switch_builddir)/libfreeswitch.la
......
差异被折叠。
......@@ -23,6 +23,7 @@
* Contributor(s):
*
* Mathieu Rene <mrene@avgs.ca>
* Seven Du <dujinfang@gmail.com>
*
* mod_rtmp.h -- RTMP Endpoint Module
*
......@@ -45,8 +46,8 @@
#define RTMP_USER_VARIABLE_PREFIX "rtmp_u_"
#define RTMP_DEFAULT_PORT 1935
#define RTMP_TCP_READ_BUF 2048
#define AMF_MAX_SIZE 2048
#define RTMP_TCP_READ_BUF 2048 * 16
#define AMF_MAX_SIZE 2048 * 16
#define SUPPORT_SND_NONE 0x0000
#define SUPPORT_SND_ADPCM 0x0002
......@@ -77,7 +78,7 @@
#define kAMF0 0
#define kAMF3 3
#define RTMP_DEFAULT_ACK_WINDOW 0x20000
#define RTMP_DEFAULT_ACK_WINDOW 0x200000
#define RTMP_TYPE_CHUNKSIZE 0x01
#define RTMP_TYPE_ABORT 0x2
......@@ -130,6 +131,12 @@
#define INT32_MAX 0x7fffffffL
#endif
/* Media debug flags */
#define RTMP_MD_AUDIO_READ (1 << 0)
#define RTMP_MD_AUDIO_WRITE (1 << 1)
#define RTMP_MD_VIDEO_READ (1 << 2)
#define RTMP_MD_VIDEO_WRITE (1 << 3)
typedef enum {
RTMP_AUDIO_PCM = 0,
RTMP_AUDIO_ADPCM = 1,
......@@ -412,6 +419,29 @@ struct rtmp_account {
rtmp_account_t *next;
};
typedef struct rtmp2rtp_helper_s
{
amf0_data *sps;
amf0_data *pps;
amf0_data *nal_list;
uint32_t lenSize;
} rtmp2rtp_helper_t;
typedef struct rtp2rtmp_helper_s
{
amf0_data *sps;
amf0_data *pps;
amf0_data *avc_conf;
switch_bool_t send;
switch_bool_t send_avc;
switch_buffer_t *rtmp_buf;
switch_buffer_t *fua_buf; //fu_a buf
uint32_t last_recv_ts;
uint8_t last_mark;
uint16_t last_seq;
switch_bool_t sps_changed;
} rtp2rtmp_helper_t;
struct rtmp_session {
switch_memory_pool_t *pool;
rtmp_profile_t *profile;
......@@ -484,6 +514,9 @@ struct rtmp_session {
uint32_t media_streamid; /* < The stream id that was used for the last "play" command,
where we should send media */
switch_size_t dropped_video_frame;
uint8_t media_debug;
};
struct rtmp_private {
......@@ -509,6 +542,7 @@ struct rtmp_private {
uint8_t video_codec;
switch_time_t stream_start_ts;
switch_time_t stream_last_ts;
switch_timer_t timer;
switch_buffer_t *readbuf;
switch_mutex_t *readbuf_mutex;
......@@ -522,6 +556,24 @@ struct rtmp_private {
uint16_t maxlen;
int over_size;
//video
int has_video;
switch_codec_t video_read_codec;
switch_codec_t video_write_codec;
rtp2rtmp_helper_t video_write_helper;
rtmp2rtp_helper_t video_read_helper;
switch_frame_t video_read_frame;
uint32_t video_read_ts;
uint16_t seq;
unsigned char video_databuf[SWITCH_RTP_MAX_BUF_LEN]; /* < Buffer for read_frame */
switch_buffer_t *video_readbuf;
switch_mutex_t *video_readbuf_mutex;
uint16_t video_maxlen;
int video_over_size;
switch_core_media_params_t mparams;
switch_media_handle_t *media_handle;
};
struct rtmp_reg;
......@@ -540,7 +592,6 @@ typedef enum {
MSG_FULLHEADER = 1
} rtmp_message_send_flag_t;
/* Invokable functions from flash */
RTMP_INVOKE_FUNCTION(rtmp_i_connect);
RTMP_INVOKE_FUNCTION(rtmp_i_createStream);
......@@ -548,6 +599,7 @@ RTMP_INVOKE_FUNCTION(rtmp_i_noop);
RTMP_INVOKE_FUNCTION(rtmp_i_play);
RTMP_INVOKE_FUNCTION(rtmp_i_publish);
RTMP_INVOKE_FUNCTION(rtmp_i_makeCall);
RTMP_INVOKE_FUNCTION(rtmp_i_fcSubscribe);
RTMP_INVOKE_FUNCTION(rtmp_i_sendDTMF);
RTMP_INVOKE_FUNCTION(rtmp_i_login);
RTMP_INVOKE_FUNCTION(rtmp_i_logout);
......
......@@ -23,6 +23,7 @@
* Contributor(s):
*
* Mathieu Rene <mrene@avgs.ca>
* Seven Du <dujinfang@gmail.com>
*
* rtmp.c -- RTMP Signalling functions
*
......@@ -284,8 +285,7 @@ RTMP_INVOKE_FUNCTION(rtmp_i_makeCall)
if ((number = amf0_get_string(argv[1]))) {
switch_event_t *event = NULL;
char *auth, *user = NULL, *domain = NULL;
if ((auth = amf0_get_string(argv[2])) && !zstr(auth)) {
if (argc >= 3 && (auth = amf0_get_string(argv[2])) && !zstr(auth)) {
switch_split_user_domain(auth, &user, &domain);
if (rtmp_session_check_user(rsession, user, domain) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_WARNING, "Unauthorized call to %s, client is not logged in account [%s@%s]\n",
......@@ -327,6 +327,33 @@ RTMP_INVOKE_FUNCTION(rtmp_i_makeCall)
return SWITCH_STATUS_SUCCESS;
}
RTMP_INVOKE_FUNCTION(rtmp_i_fcSubscribe)
{
switch_status_t status;
int ac;
amf0_data *av[3] = { 0 };
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_DEBUG, "Got FCSubscribe for %s on stream %d\n", switch_str_nil(amf0_get_string(argv[1])), state->stream_id);
ac = 3;
av[0] = argv[0];
av[1] = argv[1];
av[2] = amf0_boolean_new(1);
switch_assert(av[2]);
status = rtmp_i_receiveaudio(rsession, state, amfnumber, transaction_id, ac, av);
if (status != SWITCH_STATUS_SUCCESS) return status;
rtmp_i_receivevideo(rsession, state, amfnumber, transaction_id, ac, av);
if (status != SWITCH_STATUS_SUCCESS) return status;
amf0_data_free(av[2]);
rtmp_i_makeCall(rsession, state, amfnumber, transaction_id, argc, argv);
return status;
}
RTMP_INVOKE_FUNCTION(rtmp_i_sendDTMF)
{
/* Send DTMFs on the active channel */
......
......@@ -49,34 +49,10 @@ typedef struct rtmp_io_tcp rtmp_io_tcp_t;
struct rtmp_tcp_io_private {
switch_pollfd_t *pollfd;
switch_socket_t *socket;
switch_buffer_t *sendq;
switch_bool_t poll_send;
};
typedef struct rtmp_tcp_io_private rtmp_tcp_io_private_t;
static void rtmp_tcp_alter_pollfd(rtmp_session_t *rsession, switch_bool_t pollout)
{
rtmp_tcp_io_private_t *io_pvt = rsession->io_private;
rtmp_io_tcp_t *io = (rtmp_io_tcp_t*)rsession->profile->io;
if (pollout && (io_pvt->pollfd->reqevents & SWITCH_POLLOUT)) {
return;
} else if (!pollout && !(io_pvt->pollfd->reqevents & SWITCH_POLLOUT)) {
return;
}
switch_pollset_remove(io->pollset, io_pvt->pollfd);
io_pvt->pollfd->reqevents = SWITCH_POLLIN | SWITCH_POLLERR;
if (pollout) {
io_pvt->pollfd->reqevents |= SWITCH_POLLOUT;
}
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_NOTICE, "Pollout: %s\n",
pollout ? "true" : "false");
switch_pollset_add(io->pollset, io_pvt->pollfd);
}
static switch_status_t rtmp_tcp_read(rtmp_session_t *rsession, unsigned char *buf, switch_size_t *len)
{
//rtmp_io_tcp_t *io = (rtmp_io_tcp_t*)rsession->profile->io;
......@@ -117,8 +93,10 @@ static switch_status_t rtmp_tcp_write(rtmp_session_t *rsession, const unsigned c
{
//rtmp_io_tcp_t *io = (rtmp_io_tcp_t*)rsession->profile->io;
rtmp_tcp_io_private_t *io_pvt = rsession->io_private;
switch_status_t status;
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_size_t orig_len = *len;
switch_size_t remaining = *len;
int sanity = 100;
#ifdef RTMP_DEBUG_IO
{
......@@ -139,29 +117,32 @@ static switch_status_t rtmp_tcp_write(rtmp_session_t *rsession, const unsigned c
}
#endif
if (io_pvt->sendq && switch_buffer_inuse(io_pvt->sendq) > 0) {
/* We already have queued data, append it to the sendq */
switch_buffer_write(io_pvt->sendq, buf, *len);
return SWITCH_STATUS_SUCCESS;
}
status = switch_socket_send_nonblock(io_pvt->socket, (char*)buf, len);
if (*len > 0 && *len < orig_len) {
while (remaining > 0) {
if (rsession->state >= RS_DESTROY) {
return SWITCH_STATUS_FALSE;
}
/* We didnt send it all... add it to the sendq*/
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_DEBUG, "%"SWITCH_SIZE_T_FMT" bytes added to sendq.\n", (orig_len - *len));
again:
status = switch_socket_send_nonblock(io_pvt->socket, (char*)buf, len);
switch_buffer_write(io_pvt->sendq, (buf + *len), orig_len - *len);
if ((status == 32 || SWITCH_STATUS_IS_BREAK(status)) && sanity-- > 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "sending too fast, retrying %d\n", sanity);
goto again;
}
if (status != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "send error %d\n", status);
break;
}
/* Make sure we poll-write */
rtmp_tcp_alter_pollfd(rsession, SWITCH_TRUE);
if (*len != orig_len) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "sent %ld of %ld\n", *len, orig_len);
buf += *len;
remaining -= *len;
*len = remaining;
}
*len = orig_len;
return status;
}
......@@ -178,11 +159,6 @@ static switch_status_t rtmp_tcp_close(rtmp_session_t *rsession)
switch_socket_close(io_pvt->socket);
io_pvt->socket = NULL;
}
if ( io_pvt->sendq ) {
switch_buffer_destroy(&(io_pvt->sendq));
}
return SWITCH_STATUS_SUCCESS;
}
......@@ -246,7 +222,6 @@ void *SWITCH_THREAD_FUNC rtmp_io_tcp_thread(switch_thread_t *thread, void *obj)
pvt->socket = newsocket;
switch_socket_create_pollfd(&pvt->pollfd, newsocket, SWITCH_POLLIN | SWITCH_POLLERR, rsession, rsession->pool);
switch_pollset_add(io->pollset, pvt->pollfd);
switch_buffer_create_dynamic(&pvt->sendq, 512, 1024, 0);
/* Get the remote address/port info */
switch_socket_addr_get(&addr, SWITCH_TRUE, newsocket);
......@@ -261,18 +236,7 @@ void *SWITCH_THREAD_FUNC rtmp_io_tcp_thread(switch_thread_t *thread, void *obj)
rtmp_session_t *rsession = (rtmp_session_t*)fds[i].client_data;
rtmp_tcp_io_private_t *io_pvt = (rtmp_tcp_io_private_t*)rsession->io_private;
if (fds[i].rtnevents & SWITCH_POLLOUT && switch_buffer_inuse(io_pvt->sendq) > 0) {
/* Send as much remaining data as possible */
switch_size_t sendlen;
const void *ptr;
sendlen = switch_buffer_peek_zerocopy(io_pvt->sendq, &ptr);
switch_socket_send_nonblock(io_pvt->socket, ptr, &sendlen);
switch_buffer_toss(io_pvt->sendq, sendlen);
if (switch_buffer_inuse(io_pvt->sendq) == 0) {
/* Remove our fd from OUT polling */
rtmp_tcp_alter_pollfd(rsession, SWITCH_FALSE);
}
} else if (fds[i].rtnevents & SWITCH_POLLIN && rtmp_handle_data(rsession) != SWITCH_STATUS_SUCCESS) {
if (fds[i].rtnevents & SWITCH_POLLIN && rtmp_handle_data(rsession) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_DEBUG, "Closing socket\n");
switch_mutex_lock(io->mutex);
......
差异被折叠。
/*
* mod_rtmp for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2015, Seven Du.
*
* 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 rtmp_video for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is Seven Du
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Seven Du <dujinfang@gmail.com>
* Da Xiong <wavecb@gmail.com>
*
* rtmp_video.h -- RTMP video
*
*/
#include "amf0.h"
#include "mod_rtmp.h"
#define MAX_RTP_PAYLOAD_SIZE 1400
void rtmp2rtp_helper_init(rtmp2rtp_helper_t *helper);
void rtp2rtmp_helper_init(rtp2rtmp_helper_t *helper);
void rtmp2rtp_helper_destroy(rtmp2rtp_helper_t *helper);
void rtp2rtmp_helper_destroy(rtp2rtmp_helper_t *helper);
switch_status_t on_rtmp_tech_init(switch_core_session_t *session, rtmp_private_t *tech_pvt);
switch_status_t on_rtmp_destroy(rtmp_private_t *tech_pvt);
/*Rtmp packet to rtp frame*/
switch_status_t rtmp_rtmp2rtpH264(rtmp2rtp_helper_t *read_helper, uint8_t* data, uint32_t len);
switch_status_t rtmp_rtp2rtmpH264(rtp2rtmp_helper_t *helper, switch_frame_t *frame);
switch_status_t rtmp_write_video_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id);
switch_status_t rtmp_read_video_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id);
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论