winlin

support publish

... ... @@ -116,11 +116,10 @@ function srs_init(rtmp_url, hls_url, modal_player) {
/**
* the SrsPlayer object.
* @param container the html container id.
* @param stream_url the url of stream, rtmp or http.
* @param width a float value specifies the width of player.
* @param height a float value specifies the height of player.
*/
function SrsPlayer(container, stream_url, width, height) {
function SrsPlayer(container, width, height) {
if (!SrsPlayer.__id) {
SrsPlayer.__id = 100;
}
... ... @@ -131,12 +130,12 @@ function SrsPlayer(container, stream_url, width, height) {
SrsPlayer.__players.push(this);
this.container = container;
this.stream_url = stream_url;
this.width = width;
this.height = height;
this.id = SrsPlayer.__id++;
this.callbackObj = null;
this.stream_url = null;
this.buffer_time = 0.8; // default to 0.8
this.callbackObj = null;
// callback set the following values.
this.meatadata = {}; // for on_player_metadata
... ... @@ -178,7 +177,12 @@ SrsPlayer.prototype.start = function() {
return this;
}
SrsPlayer.prototype.play = function() {
/**
* play the stream.
* @param stream_url the url of stream, rtmp or http.
*/
SrsPlayer.prototype.play = function(url) {
this.stream_url = url;
this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time);
}
SrsPlayer.prototype.stop = function() {
... ... @@ -233,7 +237,6 @@ SrsPlayer.prototype.set_bt = function(buffer_time) {
this.callbackObj.ref.__set_bt(buffer_time);
}
SrsPlayer.prototype.on_player_ready = function() {
this.play();
}
SrsPlayer.prototype.on_player_metadata = function(metadata) {
// ignore.
... ... @@ -286,4 +289,131 @@ function __srs_on_player_timer(id, time, buffer_length) {
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
/**
* the SrsPublisher object.
* @param container the html container id.
* @param width a float value specifies the width of publisher.
* @param height a float value specifies the height of publisher.
*/
function SrsPublisher(container, width, height) {
if (!SrsPublisher.__id) {
SrsPublisher.__id = 100;
}
if (!SrsPublisher.__publishers) {
SrsPublisher.__publishers = [];
}
SrsPublisher.__publishers.push(this);
this.container = container;
this.width = width;
this.height = height;
this.id = SrsPublisher.__id++;
this.callbackObj = null;
// set the values when publish.
this.url = null;
this.vcodec = {};
this.acodec = {};
// callback set the following values.
this.cameras = [];
this.microphones = [];
this.code = 0;
// error code defines.
this.error_device_muted = 100;
}
/**
* user can set some callback, then start the publisher.
* callbacks:
* on_publisher_ready(cameras, microphones):int, when srs publisher ready, user can publish.
* on_publisher_error(code):int, when srs publisher error, callback this method.
*/
SrsPublisher.prototype.start = function() {
// embed the flash.
var flashvars = {};
flashvars.id = this.id;
flashvars.on_publisher_ready = "__srs_on_publisher_ready";
flashvars.on_publisher_error = "__srs_on_publisher_error";
var params = {};
params.wmode = "opaque";
params.allowFullScreen = "true";
params.allowScriptAccess = "always";
var attributes = {};
var self = this;
swfobject.embedSWF(
"srs_publisher/release/srs_publisher.swf", this.container,
this.width, this.height,
"11.1", "js/AdobeFlashPlayerInstall.swf",
flashvars, params, attributes,
function(callbackObj){
self.callbackObj = callbackObj;
}
);
return this;
}
/**
* publish stream to server.
* @param url a string indicates the rtmp url to publish.
* @param vcodec an object contains the video codec info.
* @param acodec an object contains the audio codec info.
*/
SrsPublisher.prototype.publish = function(url, vcodec, acodec) {
this.url = url;
this.vcodec = vcodec;
this.acodec = acodec;
this.callbackObj.ref.__publish(url, this.width, this.height, vcodec, acodec);
}
SrsPublisher.prototype.stop = function() {
this.callbackObj.ref.__stop();
}
/**
* when publisher ready.
* @param cameras a string array contains the names of cameras.
* @param microphones a string array contains the names of microphones.
*/
SrsPublisher.prototype.on_publisher_ready = function(cameras, microphones) {
}
/**
* when publisher error.
* @code the error code.
*/
SrsPublisher.prototype.on_publisher_error = function(code) {
throw new Error("publisher error. code=" + code);
}
function __srs_find_publisher(id) {
for (var i = 0; i < SrsPublisher.__publishers.length; i++) {
var publisher = SrsPublisher.__publishers[i];
if (publisher.id != id) {
continue;
}
return publisher;
}
throw new Error("publisher not found. id=" + id);
}
function __srs_on_publisher_ready(id, cameras, microphones) {
var publisher = __srs_find_publisher(id);
publisher.cameras = cameras;
publisher.microphones = microphones;
publisher.on_publisher_ready(cameras, microphones);
}
function __srs_on_publisher_error(id, code) {
var publisher = __srs_find_publisher(id);
publisher.code = code;
publisher.on_publisher_error(code);
}
... ...
... ... @@ -35,14 +35,13 @@
$("#main_modal").on("show", function(){
$("#div_container").remove();
var obj = $("<div/>");
$(obj).attr("id", "div_container");
var div_container = $("<div/>");
$(div_container).attr("id", "div_container");
$("#player").append(div_container);
var player = $("<div/>");
$(obj).append(player);
$(obj).attr("id", "player_id");
$("#player").append(obj);
$(player).attr("id", "player_id");
$(div_container).append(player);
var conf = {
file: _url,
... ...
... ... @@ -21,14 +21,13 @@
function osmf_play(url) {
$("#div_container").remove();
var obj = $("<div/>");
$(obj).attr("id", "div_container");
var div_container = $("<div/>");
$(div_container).attr("id", "div_container");
$("#player").append(div_container);
var player = $("<div/>");
$(obj).append(player);
$(obj).attr("id", "player_id");
$("#player").append(obj);
$(player).attr("id", "player_id");
$(div_container).append(player);
var flashvars = {};
flashvars.src = url;
... ...
... ... @@ -80,28 +80,26 @@
$("#div_container").remove();
var obj = $("<div/>");
$(obj).attr("id", "div_container");
var div_container = $("<div/>");
$(div_container).attr("id", "div_container");
$("#player").append(div_container);
var player = $("<div/>");
$(obj).append(player);
$(obj).attr("id", "player_id");
$("#player").append(obj);
$(player).attr("id", "player_id");
$(div_container).append(player);
var url = $("#txt_url").val();
srs_player = new SrsPlayer("player_id", url,
srs_get_player_width(), srs_get_player_height());
srs_player = new SrsPlayer("player_id", srs_get_player_width(), srs_get_player_height());
srs_player.on_player_ready = function() {
select_buffer_time("#btn_bt_0_8", 0.8);
srs_player.play();
}
srs_player.play(url);
};
srs_player.on_player_metadata = function(metadata) {
$("#btn_dar_original").text("视频原始比例" + "(" + metadata.width + ":" + metadata.height + ")");
select_dar("#btn_dar_original", 0, 0);
select_fs_size("#btn_fs_size_screen_100", "screen", 100);
}
};
srs_player.on_player_timer = function(time, buffer_length) {
var buffer = buffer_length / srs_player.buffer_time * 100;
$("#pb_buffer").width(Number(buffer).toFixed(1) + "%");
... ... @@ -124,7 +122,7 @@
time_str += padding(parseInt(time), 2, '0');
// show
$("#txt_time").val(time_str);
}
};
srs_player.start();
});
... ... @@ -218,6 +216,11 @@
select_buffer_time("#btn_bt_30", 30);
});
}
var query = parse_query_string();
if (query.autostart == "true") {
$("#main_modal").modal({show:true, keyboard:false});
}
});
</script>
</head>
... ...
... ... @@ -258,6 +258,10 @@ package
* function for js to call: to stop the stream. ignore if not play.
*/
private function js_call_stop():void {
if (this.media_video) {
this.removeChild(this.media_video);
this.media_video = null;
}
if (this.media_stream) {
this.media_stream.close();
this.media_stream = null;
... ... @@ -266,10 +270,6 @@ package
this.media_conn.close();
this.media_conn = null;
}
if (this.media_video) {
this.removeChild(this.media_video);
this.media_video = null;
}
}
/**
... ...
... ... @@ -14,9 +14,174 @@
}
</style>
<script type="text/javascript">
var srs_publisher = null;
var remote_player = null;
$(function(){
update_nav();
// get the vhost and port to set the default url.
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
// url set to: rtmp://demo:1935/live/livestream
srs_init("#txt_url", null, null);
$("#btn_video_settings").click(function(){
$("#video_modal").modal({show:true});
});
$("#btn_audio_settings").click(function(){
$("#audio_modal").modal({show:true});
});
$("#btn_publish").click(on_user_publish);
update_play_url();
// start the publisher.
srs_publisher = new SrsPublisher("local_publisher", 430, 185);
srs_publisher.on_publisher_ready = function(cameras, microphones) {
$("#sl_cameras").empty();
for (var i = 0; i < cameras.length; i++) {
$("#sl_cameras").append("<option value='" + i + "'>" + cameras[i] + "</option");
}
$("#sl_microphones").empty();
for (var i = 0; i < microphones.length; i++) {
$("#sl_microphones").append("<option value='" + i + "'>" + microphones[i] + "</option");
}
$("#sl_vcodec").empty();
var vcodecs = ["h264", "vp6"];
for (var i = 0; i < vcodecs.length; i++) {
$("#sl_vcodec").append("<option value='" + vcodecs[i] + "'>" + vcodecs[i] + "</option");
}
$("#sl_profile").empty();
var profiles = ["baseline", "main"];
for (var i = 0; i < profiles.length; i++) {
$("#sl_profile").append("<option value='" + profiles[i] + "'>" + profiles[i] + "</option");
}
$("#sl_level").empty();
var levels = ["1", "1b", "1.1", "1.2", "1.3",
"2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
for (var i = 0; i < levels.length; i++) {
$("#sl_level").append("<option value='" + levels[i] + "'>" + levels[i] + "</option");
}
$("#sl_level option[value='4.1']").attr("selected", true);
$("#sl_gop").empty();
var gops = ["0.3", "0.5", "1", "2", "3", "4",
"5", "6", "7", "8", "9", "10", "15", "20"];
for (var i = 0; i < gops.length; i++) {
$("#sl_gop").append("<option value='" + gops[i] + "'>" + gops[i] + "秒</option");
}
$("#sl_gop option[value='5']").attr("selected", true);
$("#sl_size").empty();
var sizes = ["176x144", "320x240", "352x240",
"352x288", "460x240", "640x480", "720x480", "720x576", "800x600",
"1024x768", "1280x720", "1360x768", "1920x1080"];
for (i = 0; i < sizes.length; i++) {
$("#sl_size").append("<option value='" + sizes[i] + "'>" + sizes[i] + "</option");
}
$("#sl_size option[value='460x240']").attr("selected", true);
$("#sl_fps").empty();
var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
for (i = 0; i < fpses.length; i++) {
$("#sl_fps").append("<option value='" + fpses[i] + "'>" + Number(fpses[i]).toFixed(2) + " 帧/秒</option");
}
$("#sl_fps option[value='15']").attr("selected", true);
$("#sl_bitrate").empty();
var bitrates = ["50", "200", "350", "500", "650", "800",
"950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
for (i = 0; i < bitrates.length; i++) {
$("#sl_bitrate").append("<option value='" + bitrates[i] + "'>" + bitrates[i] + " kbps</option");
}
$("#sl_bitrate option[value='350']").attr("selected", true);
};
srs_publisher.on_publisher_error = function(code) {
if (code == srs_publisher.error_device_muted) {
error(code, "摄像头和麦克风被禁用,请右键flash播放器启用。");
} else {
error(code, "未知系统错误");
}
};
srs_publisher.start();
// start the player.
remote_player = new SrsPlayer("remote_player", 430, 185);
remote_player.on_player_ready = function() {
};
remote_player.start();
});
function update_play_url() {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = $("#txt_url").val().replace("rtmp://", "http://");
var url = "http://" + window.location.host;
url += window.location.pathname.substr(0, window.location.pathname.lastIndexOf("/"));
url += "/srs_player.html?";
url += "vhost=" + a.hostname;
url += "&port=" + a.port;
url += "&app=" + a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
url += "&stream=" + a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
// autostart
url += "&autostart=true";
$("#txt_play_url").text(url);
$("#txt_play_url").attr("href", url);
}
function on_user_publish() {
if ($("#btn_publish").text() == "停止发布") {
srs_publisher.stop();
$("#btn_publish").text("发布视频");
return;
}
$("#btn_publish").text("停止发布");
update_play_url();
var url = $("#txt_url").val();
var vcodec = {};
var acodec = {};
acodec.device_code = $("#sl_microphones").val();
acodec.device_name = $("#sl_microphones").text();
vcodec.device_code = $("#sl_cameras").find("option:selected").val();
vcodec.device_name = $("#sl_cameras").find("option:selected").text();
vcodec.codec = $("#sl_vcodec").find("option:selected").val();
vcodec.profile = $("#sl_profile").find("option:selected").val();
vcodec.level = $("#sl_level").find("option:selected").val();
vcodec.fps = $("#sl_fps").find("option:selected").val();
vcodec.gop = $("#sl_gop").find("option:selected").val();
vcodec.size = $("#sl_size").find("option:selected").val();
vcodec.bitrate = $("#sl_bitrate").find("option:selected").val();
info("开始推流到服务器");
srs_publisher.publish(url, vcodec, acodec);
// replay the url.
remote_player.stop();
remote_player.play(url);
}
function info(desc) {
$("#txt_log").removeClass("alert-error").addClass("alert-info");
$("#txt_log_title").text("Info:");
$("#txt_log_msg").text(desc);
}
function error(code, desc) {
$("#txt_log").removeClass("alert-info").addClass("alert-error");
$("#txt_log_title").text("Error:");
$("#txt_log_msg").text("code: " + code + ", " + desc);
}
</script>
</head>
<body>
... ... @@ -38,6 +203,187 @@
</div>
</div>
<div class="container">
<div class="alert alert-info fade in" id="txt_log">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong><span id="txt_log_title">Usage:</span></strong>
<span id="txt_log_msg">输入地址后点击发布按钮</span>
</div>
<div class="control-group">
<div class="form-inline">
<button class="btn" id="btn_video_settings">视频编码配置</button>
<button class="btn" id="btn_audio_settings">音频编码配置</button>
</div>
</div>
<div class="control-group">
<div class="form-inline">
发布地址:
<input type="text" id="txt_url" class="input-xxlarge" value=""></input>
<button class="btn" id="btn_publish">发布视频</button>
</div>
</div>
<div class="control-group">
<div class="form-inline">
观看地址:
<a id="txt_play_url" class="input-xxlarge" href="srs_player.html">srs_player.html</a>
</div>
</div>
<div id="video_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>视频编码</h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<div class="control-group">
<label class="control-label" for="sl_cameras">
摄像头
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span4" id="sl_cameras"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_vcodec">
Codec
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_vcodec"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_profile">
Profile
<a id="sl_profile_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_profile"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_level">
Level
<a id="sl_level_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_level"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_gop">
GOP
<a id="sl_gop_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_gop"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_size">
尺寸
<a id="sl_size_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_size"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_fps">
帧率
<a id="sl_fps_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_fps"></select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="sl_bitrate">
码率
<a id="sl_bitrate_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span2" id="sl_bitrate"></select>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
</div>
</div>
<div id="audio_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>音频编码</h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<div class="control-group">
<label class="control-label" for="sl_microphones">
麦克风
<a id="worker_id_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
<img src="img/tooltip.png"/>
</a>
</label>
<div class="controls">
<select class="span4" id="sl_microphones"></select>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
</div>
</div>
<div class="container">
<div class="row-fluid">
<div class="span6">
<div class="accordion-group">
<div class="accordion-heading">
<span class="accordion-toggle" data-toggle="collapse" href="#collapseOne">
<strong>本地摄像头</strong>
</span>
</div>
<div id="collapseOne" class="accordion-body collapse in">
<div class="accordion-inner">
<div id="local_publisher"></div>
</div>
</div>
</div>
</div>
<div class="span6">
<div class="accordion-group">
<div class="accordion-heading">
<span class="accordion-toggle" data-toggle="collapse" href="#collapseTwo">
<strong>远程服务器</strong>
</span>
</div>
<div id="collapseTwo" class="accordion-body collapse in">
<div class="accordion-inner">
<div id="remote_player"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<hr>
<footer>
<p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team &copy; 2013</a></p>
... ...
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.NetStatusEvent;
import flash.external.ExternalInterface;
import flash.media.Camera;
import flash.media.H264Profile;
import flash.media.H264VideoStreamSettings;
import flash.media.Microphone;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.ui.ContextMenu;
import flash.utils.setTimeout;
public class srs_publisher extends Sprite
{
// user set id.
private var js_id:String = null;
// user set callback
private var js_on_publisher_ready:String = null;
private var js_on_publisher_error:String = null;
// publish param url.
private var user_url:String = null;
// play param, user set width and height
private var user_w:int = 0;
private var user_h:int = 0;
private var user_vcodec:Object = {};
private var user_acodec:Object = {};
// media specified.
private var media_conn:NetConnection = null;
private var media_stream:NetStream = null;
private var media_video:Video = null;
private var media_camera:Camera = null;
private var media_microphone:Microphone = null;
// error code.
private const error_device_muted:int = 100;
public function srs_publisher()
{
if (!this.stage) {
this.addEventListener(Event.ADDED_TO_STAGE, this.system_on_add_to_stage);
} else {
this.system_on_add_to_stage(null);
}
}
/**
* system event callback, when this control added to stage.
* the main function.
*/
private function system_on_add_to_stage(evt:Event):void {
this.removeEventListener(Event.ADDED_TO_STAGE, this.system_on_add_to_stage);
this.stage.align = StageAlign.TOP_LEFT;
this.stage.scaleMode = StageScaleMode.NO_SCALE;
this.contextMenu = new ContextMenu();
this.contextMenu.hideBuiltInItems();
var flashvars:Object = this.root.loaderInfo.parameters;
if (!flashvars.hasOwnProperty("id")) {
throw new Error("must specifies the id");
}
this.js_id = flashvars.id;
this.js_on_publisher_ready = flashvars.on_publisher_ready;
this.js_on_publisher_error = flashvars.on_publisher_error;
flash.utils.setTimeout(this.system_on_js_ready, 0);
}
/**
* system callack event, when js ready, register callback for js.
* the actual main function.
*/
private function system_on_js_ready():void {
if (!flash.external.ExternalInterface.available) {
trace("js not ready, try later.");
flash.utils.setTimeout(this.system_on_js_ready, 100);
return;
}
flash.external.ExternalInterface.addCallback("__publish", this.js_call_publish);
flash.external.ExternalInterface.addCallback("__stop", this.js_call_stop);
var cameras:Array = Camera.names;
var microphones:Array = Microphone.names;
trace("retrieve system cameras(" + cameras + ") and microphones(" + microphones + ")");
flash.external.ExternalInterface.call(this.js_on_publisher_ready, this.js_id, cameras, microphones);
}
/**
* notify the js an error occur.
*/
private function system_error(code:int, desc:String):void {
trace("system error, code=" + code + ", error=" + desc);
flash.external.ExternalInterface.call(this.js_on_publisher_error, this.js_id, code);
}
/**
* publish stream to server.
* @param url a string indicates the rtmp url to publish.
* @param _width, the player width.
* @param _height, the player height.
* @param vcodec an object contains the video codec info.
* @param acodec an object contains the audio codec info.
*/
private function js_call_publish(url:String, _width:int, _height:int, vcodec:Object, acodec:Object):void {
trace("start to publish to " + url + ", vcodec " + JSON.stringify(vcodec) + ", acodec " + JSON.stringify(acodec));
this.user_url = url;
this.user_w = _width;
this.user_h = _height;
this.user_vcodec = vcodec;
this.user_acodec = acodec;
this.js_call_stop();
// microphone and camera
var m:Microphone = Microphone.getMicrophone(acodec.device_code);
if(m == null){
trace("failed to open microphone " + acodec.device_code + "(" + acodec.device_name + ")");
}
if(m.muted){
trace("Access Denied, microphone " + acodec.device_code + "(" + acodec.device_name + ") is muted");
m = null;
}
// Remark: the name is the index!
var c:Camera = Camera.getCamera(vcodec.device_code);
if(c == null){
trace("failed to open camera " + vcodec.device_code + "(" + vcodec.device_name + ")");
}
if(c.muted){
trace("Access Denied, camera " + vcodec.device_code + "(" + vcodec.device_name + ") is muted");
c = null;
}
if (m == null && c == null) {
system_error(error_device_muted, "failed to publish, for neither camera or microphone is ok.");
return;
}
this.media_camera = c;
this.media_microphone = m;
this.media_conn = new NetConnection();
this.media_conn.client = {};
this.media_conn.client.onBWDone = function():void {};
this.media_conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {
trace ("NetConnection: code=" + evt.info.code);
// TODO: FIXME: failed event.
if (evt.info.code != "NetConnection.Connect.Success") {
return;
}
media_stream = new NetStream(media_conn);
media_stream.client = {};
media_stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {
trace ("NetStream: code=" + evt.info.code);
// TODO: FIXME: failed event.
});
__build_video_codec(media_stream, c, vcodec);
__build_audio_codec(media_stream, m, acodec);
if (media_microphone) {
media_stream.attachAudio(m);
}
if (media_camera) {
media_stream.attachCamera(c);
}
var streamName:String = url.substr(url.lastIndexOf("/"));
media_stream.publish(streamName);
media_video = new Video();
media_video.width = _width;
media_video.height = _height;
media_video.attachCamera(media_camera);
media_video.smoothing = true;
addChild(media_video);
//__draw_black_background(_width, _height);
// lowest layer, for mask to cover it.
setChildIndex(media_video, 0);
});
var tcUrl:String = this.user_url.substr(0, this.user_url.lastIndexOf("/"));
this.media_conn.connect(tcUrl);
}
/**
* function for js to call: to stop the stream. ignore if not publish.
*/
private function js_call_stop():void {
if (this.media_video) {
this.removeChild(this.media_video);
this.media_video = null;
}
if (this.media_stream) {
this.media_stream.close();
this.media_stream = null;
}
if (this.media_conn) {
this.media_conn.close();
this.media_conn = null;
}
}
private function __build_audio_codec(stream:NetStream, m:Microphone, acodec:Object):void {
if (!m) {
return;
}
// if no microphone, donot set the params.
if(m == null){
return;
}
// use default values.
var microEncodeQuality:int = 8;
var microRate:int = 22; // 22 === 22050 Hz
trace("[Publish] audio encoding parameters: "
+ "audio(microphone) encodeQuality=" + microEncodeQuality
+ ", rate=" + microRate + "(22050Hz)"
);
// The encoded speech quality when using the Speex codec. Possible values are from 0 to 10. The default value is 6. Higher numbers
// represent higher quality but require more bandwidth, as shown in the following table. The bit rate values that are listed represent
// net bit rates and do not include packetization overhead.
m.encodeQuality = microEncodeQuality;
// The rate at which the microphone is capturing sound, in kHz. Acceptable values are 5, 8, 11, 22, and 44. The default value is 8 kHz
// if your sound capture device supports this value. Otherwise, the default value is the next available capture level above 8 kHz that
// your sound capture device supports, usually 11 kHz.
m.rate = microRate;
}
private function __build_video_codec(stream:NetStream, c:Camera, vcodec:Object):void {
if (!c) {
return;
}
if(vcodec.codec == "vp6"){
trace("use VP6, donot use H.264 publish encoding.");
return;
}
var x264profile:String = (vcodec.profile == "main") ? H264Profile.MAIN : H264Profile.BASELINE;
var x264level:String = vcodec.level;
var cameraFps:Number = Number(vcodec.fps);
var x264KeyFrameInterval:int = int(vcodec.gop * cameraFps);
var cameraWidth:int = String(vcodec.size).split("x")[0];
var cameraHeight:int = String(vcodec.size).split("x")[1];
var cameraBitrate:int = int(vcodec.bitrate);
// use default values.
var cameraQuality:int = 85;
trace("[Publish] video h.264(x264) encoding parameters: "
+ "profile=" + x264profile
+ ", level=" + x264level
+ ", keyFrameInterval(gop)=" + x264KeyFrameInterval
+ "; video(camera) width=" + cameraWidth
+ ", height=" + cameraHeight
+ ", fps=" + cameraFps
+ ", bitrate=" + cameraBitrate
+ ", quality=" + cameraQuality
);
var h264Settings:H264VideoStreamSettings = new H264VideoStreamSettings();
// we MUST set its values first, then set the NetStream.videoStreamSettings, or it will keep the origin values.
h264Settings.setProfileLevel(x264profile, x264level);
stream.videoStreamSettings = h264Settings;
// the setKeyFrameInterval/setMode/setQuality use the camera settings.
// http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/VideoStreamSettings.html
// Note This feature will be supported in future releases of Flash Player and AIR, for now, Camera parameters are used.
//
//h264Settings.setKeyFrameInterval(4);
//h264Settings.setMode(800, 600, 15);
//h264Settings.setQuality(500, 0);
// set the camera and microphone.
// setKeyFrameInterval(keyFrameInterval:int):void
// keyFrameInterval:int — A value that specifies which video frames are transmitted in full (as keyframes) instead of being
// interpolated by the video compression algorithm. A value of 1 means that every frame is a keyframe, a value of 3 means
// that every third frame is a keyframe, and so on. Acceptable values are 1 through 48.
c.setKeyFrameInterval(x264KeyFrameInterval);
// setMode(width:int, height:int, fps:Number, favorArea:Boolean = true):void
// width:int — The requested capture width, in pixels. The default value is 160.
// height:int — The requested capture height, in pixels. The default value is 120.
// fps:Number — The requested rate at which the camera should capture data, in frames per second. The default value is 15.
c.setMode(cameraWidth, cameraHeight, cameraFps);
// setQuality(bandwidth:int, quality:int):void
// bandwidth:int — Specifies the maximum amount of bandwidth that the current outgoing video feed can use, in bytes per second.
// To specify that the video can use as much bandwidth as needed to maintain the value of quality, pass 0 for bandwidth.
// The default value is 16384.
// quality:int — An integer that specifies the required level of picture quality, as determined by the amount of compression
// being applied to each video frame. Acceptable values range from 1 (lowest quality, maximum compression) to 100
// (highest quality, no compression). To specify that picture quality can vary as needed to avoid exceeding bandwidth,
// pass 0 for quality.
// winlin:
// bandwidth is in bps not kbps. 500*1000 = 500kbps.
// quality=1 is lowest quality, 100 is highest quality.
c.setQuality(cameraBitrate * 1000, cameraQuality);
}
}
}
\ No newline at end of file
... ...