1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-02-14 20:31:56 +00:00

merge from wenjie, the bandwidth test feature.

This commit is contained in:
winlin 2013-12-27 17:03:12 +08:00
parent 6bba081485
commit 20d1732ced
16 changed files with 1577 additions and 246 deletions

View file

@ -278,6 +278,7 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw
* nginx v1.5.0: 139524 lines <br/>
### History
* v1.0, 2013-12-27, merge from wenjie, the bandwidth test feature.
* v0.9, 2013-12-25, [v0.9](https://github.com/winlinvip/simple-rtmp-server/releases/tag/0.9) released. 20926 lines.
* v0.9, 2013-12-25, fix the bitrate bug(in Bps), use enhanced microphone.
* v0.9, 2013-12-22, demo video meeting or chat(srs+cherrypy+jquery+bootstrap).

View file

@ -223,7 +223,7 @@ vhost bandcheck.srs.com {
enabled on;
# the key for server to valid,
# if invalid key, server disconnect and abort the bandwidth check.
key 35c9b402c12a7246868752e2878f7e0e;
key "35c9b402c12a7246868752e2878f7e0e";
# the interval in seconds for bandwidth check,
# server donot allow new test request.
# default: 30

20
trunk/configure vendored
View file

@ -47,22 +47,27 @@ echo "" >> $SRS_AUTO_HEADERS_H
echo "generate Makefile"
cat << END > ${SRS_MAKEFILE}
.PHONY: default help clean server _prepare_dir
default: server
.PHONY: default help clean server bandwidth _prepare_dir
default: server bandwidth
help:
@echo "Usage: make <help>|<clean>|<server>"
@echo "Usage: make <help>|<clean>|<server>|<bandwidth>"
@echo " help display this help menu"
@echo " clean cleanup project"
@echo " server build the srs(simple rtmp server) over st(state-threads)"
@echo " bandwidth build the bandwidth test client tool."
clean:
(rm -f Makefile; cd ${SRS_OBJS}; rm -rf srs Makefile *.hpp src st_*_load)
(rm -f Makefile; cd ${SRS_OBJS}; rm -rf srs bandwidth Makefile *.hpp src st_*_load)
server: _prepare_dir
@echo "build the srs(simple rtmp server) over st(state-threads)"
\$(MAKE) -f ${SRS_OBJS}/${SRS_MAKEFILE} srs
bandwidth: _prepare_dir
@echo "build the bandwidth test client tool"
\$(MAKE) -f ${SRS_OBJS}/${SRS_MAKEFILE} bandwidth
# the ./configure will generate it.
_prepare_dir:
@mkdir -p ${SRS_OBJS}
@ -87,7 +92,7 @@ GCC = g++
LINK = \$(GCC)
AR = ar
.PHONY: default srs
.PHONY: default srs bandwidth
default:
@ -124,12 +129,12 @@ CORE_OBJS="${MODULE_OBJS[@]}"
MODULE_ID="MAIN"
MODULE_DEPENDS=("CORE")
ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS})
MODULE_FILES=("srs_main_server")
MODULE_FILES=("srs_main_server" "srs_main_bandcheck")
MODULE_DIR="src/main" . auto/modules.sh
MAIN_OBJS="${MODULE_OBJS[@].o}"
# all main entrances
MAIN_ENTRANCES=("srs_main_server")
MAIN_ENTRANCES=("srs_main_server" "srs_main_bandcheck")
# srs(simple rtmp server) over st(state-threads)
ModuleLibFiles=(${LibSTfile})
@ -143,6 +148,7 @@ else
LINK_OPTIONS="-ldl"
fi
BUILD_KEY="srs" APP_MAIN="srs_main_server" APP_NAME="srs" SO_PATH="" . auto/apps.sh
BUILD_KEY="bandwidth" APP_MAIN="srs_main_bandcheck" APP_NAME="bandwidth" SO_PATH="" . auto/apps.sh
echo 'configure ok! '

View file

@ -0,0 +1,27 @@
// for bw to init url
// url: scheme://host:port/path?query#fragment
function srs_init_bwt(rtmp_url, hls_url) {
update_nav();
if (rtmp_url) {
//var query = parse_query_string();
var search_filed = String(window.location.search).replace(" ", "").split("?")[1];
$(rtmp_url).val("rtmp://" + window.location.host + ":" + 1935 + "/app?" + search_filed);
}
if (hls_url) {
$(hls_url).val(build_default_hls_url());
}
}
function srs_bwt_check_url(url) {
if (url.indexOf("key") != -1 && url.indexOf("vhost") != -1) {
return true;
}
return false;
}
function srs_bwt_build_default_url() {
var url_default = "rtmp://" + window.location.host + ":" + 1935 + "/app?key=35c9b402c12a7246868752e2878f7e0e&vhost=bandcheck.srs.com";
return url_default;
}

View file

@ -13,14 +13,111 @@
<script type="text/javascript" src="js/srs.publisher.js"></script>
<script type="text/javascript" src="js/srs.utility.js"></script>
<script type="text/javascript" src="js/srs.utility.js"></script>
<script type="text/javascript" src="js/srs.bandwidth.js"></script>
<style>
body{
padding-top: 55px;
}
#main_modal {
width: 600px;
margin-left: -300px;
}
#check_status {
margin-left: 20px;
margin-top: -55px;
}
#pb_buffer_bg {
margin-top: 40px;
margin-bottom: 10px;
}
</style>
<script type="text/javascript">
function update_progress(percent) {
$("#progress_bar").width(percent);
}
function progress_reset() {
$("#progress_bar").width("0%");
}
function update_status(text) {
$("#check_status").text(text);
}
function get_swf_width() {
return 1;
}
function get_swf_height() {
return 1;
}
function show_modal() {
$("#main_modal").modal({show:true, keyboard:false});
}
function band_check(url) {
// remove flash contet
var bw_div = $("<div/>");
$(bw_div).attr("id", "bw_div");
$("#bw_center").append(bw_div);
var flashvars = {};
flashvars.url = url;
flashvars.update_progress = "update_progress";
flashvars.progress_reset = "progress_reset";
flashvars.update_status = "update_status";
var params = {};
params.allowFullScreen = true;
var attributes = {};
swfobject.embedSWF(
"srs_bwt/release/srs_bwt.swf", "bw_div",
get_swf_width(), get_swf_height(),
"11.1.0", "js/AdobeFlashPlayerInstall.swf",
flashvars, params, attributes
);
}
$(function(){
update_nav();
srs_init_bwt("#txt_url");
var txt_input = $("#txt_url").val();
// if valid ?
if (!srs_bwt_check_url(txt_input)) {
$("#txt_url").val(srs_bwt_build_default_url());
}
$("#main_modal").on(
"show",
function()
{
progress_reset();
update_status("");
var url = $("#txt_url").val();
/*!
url encode
*/
url = escape(url);
band_check(url);
}
);
$("#main_modal").on("hide", function(){
$("#bw_div").remove();
});
$("#btn_play").click(
function()
{
$("#main_modal").modal({show:true, keyboard:false});
}
);
});
</script>
</head>
@ -44,6 +141,34 @@
</div>
</div>
<div class="container">
<div class="form-inline">
URL:
<input type="text" id="txt_url" class="input-xxlarge" value="" placeholder="例如rtmp://host:port/app?key=xx&vhost=yy"></input>
<button class="btn btn-primary" id="btn_play">开始测速</button>
</div>
<div id="main_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>SRS Band Check</h3>
</div>
<div class="modal-body">
<div id="player"></div>
<div class="progress progress-striped active" id="pb_buffer_bg">
<div class="bar" style="width: 50%;" id="progress_bar"></div>
</div>
</div>
<div class="modal-body" id="bw_center">
</div>
<span id="check_status1"><font ><strong id="check_status">status</strong></font> </span>
<div class="modal-footer">
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true"> 关闭 </button>
</div>
</div>
<hr>
<footer>
<p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team &copy; 2013</a></p>

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<actionScriptProperties analytics="false" mainApplicationPath="srs_bwt.as" projectUUID="00251213-e6a2-4dd5-a033-125cc78f843c" version="10">
<compiler additionalCompilerArguments="-locale en_US" autoRSLOrdering="true" copyDependentFiles="true" fteInMXComponents="false" generateAccessible="true" htmlExpressInstall="true" htmlGenerate="true" htmlHistoryManagement="true" htmlPlayerVersionCheck="true" includeNetmonSwc="false" outputFolderPath="bin-debug" removeUnusedRSL="true" sourceFolderPath="src" strict="true" targetPlayerVersion="0.0.0" useApolloConfig="false" useDebugRSLSwfs="true" verifyDigests="true" warn="true">
<compilerSourcePath/>
<libraryPath defaultLinkType="0">
<libraryPathEntry kind="4" path="">
<excludedEntries>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_charts.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="1" linkType="1" path="${PROJECT_FRAMEWORKS}/locale/{locale}"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/advancedgrids.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/qtp.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_air.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/charts.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/framework.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/mx/mx.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/netmon.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/spark.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/sparkskins.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/rpc.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/videoPlayer.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/qtp_air.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/datavisualization.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/spark_dmv.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/flash-integration.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_dmv.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_flashflexkit.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_agent.swc" useDefaultLinkType="false"/>
</excludedEntries>
</libraryPathEntry>
</libraryPath>
<sourceAttachmentPath/>
</compiler>
<applications>
<application path="srs_bwt.as"/>
</applications>
<modules/>
<buildCSSFiles/>
<flashCatalyst validateFlashCatalystCompatibility="false"/>
</actionScriptProperties>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>srs_bwt</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.adobe.flexbuilder.project.flexbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
</natures>
</projectDescription>

Binary file not shown.

View file

@ -0,0 +1,24 @@
package SrsClass
{
import flash.system.System;
public class SrsElapsedTimer
{
private var beginDate:Date;
public function SrsElapsedTimer()
{
beginDate = new Date;
}
public function elapsed():Number{
var endDate:Date = new Date;
// get deiff by ms
return (endDate.time - beginDate.time);
}
public function restart():void{
beginDate = new Date;
}
}
}

View file

@ -0,0 +1,27 @@
package SrsClass
{
import flash.net.SharedObject;
public class SrsSettings
{
private var settings:SharedObject;
private var key:String = "SrsBandCheck";
public function SrsSettings()
{
settings = SharedObject.getLocal(key);
}
public function addAddressText(val:String):void{
settings.data.address_text = val;
}
public function addressText():String{
return settings.data.address_text;
}
static public function instance():SrsSettings{
return new SrsSettings;
}
}
}

View file

@ -0,0 +1,227 @@
package
{
import SrsClass.SrsElapsedTimer;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.NetStatusEvent;
import flash.events.TimerEvent;
import flash.external.ExternalInterface;
import flash.net.NetConnection;
import flash.net.ObjectEncoding;
import flash.system.System;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import flash.utils.Timer;
import flash.utils.setTimeout;
public class srs_bwt extends Sprite
{
private var connection:NetConnection;
private var updatePlayProgressTimer:Timer;
private var elapTimer:SrsElapsedTimer;
// server ip get from server
private var server_ip:String;
// test wheth publish should to stop
private var stop_pub:Boolean = false;
// js interface
private var js_update_progress:String;
private var js_progress_reset:String;
private var js_update_status:String;
private var value_progressbar:Number = 0;
private var max_progressbar:Number = 0;
// set NetConnection ObjectEncoding to AMF0
NetConnection.defaultObjectEncoding = ObjectEncoding.AMF0;
public function srs_bwt()
{
this.stage.scaleMode = StageScaleMode.NO_SCALE;
this.stage.align = StageAlign.TOP_LEFT;
var flashvars:Object = this.root.loaderInfo.parameters;
this.js_update_progress = flashvars.update_progress;
this.js_progress_reset = flashvars.progress_reset;
this.js_update_status = flashvars.update_status;
// init context menu, add action "Srs 带宽测试工具 0.1"
var myMenu:ContextMenu = new ContextMenu();
myMenu.hideBuiltInItems();
myMenu.customItems.push(new ContextMenuItem("Srs 带宽测试工具 0.1", true));
this.contextMenu = myMenu;
// init connection
connection = new NetConnection;
connection.client = this;
connection.addEventListener(NetStatusEvent.NET_STATUS, onStatus);
connection.connect(flashvars.url);
//connection.connect("rtmp://192.168.8.234:1935/app?key=35c9b402c12a7246868752e2878f7e0e&vhost=bandcheck.srs.com");
// for play to update progress bar
elapTimer = new SrsElapsedTimer;
// we suppose the check time = 7 S
updatePlayProgressTimer = new Timer(100);
updatePlayProgressTimer.addEventListener(TimerEvent.TIMER, onTimerTimeout);
updatePlayProgressTimer.start();
}
// get NetConnection NetStatusEvent
public function onStatus(evt:NetStatusEvent) : void{
trace(evt.info.code);
switch(evt.info.code){
case "NetConnection.Connect.Failed":
updateState("连接服务器失败!");
break;
case "NetConnection.Connect.Rejected":
updateState("服务器拒绝连接!");
break;
case "NetConnection.Connect.Success":
server_ip = evt.info.data.srs_server_ip;
updateState("连接服务器成功!");
break;
case "NetConnection.Connect.Closed":
//updateState("连接已断开!");
break;
}
}
/**
* NetConnection callback this function, when recv server call "onSrsBandCheckStartPlayBytes"
* then start @updatePlayProgressTimer for updating the progressbar
* */
public function onSrsBandCheckStartPlayBytes(evt:Object):void{
var duration_ms:Number = evt.duration_ms;
var interval_ms:Number = evt.interval_ms;
connection.call("onSrsBandCheckStartingPlayBytes", null);
updateState("测试下行带宽(" + server_ip + ")");
// we suppose play duration_ms = pub duration_ms
max_progressbar = duration_ms * 2;
}
public function onSrsBandCheckPlaying(evt:Object):void{
}
public function onTimerTimeout(evt:TimerEvent):void
{
value_progressbar = elapTimer.elapsed();
updateProgess(value_progressbar, max_progressbar);
}
public function onSrsBandCheckStopPlayBytes(evt:Object):void{
var duration_ms:Number = evt.duration_ms;
var interval_ms:Number = evt.interval_ms;
var duration_delta:Number = evt.duration_delta;
var bytes_delta:Number = evt.bytes_delta;
var kbps:Number = 0;
if(duration_delta > 0){
kbps = bytes_delta * 8.0 / duration_delta; // b/ms == kbps
}
kbps = (int(kbps * 10))/10.0;
flash.utils.setTimeout(stopPlayTest, 0);
}
private function stopPlayTest():void{
connection.call("onSrsBandCheckStoppedPlayBytes", null);
}
public function onSrsBandCheckStartPublishBytes(evt:Object):void{
var duration_ms:Number = evt.duration_ms;
var interval_ms:Number = evt.interval_ms;
connection.call("onSrsBandCheckStartingPublishBytes", null);
updateState("测试上行带宽(" + server_ip + ")");
flash.utils.setTimeout(publisher, 0);
}
private function publisher():void{
if (stop_pub) {
return;
}
var data:Array = new Array();
var data_size:int = 100;
for(var i:int; i < data_size; i++){
data.push("SrS band check data from client's publishing......");
}
data_size += 100;
connection.call("onSrsBandCheckPublishing", null, data);
flash.utils.setTimeout(publisher, 0);
}
public function onSrsBandCheckStopPublishBytes(evt:Object):void{
var duration_ms:Number = evt.duration_ms;
var interval_ms:Number = evt.interval_ms;
var duration_delta:Number = evt.duration_delta;
var bytes_delta:Number = evt.bytes_delta;
var kbps:Number = 0;
if(duration_delta > 0){
kbps = bytes_delta * 8.0 / duration_delta; // b/ms == kbps
}
kbps = (int(kbps * 10))/10.0;
stopPublishTest();
}
private function stopPublishTest():void{
if(connection.connected){
connection.call("onSrsBandCheckStoppedPublishBytes", null);
}
stop_pub = true;
value_progressbar = max_progressbar;
updateProgess(value_progressbar, max_progressbar);
updatePlayProgressTimer.stop();
}
public function onSrsBandCheckFinished(evt:Object):void{
var code:Number = evt.code;
var start_time:Number = evt.start_time;
var end_time:Number = evt.end_time;
var play_kbps:Number = evt.play_kbps;
var publish_kbps:Number = evt.publish_kbps;
var play_bytes:Number = evt.play_bytes;
var play_time:Number = evt.play_time;
var publish_bytes:Number = evt.publish_bytes;
var publish_time:Number = evt.publish_time;
updateState("检测结束: 服务器: " + server_ip + " 上行: " + publish_kbps + " kbps" + " 下行: " + play_kbps + " kbps"
+ " 测试时间: " + (end_time-start_time)/1000 + " 秒");
connection.call("finalClientPacket", null);
}
public function onBWDone():void{
// do nothing
}
// update progressBar's value
private function updateProgess(value:Number, maxValue:Number):void{
flash.external.ExternalInterface.call(this.js_update_progress, value * 100 / maxValue + "%");
trace(value + "-" + maxValue + "-" + value * 100 / maxValue + "%");
}
// update checking status
private function updateState(text:String):void{
flash.external.ExternalInterface.call(this.js_update_status, text);
trace(text);
}
}
}

16
trunk/src/core/srs_core_bandwidth.cpp Normal file → Executable file
View file

@ -24,6 +24,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_bandwidth.hpp>
#include <arpa/inet.h>
#include <sstream>
using namespace std;
@ -260,21 +261,24 @@ int SrsBandwidth::check_play(
int64_t current_time = srs_get_system_time_ms();
int size = 1024; // TODO: FIXME: magic number
char random_data[size];
memset(random_data, 0x01, size);
memset(random_data, 'A', size);
int interval = 0;
int data_count = 1;
while ( (srs_get_system_time_ms() - current_time) < duration_ms ) {
st_usleep(interval);
// TODO: FIXME: use shared ptr message.
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_playing();
// TODO: FIXME: magic number
for (int i = 0; i < 100; ++i) {
char buf[32]; // TODO: FIXME: magic number
sprintf(buf, "%d", i);
pkt->data->set(buf, new SrsAmf0String(random_data));
// TODO: FIXME: magic number
for (int i = 0; i < data_count; ++i) {
std::stringstream seq;
seq << i;
std::string play_data = "SrS band check data from server's playing......";
pkt->data->set(seq.str(), new SrsAmf0String(play_data.c_str()));
}
data_count += 2;
// TODO: FIXME: get length from the rtmp protocol stack.
play_bytes += pkt->get_payload_length();

2
trunk/src/core/srs_core_protocol.cpp Normal file → Executable file
View file

@ -2789,7 +2789,7 @@ SrsBandwidthPacket* SrsBandwidthPacket::create_start_play()
SrsBandwidthPacket* SrsBandwidthPacket::create_playing()
{
SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
return pkt->set_command(SRS_BW_CHECK_STARTING_PLAY);
return pkt->set_command(SRS_BW_CHECK_PLAYING);
}
SrsBandwidthPacket* SrsBandwidthPacket::create_stop_play()

2
trunk/src/core/srs_core_rtmp.hpp Normal file → Executable file
View file

@ -110,7 +110,7 @@ enum SrsClientType
*/
class SrsRtmpClient
{
private:
protected:
SrsProtocol* protocol;
st_netfd_t stfd;
public:

View file

@ -0,0 +1,832 @@
/*
The MIT License (MIT)
Copyright (c) 2013 wenjiegit
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sstream>
#include <getopt.h>
#include <stdlib.h>
#include <srs_core_rtmp.hpp>
#include <srs_core_protocol.hpp>
#include <srs_core_error.hpp>
#include <srs_core_amf0.hpp>
#include <srs_core_autofree.hpp>
#include <srs_core_stream.hpp>
#include <st.h>
// server play control
#define SRS_BW_CHECK_START_PLAY "onSrsBandCheckStartPlayBytes"
#define SRS_BW_CHECK_STARTING_PLAY "onSrsBandCheckStartingPlayBytes"
#define SRS_BW_CHECK_STOP_PLAY "onSrsBandCheckStopPlayBytes"
#define SRS_BW_CHECK_STOPPED_PLAY "onSrsBandCheckStoppedPlayBytes"
// server publish control
#define SRS_BW_CHECK_START_PUBLISH "onSrsBandCheckStartPublishBytes"
#define SRS_BW_CHECK_STARTING_PUBLISH "onSrsBandCheckStartingPublishBytes"
#define SRS_BW_CHECK_STOP_PUBLISH "onSrsBandCheckStopPublishBytes"
#define SRS_BW_CHECK_STOPPED_PUBLISH "onSrsBandCheckStoppedPublishBytes"
// EOF control.
#define SRS_BW_CHECK_FINISHED "onSrsBandCheckFinished"
#define SRS_BW_CHECK_FLASH_FINAL "finalClientPacket"
// client only
#define SRS_BW_CHECK_PLAYING "onSrsBandCheckPlaying"
#define SRS_BW_CHECK_PUBLISHING "onSrsBandCheckPublishing"
/**
* @brief class of Linux version band check client
* check play and publish speed.
*/
class SrsBandCheckClient : public SrsRtmpClient
{
public:
SrsBandCheckClient(st_netfd_t _stfd);
~SrsBandCheckClient();
public:
/**
* @brief test play
*
*/
int check_play();
/**
* @brief test publish
*
*/
int check_publish();
private:
/**
* @brief just return success.
*/
int create_stream(int& stream_id);
/**
* @brief just return success.
*/
int play(std::string stream, int stream_id);
/**
* @brief just return success.
*/
int publish(std::string stream, int stream_id);
private:
int expect_start_play();
int send_starting_play();
int expect_stop_play();
int send_stopped_play();
int expect_start_pub();
int send_starting_pub();
int send_pub_data();
int expect_stop_pub();
/**
* @brief expect result.
* because the core module has no method to decode this packet
* so we must get the internal data and decode it here.
*/
int expect_finished();
int send_stopped_pub();
/**
* @brief notify server the check procedure is over.
*/
int send_final();
};
/**
* @brief class of band check
* used to check band width with a client @param bandCheck_Client
*/
class SrsBandCheck
{
public:
SrsBandCheck();
~SrsBandCheck();
public:
/**
* @brief band check method
*
* connect to server------>rtmp handshake------>rtmp connect------>play------>publish
* @retval ERROR_SUCCESS when success.
*/
int check(const std::string& app, const std::string& tcUrl);
/**
* @brief set the address and port of test server
*
* @param server server address, domain or ip
* @param server listened port ,default is 1935
*/
void set_server(const std::string& server, int port = 1935);
private:
int connect_server();
private:
SrsBandCheckClient* bandCheck_Client;
std::string server_address;
int server_port;
};
/**
* @brief init st lib
*/
static int init_st();
static void print_help(char** argv);
static void print_version();
/**
* @brief get user option
* @internal ip Mandatory arguments
* @internal key Mandatory arguments
* @internal port default 1935
* @internal vhost default bandcheck.srs.com
*/
static int get_opt(int argc ,char* argv[]);
/**
* global var.
*/
static struct option long_options[] =
{
{"ip", required_argument, 0, 'i'},
{"port", optional_argument, 0, 'p'},
{"key", required_argument, 0, 'k'},
{"vhost", optional_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
};
static const char* short_options = "i:p::k:v::hV";
static std::string g_ip;
static int g_port = 1935;
static std::string g_key;
static std::string g_vhost = "bandcheck.srs.com";
#define BUILD_VERSION "srs band check 0.1"
int main(int argc ,char* argv[])
{
int ret = ERROR_SUCCESS;
if (argc <= 1) {
print_help(argv);
exit(1);
}
if ((ret = get_opt(argc, argv)) != ERROR_SUCCESS) {
return -1;
}
// check param
if (g_ip.empty()) {
printf("ip address should not be empty.\n");
return -1;
}
if (g_key.empty()) {
printf("test key should not be empty.\n");
return -1;
}
if ((ret = init_st()) != ERROR_SUCCESS) {
srs_error("band check init failed. ret=%d", ret);
return ret;
}
std::string app = "app?key=" + g_key + "&vhost=" + g_vhost;
char tcUrl_buffer[1024] = {0};
sprintf(tcUrl_buffer, "rtmp://%s:%d/%s", g_ip.c_str(), g_port, app.c_str());
std::string tcUrl = tcUrl_buffer;
SrsBandCheck band_check;
band_check.set_server(g_ip, g_port);
if ((ret = band_check.check(app, tcUrl)) != ERROR_SUCCESS) {
srs_error("band check failed. address=%s ret=%d", "xx.com", ret);
return -1;
}
return 0;
}
SrsBandCheckClient::SrsBandCheckClient(st_netfd_t _stfd)
: SrsRtmpClient(_stfd)
{
}
SrsBandCheckClient::~SrsBandCheckClient()
{
}
int SrsBandCheckClient::check_play()
{
int ret = ERROR_SUCCESS;
if ((ret = expect_start_play()) != ERROR_SUCCESS) {
srs_error("expect_start_play failed. ret=%d", ret);
return ret;
}
if ((ret = send_starting_play()) != ERROR_SUCCESS) {
srs_error("send starting play failed. ret=%d", ret);
return ret;
}
if ((ret = expect_stop_play()) != ERROR_SUCCESS) {
srs_error("expect stop play failed. ret=%d", ret);
return ret;
}
if ((ret = send_stopped_play()) != ERROR_SUCCESS) {
srs_error("send stopped play failed. ret=%d", ret);
return ret;
}
return ret;
}
int SrsBandCheckClient::check_publish()
{
int ret = ERROR_SUCCESS;
if ((ret = expect_start_pub()) != ERROR_SUCCESS) {
srs_error("expect start pub failed. ret=%d", ret);
return ret;
}
if ((ret = send_starting_pub())!= ERROR_SUCCESS) {
srs_error("send starting pub failed. ret=%d", ret);
return ret;
}
if ((ret = send_pub_data()) != ERROR_SUCCESS) {
srs_error("publish data failed. ret=%d", ret);
return ret;
}
if ((ret = send_stopped_pub()) != ERROR_SUCCESS) {
srs_error("send stopped pub failed. ret=%d", ret);
return ret;
}
if ((ret = expect_finished()) != ERROR_SUCCESS) {
srs_error("expect finished msg failed. ret=%d", ret);
return ret;
}
if ((ret = send_final()) != ERROR_SUCCESS) {
srs_error("send final msg failed. ret=%d", ret);
return ret;
}
return ret;
}
int SrsBandCheckClient::create_stream(int &stream_id)
{
return ERROR_SUCCESS;
}
int SrsBandCheckClient::play(std::string stream, int stream_id)
{
return ERROR_SUCCESS;
}
int SrsBandCheckClient::publish(std::string stream, int stream_id)
{
return ERROR_SUCCESS;
}
int SrsBandCheckClient::expect_start_play()
{
int ret = ERROR_SUCCESS;
// expect connect _result
SrsCommonMessage* msg = NULL;
SrsBandwidthPacket* pkt = NULL;
if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
srs_error("expect bandcheck start play message failed. ret=%d", ret);
return ret;
}
SrsAutoFree(SrsCommonMessage, msg, false);
srs_info("get bandcheck start play message");
if (pkt->command_name != SRS_BW_CHECK_START_PLAY) {
srs_error("pkt error. expect=%s, actual=%s", SRS_BW_CHECK_START_PLAY, pkt->command_name.c_str());
return -1;
}
return ret;
}
int SrsBandCheckClient::send_starting_play()
{
int ret = ERROR_SUCCESS;
SrsCommonMessage* msg = new SrsCommonMessage;
SrsBandwidthPacket* pkt = new SrsBandwidthPacket;
pkt->command_name = SRS_BW_CHECK_STARTING_PLAY;
msg->set_packet(pkt, 0);
if ((ret = send_message(msg)) != ERROR_SUCCESS) {
srs_error("send starting play msg failed. ret=%d", ret);
return ret;
}
return ret;
}
int SrsBandCheckClient::expect_stop_play()
{
int ret = ERROR_SUCCESS;
while (true) {
SrsCommonMessage* msg = NULL;
SrsBandwidthPacket* pkt = NULL;
if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
srs_error("expect stop play message failed. ret=%d", ret);
return ret;
}
SrsAutoFree(SrsCommonMessage, msg, false);
srs_info("get bandcheck stop play message");
if (pkt->command_name == SRS_BW_CHECK_STOP_PLAY) {
break;
}
}
return ret;
}
int SrsBandCheckClient::send_stopped_play()
{
int ret = ERROR_SUCCESS;
SrsCommonMessage* msg = new SrsCommonMessage;
SrsBandwidthPacket* pkt = new SrsBandwidthPacket;
pkt->command_name = SRS_BW_CHECK_STOPPED_PLAY;
msg->set_packet(pkt, 0);
if ((ret = send_message(msg)) != ERROR_SUCCESS) {
srs_error("send stopped play msg failed. ret=%d", ret);
return ret;
}
return ret;
}
int SrsBandCheckClient::expect_start_pub()
{
int ret = ERROR_SUCCESS;
while (true) {
SrsCommonMessage* msg = NULL;
SrsBandwidthPacket* pkt = NULL;
if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
srs_error("expect start pub message failed. ret=%d", ret);
return ret;
}
SrsAutoFree(SrsCommonMessage, msg, false);
srs_info("get bandcheck start pub message");
if (pkt->command_name == SRS_BW_CHECK_START_PUBLISH) {
break;
}
}
return ret;
}
int SrsBandCheckClient::send_starting_pub()
{
int ret = ERROR_SUCCESS;
SrsCommonMessage* msg = new SrsCommonMessage;
SrsBandwidthPacket* pkt = new SrsBandwidthPacket;
pkt->command_name = SRS_BW_CHECK_STARTING_PUBLISH;
msg->set_packet(pkt, 0);
if ((ret = send_message(msg)) != ERROR_SUCCESS) {
srs_error("send starting play msg failed. ret=%d", ret);
return ret;
}
srs_info("send starting play msg success.");
return ret;
}
int SrsBandCheckClient::send_pub_data()
{
int ret = ERROR_SUCCESS;
int data_count = 100;
while (true) {
SrsCommonMessage* msg = new SrsCommonMessage;
SrsBandwidthPacket* pkt = new SrsBandwidthPacket;
pkt->command_name = SRS_BW_CHECK_PUBLISHING;
msg->set_packet(pkt, 0);
for (int i = 0; i < data_count; ++i) {
std::stringstream seq;
seq << i;
std::string play_data = "SrS band check data from client's publishing......";
pkt->data->set(seq.str(), new SrsAmf0String(play_data.c_str()));
}
data_count += 100;
if ((ret = send_message(msg)) != ERROR_SUCCESS) {
srs_error("send publish message failed.ret=%d", ret);
return ret;
}
if ((ret = expect_stop_pub()) == ERROR_SUCCESS) {
break;
}
}
return ret;
}
int SrsBandCheckClient::expect_stop_pub()
{
int ret = ERROR_SUCCESS;
while (true) {
if ((ret = st_netfd_poll(stfd, POLLIN, 1000)) == ERROR_SUCCESS) {
SrsCommonMessage* msg = 0;
if ((ret = recv_message(&msg)) != ERROR_SUCCESS)
{
srs_error("recv message failed while expect stop pub. ret=%d", ret);
return ret;
}
if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) {
srs_error("decode packet error while expect stop pub. ret=%d", ret);
return ret;
}
SrsBandwidthPacket* pkt = dynamic_cast<SrsBandwidthPacket*>(msg->get_packet());
if (pkt && pkt->command_name == SRS_BW_CHECK_STOP_PUBLISH) {
return ret;
}
} else {
break;
}
}
return ret;
}
int SrsBandCheckClient::expect_finished()
{
int ret = ERROR_SUCCESS;
while (true) {
SrsCommonMessage* msg = NULL;
SrsBandwidthPacket* pkt = NULL;
if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
srs_error("expect finished message failed. ret=%d", ret);
return ret;
}
SrsAutoFree(SrsCommonMessage, msg, false);
srs_info("get bandcheck finished message");
if (pkt->command_name == SRS_BW_CHECK_FINISHED) {
SrsStream *stream = new SrsStream;
SrsAutoFree(SrsStream, stream, false);
if ((ret = stream->initialize((char*)msg->payload, msg->size)) != ERROR_SUCCESS) {
srs_error("initialize stream error. ret=%d", ret);
return ret;
}
std::string command_name;
if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
srs_error("amfo read string error. ret=%d", ret);
return ret;
}
double action_id;
if ((ret = srs_amf0_read_number(stream, action_id)) != ERROR_SUCCESS) {
srs_error("amfo read number error. ret=%d", ret);
return ret;
}
if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
srs_error("amfo read number error. ret=%d", ret);
return ret;
}
SrsAmf0Object* object;
if ((ret = srs_amf0_read_object(stream, object)) != ERROR_SUCCESS) {
srs_error("amfo read object error. ret=%d", ret);
return ret;
}
int64_t start_time = 0;
int64_t end_time = 0;
SrsAmf0Any* start_time_any = object->get_property("start_time");
if (start_time_any && start_time_any->is_number()) {
SrsAmf0Number* start_time_number = dynamic_cast<SrsAmf0Number*> (start_time_any);
if (start_time_number) {
start_time = start_time_number->value;
}
}
SrsAmf0Any* end_time_any = object->get_property("end_time");
if (end_time_any && end_time_any->is_number()) {
SrsAmf0Number* end_time_number = dynamic_cast<SrsAmf0Number*> (end_time_any);
if (end_time_number) {
end_time = end_time_number->value;
}
}
int play_kbps = 0;
int pub_kbps = 0;
SrsAmf0Any* play_kbp_any = object->get_property("play_kbps");
if (play_kbp_any && play_kbp_any->is_number()) {
SrsAmf0Number* play_kbps_number = dynamic_cast<SrsAmf0Number*> (play_kbp_any);
if (play_kbps_number) {
play_kbps = play_kbps_number->value;
}
}
SrsAmf0Any* pub_kbp_any = object->get_property("publish_kbps");
if (pub_kbp_any && pub_kbp_any->is_number()) {
SrsAmf0Number* pub_kbps_number = dynamic_cast<SrsAmf0Number*> (pub_kbp_any);
if (pub_kbps_number) {
pub_kbps = pub_kbps_number->value;
}
}
float time_elapsed;
if (end_time - start_time > 0) {
time_elapsed = (end_time - start_time) / 1000.00;
}
srs_trace("result: play %d kbps, publish %d kbps, check time %.4f S\n"
, play_kbps, pub_kbps, time_elapsed);
break;
}
}
return ret;
}
int SrsBandCheckClient::send_stopped_pub()
{
int ret = ERROR_SUCCESS;
SrsCommonMessage* msg = new SrsCommonMessage;
SrsBandwidthPacket* pkt = new SrsBandwidthPacket;
pkt->command_name = SRS_BW_CHECK_STOPPED_PUBLISH;
msg->set_packet(pkt, 0);
if ((ret = send_message(msg)) != ERROR_SUCCESS) {
srs_error("send stopped pub msg failed. ret=%d", ret);
return ret;
}
srs_info("send stopped pub msg success.");
return ret;
}
int SrsBandCheckClient::send_final()
{
int ret = ERROR_SUCCESS;
SrsCommonMessage* msg = new SrsCommonMessage;
SrsBandwidthPacket* pkt = new SrsBandwidthPacket;
pkt->command_name = SRS_BW_CHECK_FLASH_FINAL;
msg->set_packet(pkt, 0);
if ((ret = send_message(msg)) != ERROR_SUCCESS) {
srs_error("send final msg failed. ret=%d", ret);
return ret;
}
srs_info("send final msg success.");
return ret;
}
SrsBandCheck::SrsBandCheck()
: bandCheck_Client(0)
{
}
SrsBandCheck::~SrsBandCheck()
{
if (bandCheck_Client) {
srs_freep(bandCheck_Client);
}
}
int SrsBandCheck::check(const std::string &app, const std::string &tcUrl)
{
int ret = ERROR_SUCCESS;
if ((ret = connect_server()) != ERROR_SUCCESS) {
srs_error("connect to server failed. ret = %d", ret);
return ret;
}
if ((ret = bandCheck_Client->handshake()) != ERROR_SUCCESS) {
srs_error("handshake failed. ret = %d", ret);
return ret;
}
if ((ret = bandCheck_Client->connect_app(app, tcUrl)) != ERROR_SUCCESS) {
srs_error("handshake failed. ret = %d", ret);
return ret;
}
if ((ret = bandCheck_Client->check_play()) != ERROR_SUCCESS) {
srs_error("band check play failed.");
return ret;
}
if ((ret = bandCheck_Client->check_publish()) != ERROR_SUCCESS) {
srs_error("band check publish failed.");
return ret;
}
return ret;
}
void SrsBandCheck::set_server(const std::string &server, int port)
{
server_address = server;
server_port = port;
}
int SrsBandCheck::connect_server()
{
int ret = ERROR_SUCCESS;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == -1){
ret = ERROR_SOCKET_CREATE;
srs_error("create socket error. ret=%d", ret);
return ret;
}
st_netfd_t stfd = st_netfd_open_socket(sock);
if(stfd == NULL){
ret = ERROR_ST_OPEN_SOCKET;
srs_error("st_netfd_open_socket failed. ret=%d", ret);
return ret;
}
bandCheck_Client = new SrsBandCheckClient(stfd);
// connect to server.
std::string ip = srs_dns_resolve(server_address);
if (ip.empty()) {
ret = ERROR_SYSTEM_IP_INVALID;
srs_error("dns resolve server error, ip empty. ret=%d", ret);
return ret;
}
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(server_port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1){
ret = ERROR_ST_CONNECT;
srs_error("connect to server error. ip=%s, port=%d, ret=%d", ip.c_str(), server_port, ret);
return ret;
}
srs_trace("connect to server success. server=%s, ip=%s, port=%d", server_address.c_str(), ip.c_str(), server_port);
return ret;
}
int init_st()
{
int ret = ERROR_SUCCESS;
if (st_set_eventsys(ST_EVENTSYS_ALT) == -1) {
ret = ERROR_ST_SET_EPOLL;
srs_error("st_set_eventsys use linux epoll failed. ret=%d", ret);
return ret;
}
if(st_init() != 0){
ret = ERROR_ST_INITIALIZE;
srs_error("st_init failed. ret=%d", ret);
return ret;
}
return ret;
}
void print_help(char** argv)
{
printf(
"Usage: %s [OPTION]...\n"
"test band width from client to rtmp server.\n"
"Mandatory arguments to long options are mandatory for short options too.\n"
" -i, --ip the ip or domain that to test\n"
" -p, --port the port that server listen \n"
" -k, --key the key used to test \n"
" -v, --vhost the vhost used to test \n"
" -V, --version output version information and exit \n"
" -h, --help display this help and exit \n"
"\n"
"For example:\n"
" %s -i 192.168.1.248 -p 1935 -v bandcheck.srs.com -k 35c9b402c12a7246868752e2878f7e0e"
"\n\n"
"Exit status:\n"
"0 if OK,\n"
"other if error occured, and the detail should be printed.\n"
"\n\n"
"srs home page: <http://blog.chinaunix.net/uid/25006789.html>\n",
argv[0], argv[0]);
}
void print_version()
{
const char *version = ""
"srs_bandcheck "BUILD_VERSION"\n"
"Copyright (C) 2013 wenjiegit.\n"
"License MIT\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n"
"Written by wenjie.\n";
printf("%s", version);
}
int get_opt(int argc, char *argv[])
{
int ret = ERROR_SUCCESS;
int c;
while ((c = getopt_long (argc, argv, short_options, long_options, NULL)) != -1) {
switch (c) {
case 'i':
if (optarg) {
g_ip = optarg;
}
break;
case 'p':
if (optarg) {
g_port = atoi(optarg);
}
break;
case 'k':
if (optarg) {
g_key = optarg;
}
break;
case 'v':
if (optarg) {
g_vhost = optarg;
}
break;
case 'V':
print_version();
exit(0);
break;
case 'h':
print_help(argv);
exit(0);
break;
default:
printf("see --help or -h\n");
ret = -1;
}
}
return ret;
}

View file

@ -1,6 +1,7 @@
file
main readonly separator,
..\main\srs_main_server.cpp,
..\main\srs_main_bandcheck.cpp,
auto readonly separator,
..\..\objs\srs_auto_headers.hpp,
core readonly separator,