flash actionscript 3.0 publish h264 stream

NetStream.publish捕捉摄像头的图像,并编码后发送到FMS服务器。flash 11终于支持发布h264的流。因为推送h264的流,需要flash player能编码h264格式视频,在flash player 11加入了h264编码器。

官方参考:

http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/H264VideoStreamSettings.html

编写推送h164的as程序必须要较高版本的sdk,之前使用的flex sdk 4.1的flash player版本是10.0,不能用来编写这个程序。

下载flex sdk:

http://www.adobe.com/devnet/flex/flex-sdk-download.html


h264和h263对比图(同样的码率和分辨率):



as3.0代码:

[javascript] view plain copy print ?
  1. package  
  2. {  
  3.     import fl.controls.Button;  
  4.     import fl.controls.CheckBox;  
  5.     import fl.controls.ComboBox;  
  6.     import fl.controls.Label;  
  7.     import fl.controls.TextInput;  
  8.       
  9.     import flash.display.Sprite;  
  10.     import flash.display.StageAlign;  
  11.     import flash.display.StageScaleMode;  
  12.     import flash.events.Event;  
  13.     import flash.events.MouseEvent;  
  14.     import flash.events.NetStatusEvent;  
  15.     import flash.media.Camera;  
  16.     import flash.media.H264Profile;  
  17.     import flash.media.H264VideoStreamSettings;  
  18.     import flash.media.Microphone;  
  19.     import flash.media.Video;  
  20.     import flash.net.NetConnection;  
  21.     import flash.net.NetStream;  
  22.       
  23.     public class H264Publisher extends Sprite  
  24.     {     
  25.         public function H264Publisher()  
  26.         {  
  27.             if(this.stage == null){  
  28.                 this.addEventListener(Event.ADDED_TO_STAGE, this.onAddedToStage);  
  29.             }  
  30.             else{  
  31.                 this.onAddedToStage(null);  
  32.             }  
  33.         }  
  34.           
  35.         private function onAddedToStage(evt:Event):void{  
  36.             this.stage.align = StageAlign.TOP_LEFT;  
  37.             this.stage.scaleMode = StageScaleMode.NO_SCALE;  
  38.               
  39.             var urlPanel:Sprite = new Sprite();  
  40.             this.addUrlPanel(urlPanel, onMouseClickStartPublish, onMouseClickStopPublish);  
  41.               
  42.             var cameraPanel:Sprite = new Sprite();  
  43.             this.addCameraPanel(cameraPanel);  
  44.               
  45.             var encodingPanel:Sprite = new Sprite();  
  46.             this.addEncodingPanel(encodingPanel);  
  47.               
  48.             urlPanel.x = 10;  
  49.             urlPanel.y = 10;  
  50.               
  51.             cameraPanel.x = urlPanel.x;  
  52.             cameraPanel.y = urlPanel.y + 30;  
  53.               
  54.             encodingPanel.x = cameraPanel.x;  
  55.             encodingPanel.y = cameraPanel.y + 30;  
  56.               
  57.             video = new Video();  
  58.             video.x = encodingPanel.x;  
  59.             video.y = encodingPanel.y + 30;  
  60.               
  61.             this.addChild(urlPanel);  
  62.             this.addChild(cameraPanel);  
  63.             this.addChild(encodingPanel);  
  64.             this.addChild(video);  
  65.         }  
  66.           
  67.         private var fmsUrl:String;  
  68.         private var fmsStream:String;  
  69.         private function discoveryFmsUrl():void{  
  70.             var url:String = txtUrl.text;  
  71.               
  72.             if(url.toLowerCase().indexOf("rtmp://") < 0){  
  73.                 trace("[error] the url must start with rtmp://""error");  
  74.                 return;  
  75.             }  
  76.               
  77.             // remove the start rtmp://   
  78.             url = url.substr(url.toLowerCase().indexOf("rtmp://") + "rtmp://".length);  
  79.               
  80.             var server:String = url.substr(0, url.indexOf("/"));  
  81.             url = url.substr(url.indexOf("/") + 1);  
  82.               
  83.             var port:String = "1935";  
  84.             if(server.indexOf(":") >= 0){  
  85.                 port = server.substr(server.indexOf(":")+1);  
  86.                 server = server.substr(0, server.indexOf(":"));  
  87.             }  
  88.               
  89.             var appIndex:int = -1;  
  90.             for(var i:int = 0; i < this.cbAppLevel.selectedIndex + 1; i++){  
  91.                 if(url.indexOf("/", appIndex + 1) < 0){  
  92.                     break;  
  93.                 }  
  94.                   
  95.                 appIndex = url.indexOf("/", appIndex + 1);  
  96.             }  
  97.             var app:String = url.substr(0, appIndex);  
  98.             var stream:String = url.substr(appIndex + 1);  
  99.               
  100.             // if user input ip address, set the server; otherwise, set the vhost.   
  101.             var serverIsIPAddress:Boolean = true;  
  102.             var serverItems:Array = server.split(".");  
  103.             for(i = 0; i < serverItems.length; i++){  
  104.                 if(isNaN(Number(serverItems[i]))){  
  105.                     serverIsIPAddress = false;  
  106.                 }  
  107.             }  
  108.               
  109.             fmsUrl = "rtmp://" + server + ":" + port + "/" + app;  
  110.             fmsStream = stream;  
  111.         }  
  112.           
  113.         private function buildEncodingParameters(publishStream:NetStream, c:Camera, m:Microphone):void{  
  114.             var x264profile:String = (this.cbX264Profile.selectedLabel == "Main") ? H264Profile.MAIN : H264Profile.BASELINE;  
  115.             var x264level:String = this.cbX264Level.selectedLabel;  
  116.             var x264KeyFrameInterval:int = int(this.cbX264KeyFrameInterval.selectedIndex + 1);  
  117.             var cameraWidth:int = int(this.cbCameraSize.selectedLabel.substr(0, this.cbCameraSize.selectedLabel.indexOf("x")));  
  118.             var cameraHeight:int = int(this.cbCameraSize.selectedLabel.substr(this.cbCameraSize.selectedLabel.indexOf("x") + 1));;  
  119.             var cameraFps:Number = Number(this.cbCameraFps.selectedLabel);  
  120.             var cameraBitrate:int = int(this.cbCameraBitrate.selectedLabel);  
  121.             var cameraQuality:int = 85;  
  122.             var microEncodeQuality:int = 8;  
  123.             var microRate:int = 22; // 22 === 22050 Hz   
  124.               
  125.             trace("[Publish] h.264(x264) encoding parameters: "   
  126.                 + "profile=" + x264profile   
  127.                 + ", level=" + x264level  
  128.                 + ", keyFrameInterval(gop)=" + x264KeyFrameInterval  
  129.                 + "; video(camera) width=" + cameraWidth  
  130.                 + ", height=" + cameraHeight  
  131.                 + ", fps=" + cameraFps  
  132.                 + ", bitrate=" + cameraBitrate  
  133.                 + ", quality=" + cameraQuality  
  134.                 + "; audio(microphone) encodeQuality=" + microEncodeQuality  
  135.                 + ", rate=" + microRate + "(22050Hz)"  
  136.             );  
  137.               
  138.             var h264Settings:H264VideoStreamSettings = new H264VideoStreamSettings();  
  139.             // we MUST set its values first, then set the NetStream.videoStreamSettings, or it will keep the origin values.   
  140.             h264Settings.setProfileLevel(x264profile, x264level);   
  141.             publishStream.videoStreamSettings = h264Settings;  
  142.             // the setKeyFrameInterval/setMode/setQuality use the camera settings.   
  143.             // http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/VideoStreamSettings.html   
  144.             // Note This feature will be supported in future releases of Flash Player and AIR, for now, Camera parameters are used.   
  145.             /*h264Settings.setKeyFrameInterval(4); 
  146.             h264Settings.setMode(800, 600, 15); 
  147.             h264Settings.setQuality(500, 0);*/  
  148.               
  149.             // set the camera and microphone.   
  150.               
  151.             // setKeyFrameInterval(keyFrameInterval:int):void   
  152.             //  keyFrameInterval:int — A value that specifies which video frames are transmitted in full (as keyframes) instead of being    
  153.             //      interpolated by the video compression algorithm. A value of 1 means that every frame is a keyframe, a value of 3 means    
  154.             //      that every third frame is a keyframe, and so on. Acceptable values are 1 through 48.   
  155.             c.setKeyFrameInterval(x264KeyFrameInterval);  
  156.               
  157.             // setMode(width:int, height:int, fps:Number, favorArea:Boolean = true):void   
  158.             //  width:int — The requested capture width, in pixels. The default value is 160.   
  159.             //  height:int — The requested capture height, in pixels. The default value is 120.   
  160.             //  fps:Number — The requested rate at which the camera should capture data, in frames per second. The default value is 15.   
  161.             c.setMode(cameraWidth, cameraHeight, cameraFps);  
  162.               
  163.             // setQuality(bandwidth:int, quality:int):void   
  164.             //  bandwidth:int — Specifies the maximum amount of bandwidth that the current outgoing video feed can use, in bytes per second.    
  165.             //      To specify that the video can use as much bandwidth as needed to maintain the value of quality, pass 0 for bandwidth.    
  166.             //      The default value is 16384.   
  167.             //  quality:int — An integer that specifies the required level of picture quality, as determined by the amount of compression    
  168.             //      being applied to each video frame. Acceptable values range from 1 (lowest quality, maximum compression) to 100    
  169.             //      (highest quality, no compression). To specify that picture quality can vary as needed to avoid exceeding bandwidth,    
  170.             //      pass 0 for quality.   
  171.             //  winlin:   
  172.             //      bandwidth is in bps not kbps. 500*1000 = 500kbps.   
  173.             //      quality=1 is lowest quality, 100 is highest quality.   
  174.             c.setQuality(cameraBitrate * 1000, cameraQuality);  
  175.               
  176.             // if no microphone, donot set the params.   
  177.             if(m == null){  
  178.                 return;  
  179.             }  
  180.               
  181.             // The encoded speech quality when using the Speex codec. Possible values are from 0 to 10. The default value is 6. Higher numbers    
  182.             // represent higher quality but require more bandwidth, as shown in the following table. The bit rate values that are listed represent    
  183.             // net bit rates and do not include packetization overhead.   
  184.             m.encodeQuality = microEncodeQuality;  
  185.               
  186.             // 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    
  187.             // if your sound capture device supports this value. Otherwise, the default value is the next available capture level above 8 kHz that    
  188.             // your sound capture device supports, usually 11 kHz.   
  189.             m.rate = microRate;  
  190.         }  
  191.           
  192.         private var publishStream:NetStream;  
  193.         private var publishConnection:NetConnection;  
  194.         private function onMouseClickStartPublish(evt:MouseEvent):void{  
  195.             // if published, donothing   
  196.             if(publishStream != null){  
  197.                 return;  
  198.             }  
  199.               
  200.             this.btnStartPublish.enabled = false;  
  201.             this.btnStopPublish.enabled = true;  
  202.               
  203.             this.discoveryFmsUrl();  
  204.               
  205.             publishConnection = new NetConnection();  
  206.             var conn:NetConnection = publishConnection;  
  207.               
  208.             conn.client = {};  
  209.             conn.client.onBWDone = function():void{};  
  210.               
  211.             conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void{  
  212.                 trace("[Publish][connection] code:" + evt.info.code);  
  213.                   
  214.                 switch(evt.info.code){  
  215.                     case "NetConnection.Connect.Success":  
  216.                         publishStream = new NetStream(conn);  
  217.                         // microphone and camera   
  218.                         var m:Microphone = Microphone.getMicrophone(cbMicrophone.selectedIndex);  
  219.                         // Remark: the name is the index!   
  220.                         var c:Camera = Camera.getCamera(String(cbCamera.selectedIndex));  
  221.                         if(c == null){  
  222.                             trace("[Publish][error] failed to open camera(name=" + String(cbCamera.selectedIndex) + "): " + cbCamera.selectedLabel, "error");  
  223.                             cleanupPublishedStream();  
  224.                             break;  
  225.                         }  
  226.                         else if(c.muted){  
  227.                             trace("[Publish][error] open camera(name=" + String(cbCamera.selectedIndex) + ") failed, it's muted: " + cbCamera.selectedLabel, "error");  
  228.                             cleanupPublishedStream();  
  229.                             break;  
  230.                         }  
  231.                           
  232.                         buildEncodingParameters(publishStream, c, m);  
  233.                           
  234.                         publishStream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void{  
  235.                             trace("[Publish][NetStreamStatus]" + evt.info.code);  
  236.                               
  237.                             switch(evt.info.code){  
  238.                                 case "NetStream.Publish.Start":  
  239.                                     var h264:H264VideoStreamSettings = publishStream.videoStreamSettings as H264VideoStreamSettings;  
  240.                                     trace("[Publish] video codec: " + h264.codec   
  241.                                         + ", profile=" + h264.profile  
  242.                                         + ", level=" + h264.level  
  243.                                         + ", quality=" + h264.quality  
  244.                                         + ", fps=" + h264.fps  
  245.                                         + ", gop=" + h264.keyFrameInterval  
  246.                                         + ", bandwidth=" + h264.bandwidth  
  247.                                         + ", size=" + h264.width + "x" + h264.height);  
  248.                                     break;  
  249.                                 case "NetStream.Publish.BadName":  
  250.                                     cleanupPublishedStream();  
  251.                                     break;  
  252.                             }  
  253.                         });  
  254.                         publishStream.publish(fmsStream);  
  255.                           
  256.                         // attach video and audio.   
  257.                         trace("[Publish][debug] start publish, using camera(name=" + String(cbCamera.selectedIndex) + "): " + c.name);  
  258.                         publishStream.attachCamera(c);  
  259.                         if(m != null && !m.muted){  
  260.                             trace("[Publish][debug] start publish, using microphone(name=" + String(cbMicrophone.selectedIndex) + "): " + m.name);  
  261.                             publishStream.attachAudio(m);  
  262.                         }  
  263.                         restartPlayback();  
  264.                         break;  
  265.                     case "NetConnection.Connect.Rejected":  
  266.                     case "NetConnection.Connect.Failed":  
  267.                         cleanupPublishedStream();  
  268.                         break;  
  269.                 }  
  270.             });  
  271.               
  272.             conn.connect(fmsUrl);  
  273.         }  
  274.         private function cleanupPublishedStream():void{  
  275.             this.btnStartPublish.enabled = true;  
  276.             this.btnStopPublish.enabled = false;  
  277.             if(this.publishStream != null){  
  278.                 this.publishStream.close();  
  279.             }  
  280.             if(this.publishConnection != null){  
  281.                 this.publishConnection.close();  
  282.             }  
  283.             this.publishStream = null;  
  284.         }  
  285.           
  286.         public var stream:NetStream;  
  287.         private var conn:NetConnection;  
  288.         private var video:Video;  
  289.         private function restartPlayback():void{  
  290.             // stream is playing, resume it.   
  291.             if(this.stream != null){  
  292.                 this.stream.close();  
  293.             }  
  294.               
  295.             conn = new NetConnection();  
  296.               
  297.             conn.client = {};  
  298.             conn.client.onBWDone = function():void{};  
  299.             conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void{  
  300.                 trace("[connection] code:" + evt.info.code + " desc:" + evt.info.description);  
  301.                   
  302.                 switch(evt.info.code){  
  303.                     case "NetConnection.Connect.Success":  
  304.                         stream = new NetStream(conn);  
  305.                         video.attachNetStream(stream);  
  306.                           
  307.                         //stream.bufferTime = 3;   
  308.                         stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void{  
  309.                             trace("[NetStreamStatus]" + evt.info.code + " desc:" + evt.info.description);  
  310.                         });  
  311.                         stream.client = {};  
  312.                         stream.client.onMetaData = function onMetadata(metadata:Object):void{  
  313.                             var o:Object = {};  
  314.                             for(var key:String in metadata){  
  315.                                 o[key] = metadata[key];  
  316.                                 trace("[metadata] " + "key=" + key + ", value=" + o[key]);  
  317.                             }  
  318.                               
  319.                             if(metadata.width == undefined){  
  320.                                 metadata.width = 10;  
  321.                                 trace("[warning] metadata.width is undefied, set to 10");  
  322.                             }  
  323.                             if(metadata.height == undefined){  
  324.                                 metadata.height = 10;  
  325.                                 trace("[warning] metadata.height is undefied, set to 10");  
  326.                             }  
  327.                               
  328.                             video.width = metadata.width;  
  329.                             video.height = metadata.height;  
  330.                         };  
  331.                           
  332.                         if(!cbIsLive.selected){  
  333.                             stream.play(fmsStream, 0);  
  334.                         }  
  335.                         else{  
  336.                             stream.play(fmsStream);  
  337.                         }  
  338.                         break;  
  339.                     case "NetConnection.Connect.Rejected":  
  340.                     case "NetConnection.Connect.Failed":  
  341.                         stream.close();  
  342.                         stream = null;  
  343.                         break;  
  344.                 }  
  345.             });  
  346.             conn.connect(fmsUrl);  
  347.         }  
  348.           
  349.         private function onMouseClickStopPublish(evt:MouseEvent):void{  
  350.             this.cleanupPublishedStream();  
  351.         }  
  352.           
  353.         private var txtUrl:TextInput;  
  354.         private var btnStartPublish:Button;  
  355.         private var btnStopPublish:Button;  
  356.         private var cbAppLevel:ComboBox;  
  357.         private var cbIsLive:CheckBox;  
  358.         private function addUrlPanel(  
  359.             panel:Sprite,   
  360.             onMouseClickStartPublish:Function, onMouseClickStopPublish:Function  
  361.         ):void{  
  362.             var lblUrl:Label = new Label();  
  363.             lblUrl.text = "RTMP Url:";  
  364.             lblUrl.width = 50;  
  365.             panel.addChild(lblUrl);  
  366.               
  367.             txtUrl = new TextInput();  
  368.             txtUrl.width = 380;  
  369.             txtUrl.x = lblUrl.x + lblUrl.width + 3;  
  370.             panel.addChild(txtUrl);  
  371.               
  372.             cbIsLive = new CheckBox();  
  373.             cbIsLive.selected = true;  
  374.             cbIsLive.label = "Live";  
  375.             cbIsLive.width = 53;  
  376.             cbIsLive.x = txtUrl.x + txtUrl.width + 0;  
  377.             panel.addChild(cbIsLive);  
  378.               
  379.             cbAppLevel = new ComboBox();  
  380.             cbAppLevel.addItem({label: "1级App"});  
  381.             cbAppLevel.addItem({label: "2级App"});  
  382.             cbAppLevel.addItem({label: "3级App"});  
  383.             cbAppLevel.addItem({label: "4级App"});  
  384.             cbAppLevel.width = 70;  
  385.             cbAppLevel.x = cbIsLive.x + cbIsLive.width + 0;  
  386.             panel.addChild(cbAppLevel);  
  387.               
  388.             btnStartPublish = new Button();  
  389.             btnStartPublish.label = "发布流";  
  390.             btnStartPublish.width = 60;  
  391.             btnStartPublish.x = cbAppLevel.x + cbAppLevel.width + 3;  
  392.             btnStartPublish.addEventListener(MouseEvent.CLICK, onMouseClickStartPublish);  
  393.             panel.addChild(btnStartPublish);  
  394.               
  395.             btnStopPublish = new Button();  
  396.             btnStopPublish.label = "停止发布";  
  397.             btnStopPublish.width = 60;  
  398.             btnStopPublish.enabled = false;  
  399.             btnStopPublish.x = btnStartPublish.x + btnStartPublish.width + 3;  
  400.             btnStopPublish.addEventListener(MouseEvent.CLICK, onMouseClickStopPublish);  
  401.             panel.addChild(btnStopPublish);  
  402.         }  
  403.           
  404.         private var cbX264Profile:ComboBox;  
  405.         private var cbX264Level:ComboBox;  
  406.         private var cbX264KeyFrameInterval:ComboBox;  
  407.         private var cbCameraSize:ComboBox;  
  408.         private var cbCameraFps:ComboBox;  
  409.         private var cbCameraBitrate:ComboBox;  
  410.         private function addEncodingPanel(  
  411.             panel:Sprite  
  412.         ):void{  
  413.             var lblX264Profile:Label = new Label();  
  414.             lblX264Profile.text = "Profile:";  
  415.             lblX264Profile.width = 38;  
  416.             lblX264Profile.y = 2;  
  417.             panel.addChild(lblX264Profile);  
  418.               
  419.             cbX264Profile = new ComboBox();  
  420.             cbX264Profile.width = 72;  
  421.             cbX264Profile.x = lblX264Profile.x + lblX264Profile.width + 0;  
  422.             panel.addChild(cbX264Profile);  
  423.             cbX264Profile.addItem({label:"Baseline"});  
  424.             cbX264Profile.addItem({label:"Main"});  
  425.               
  426.             var lblX264Level:Label = new Label();  
  427.             lblX264Level.text = "Level:";  
  428.             lblX264Level.width = 32;  
  429.             lblX264Level.y = 2;  
  430.             lblX264Level.x = cbX264Profile.x + cbX264Profile.width + 1;  
  431.             panel.addChild(lblX264Level);  
  432.               
  433.             cbX264Level = new ComboBox();  
  434.             cbX264Level.width = 45;  
  435.             cbX264Level.x = lblX264Level.x + lblX264Level.width + 1;  
  436.             panel.addChild(cbX264Level);  
  437.             var x264Levels:Array = ["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"];  
  438.             for(var i:int = 0; i < x264Levels.length; i++){  
  439.                 cbX264Level.addItem({label:x264Levels[i]});  
  440.             }  
  441.             cbX264Level.selectedIndex = 8;  
  442.               
  443.             var lblX264KeyFrameInterval:Label = new Label();  
  444.             lblX264KeyFrameInterval.text = "GOP:";  
  445.             lblX264KeyFrameInterval.width = 29;  
  446.             lblX264KeyFrameInterval.y = 2;  
  447.             lblX264KeyFrameInterval.x = cbX264Level.x + cbX264Level.width + 1;  
  448.             panel.addChild(lblX264KeyFrameInterval);  
  449.               
  450.             cbX264KeyFrameInterval = new ComboBox();  
  451.             cbX264KeyFrameInterval.width = 87;  
  452.             cbX264KeyFrameInterval.x = lblX264KeyFrameInterval.x + lblX264KeyFrameInterval.width + 1;  
  453.             panel.addChild(cbX264KeyFrameInterval);  
  454.             for(i = 0; i < 48; i++){  
  455.                 cbX264KeyFrameInterval.addItem({label:String(i + 1) + " seconds"});  
  456.             }  
  457.             cbX264KeyFrameInterval.selectedIndex = 3;  
  458.               
  459.             var lblCameraSize:Label = new Label();  
  460.             lblCameraSize.text = "Size:";  
  461.             lblCameraSize.width = 30;  
  462.             lblCameraSize.y = 2;  
  463.             lblCameraSize.x = cbX264KeyFrameInterval.x + cbX264KeyFrameInterval.width + 1;  
  464.             panel.addChild(lblCameraSize);  
  465.               
  466.             cbCameraSize = new ComboBox();  
  467.             cbCameraSize.width = 82;  
  468.             cbCameraSize.x = lblCameraSize.x + lblCameraSize.width + 1;  
  469.             panel.addChild(cbCameraSize);  
  470.             var sizes:Array = ["176x144""320x240""352x240""352x288""640x480""720x480""720x576""800x600""1024x768""1280x720""1360x768""1920x1080"];  
  471.             for(i = 0; i < sizes.length; i++){  
  472.                 cbCameraSize.addItem({label:sizes[i]});  
  473.             }  
  474.             cbCameraSize.selectedIndex = 1;  
  475.               
  476.             var lblCameraFps:Label = new Label();  
  477.             lblCameraFps.text = "FPS:";  
  478.             lblCameraFps.width = 28;  
  479.             lblCameraFps.y = 2;  
  480.             lblCameraFps.x = cbCameraSize.x + cbCameraSize.width + 1;  
  481.             panel.addChild(lblCameraFps);  
  482.               
  483.             cbCameraFps = new ComboBox();  
  484.             cbCameraFps.width = 58;  
  485.             cbCameraFps.x = lblCameraFps.x + lblCameraFps.width + 1;  
  486.             panel.addChild(cbCameraFps);  
  487.             var fpses:Array = ["1.00""4.00""5.00""6.00""8.00""10.00""12.00""14.98""15.00""20.00""24.00""25.00""29.97""30.00""59.94""60.00"];  
  488.             for(i = 0; i < fpses.length; i++){  
  489.                 cbCameraFps.addItem({label:fpses[i]});  
  490.             }  
  491.             cbCameraFps.selectedIndex = 8;  
  492.               
  493.             var lblCameraBitrate:Label = new Label();  
  494.             lblCameraBitrate.text = "Bitrate:";  
  495.             lblCameraBitrate.width = 40;  
  496.             lblCameraBitrate.y = 2;  
  497.             lblCameraBitrate.x = cbCameraFps.x + cbCameraFps.width + 1;  
  498.             panel.addChild(lblCameraBitrate);  
  499.               
  500.             cbCameraBitrate = new ComboBox();  
  501.             cbCameraBitrate.width = 58;  
  502.             cbCameraBitrate.x = lblCameraBitrate.x + lblCameraBitrate.width + 1;  
  503.             panel.addChild(cbCameraBitrate);  
  504.             var bitrates:Array = ["10""50""100""200""350""500""650""800""950""1000""1200""1500""1800""2000""2500""20000"];  
  505.             for(i = 0; i < bitrates.length; i++){  
  506.                 cbCameraBitrate.addItem({label:bitrates[i]});  
  507.             }  
  508.             cbCameraBitrate.selectedIndex = 3;  
  509.         }  
  510.           
  511.         private var cbCamera:ComboBox;  
  512.         private var cbMicrophone:ComboBox;  
  513.         private function addCameraPanel(  
  514.             panel:Sprite  
  515.         ):void{  
  516.             // camera   
  517.             var lblCamera:Label = new Label();  
  518.             lblCamera.text = "Available Cameras:";  
  519.             lblCamera.width = 100;  
  520.             panel.addChild(lblCamera);  
  521.               
  522.             cbCamera = new ComboBox();  
  523.             cbCamera.width = 160;  
  524.             cbCamera.x = lblCamera.x + lblCamera.width + 3;  
  525.             panel.addChild(cbCamera);  
  526.               
  527.             var cameras:Array = Camera.names;  
  528.             for(var i:int = 0; i < cameras.length; i++){  
  529.                 cbCamera.addItem({label:cameras[i]});  
  530.             }  
  531.               
  532.             // microphone   
  533.             var lblMicrophone:Label = new Label();  
  534.             lblMicrophone.text = "Available Microphones:";  
  535.             lblMicrophone.width = 120;  
  536.             lblMicrophone.x = cbCamera.x + cbCamera.width + 10;  
  537.             panel.addChild(lblMicrophone);  
  538.               
  539.             cbMicrophone = new ComboBox();  
  540.             cbMicrophone.width = 180;  
  541.             cbMicrophone.x = lblMicrophone.x + lblMicrophone.width + 3;  
  542.             panel.addChild(cbMicrophone);  
  543.               
  544.             var microphones:Array = Microphone.names;  
  545.             for(i = 0; i < microphones.length; i++){  
  546.                 cbMicrophone.addItem({label:microphones[i]});  
  547.             }  
  548.         }  
  549.     }  
  550. }  
package
{
	import fl.controls.Button;
	import fl.controls.CheckBox;
	import fl.controls.ComboBox;
	import fl.controls.Label;
	import fl.controls.TextInput;
	
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.NetStatusEvent;
	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;
	
	public class H264Publisher extends Sprite
	{	
		public function H264Publisher()
		{
			if(this.stage == null){
				this.addEventListener(Event.ADDED_TO_STAGE, this.onAddedToStage);
			}
			else{
				this.onAddedToStage(null);
			}
		}
		
		private function onAddedToStage(evt:Event):void{
			this.stage.align = StageAlign.TOP_LEFT;
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			
			var urlPanel:Sprite = new Sprite();
			this.addUrlPanel(urlPanel, onMouseClickStartPublish, onMouseClickStopPublish);
			
			var cameraPanel:Sprite = new Sprite();
			this.addCameraPanel(cameraPanel);
			
			var encodingPanel:Sprite = new Sprite();
			this.addEncodingPanel(encodingPanel);
			
			urlPanel.x = 10;
			urlPanel.y = 10;
			
			cameraPanel.x = urlPanel.x;
			cameraPanel.y = urlPanel.y + 30;
			
			encodingPanel.x = cameraPanel.x;
			encodingPanel.y = cameraPanel.y + 30;
			
			video = new Video();
			video.x = encodingPanel.x;
			video.y = encodingPanel.y + 30;
			
			this.addChild(urlPanel);
			this.addChild(cameraPanel);
			this.addChild(encodingPanel);
			this.addChild(video);
		}
		
		private var fmsUrl:String;
		private var fmsStream:String;
		private function discoveryFmsUrl():void{
			var url:String = txtUrl.text;
			
			if(url.toLowerCase().indexOf("rtmp://") < 0){
				trace("[error] the url must start with rtmp://", "error");
				return;
			}
			
			// remove the start rtmp://
			url = url.substr(url.toLowerCase().indexOf("rtmp://") + "rtmp://".length);
			
			var server:String = url.substr(0, url.indexOf("/"));
			url = url.substr(url.indexOf("/") + 1);
			
			var port:String = "1935";
			if(server.indexOf(":") >= 0){
				port = server.substr(server.indexOf(":")+1);
				server = server.substr(0, server.indexOf(":"));
			}
			
			var appIndex:int = -1;
			for(var i:int = 0; i < this.cbAppLevel.selectedIndex + 1; i++){
				if(url.indexOf("/", appIndex + 1) < 0){
					break;
				}
				
				appIndex = url.indexOf("/", appIndex + 1);
			}
			var app:String = url.substr(0, appIndex);
			var stream:String = url.substr(appIndex + 1);
			
			// if user input ip address, set the server; otherwise, set the vhost.
			var serverIsIPAddress:Boolean = true;
			var serverItems:Array = server.split(".");
			for(i = 0; i < serverItems.length; i++){
				if(isNaN(Number(serverItems[i]))){
					serverIsIPAddress = false;
				}
			}
			
			fmsUrl = "rtmp://" + server + ":" + port + "/" + app;
			fmsStream = stream;
		}
		
		private function buildEncodingParameters(publishStream:NetStream, c:Camera, m:Microphone):void{
			var x264profile:String = (this.cbX264Profile.selectedLabel == "Main") ? H264Profile.MAIN : H264Profile.BASELINE;
			var x264level:String = this.cbX264Level.selectedLabel;
			var x264KeyFrameInterval:int = int(this.cbX264KeyFrameInterval.selectedIndex + 1);
			var cameraWidth:int = int(this.cbCameraSize.selectedLabel.substr(0, this.cbCameraSize.selectedLabel.indexOf("x")));
			var cameraHeight:int = int(this.cbCameraSize.selectedLabel.substr(this.cbCameraSize.selectedLabel.indexOf("x") + 1));;
			var cameraFps:Number = Number(this.cbCameraFps.selectedLabel);
			var cameraBitrate:int = int(this.cbCameraBitrate.selectedLabel);
			var cameraQuality:int = 85;
			var microEncodeQuality:int = 8;
			var microRate:int = 22; // 22 === 22050 Hz
			
			trace("[Publish] h.264(x264) encoding parameters: " 
				+ "profile=" + x264profile 
				+ ", level=" + x264level
				+ ", keyFrameInterval(gop)=" + x264KeyFrameInterval
				+ "; video(camera) width=" + cameraWidth
				+ ", height=" + cameraHeight
				+ ", fps=" + cameraFps
				+ ", bitrate=" + cameraBitrate
				+ ", quality=" + cameraQuality
				+ "; audio(microphone) encodeQuality=" + microEncodeQuality
				+ ", rate=" + microRate + "(22050Hz)"
			);
			
			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); 
			publishStream.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);
			
			// if no microphone, donot set the params.
			if(m == null){
				return;
			}
			
			// 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 var publishStream:NetStream;
		private var publishConnection:NetConnection;
		private function onMouseClickStartPublish(evt:MouseEvent):void{
			// if published, donothing
			if(publishStream != null){
				return;
			}
			
			this.btnStartPublish.enabled = false;
			this.btnStopPublish.enabled = true;
			
			this.discoveryFmsUrl();
			
			publishConnection = new NetConnection();
			var conn:NetConnection = publishConnection;
			
			conn.client = {};
			conn.client.onBWDone = function():void{};
			
			conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void{
				trace("[Publish][connection] code:" + evt.info.code);
				
				switch(evt.info.code){
					case "NetConnection.Connect.Success":
						publishStream = new NetStream(conn);
						// microphone and camera
						var m:Microphone = Microphone.getMicrophone(cbMicrophone.selectedIndex);
						// Remark: the name is the index!
						var c:Camera = Camera.getCamera(String(cbCamera.selectedIndex));
						if(c == null){
							trace("[Publish][error] failed to open camera(name=" + String(cbCamera.selectedIndex) + "): " + cbCamera.selectedLabel, "error");
							cleanupPublishedStream();
							break;
						}
						else if(c.muted){
							trace("[Publish][error] open camera(name=" + String(cbCamera.selectedIndex) + ") failed, it's muted: " + cbCamera.selectedLabel, "error");
							cleanupPublishedStream();
							break;
						}
						
						buildEncodingParameters(publishStream, c, m);
						
						publishStream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void{
							trace("[Publish][NetStreamStatus]" + evt.info.code);
							
							switch(evt.info.code){
								case "NetStream.Publish.Start":
									var h264:H264VideoStreamSettings = publishStream.videoStreamSettings as H264VideoStreamSettings;
									trace("[Publish] video codec: " + h264.codec 
										+ ", profile=" + h264.profile
										+ ", level=" + h264.level
										+ ", quality=" + h264.quality
										+ ", fps=" + h264.fps
										+ ", gop=" + h264.keyFrameInterval
										+ ", bandwidth=" + h264.bandwidth
										+ ", size=" + h264.width + "x" + h264.height);
									break;
								case "NetStream.Publish.BadName":
									cleanupPublishedStream();
									break;
							}
						});
						publishStream.publish(fmsStream);
						
						// attach video and audio.
						trace("[Publish][debug] start publish, using camera(name=" + String(cbCamera.selectedIndex) + "): " + c.name);
						publishStream.attachCamera(c);
						if(m != null && !m.muted){
							trace("[Publish][debug] start publish, using microphone(name=" + String(cbMicrophone.selectedIndex) + "): " + m.name);
							publishStream.attachAudio(m);
						}
						restartPlayback();
						break;
					case "NetConnection.Connect.Rejected":
					case "NetConnection.Connect.Failed":
						cleanupPublishedStream();
						break;
				}
			});
			
			conn.connect(fmsUrl);
		}
		private function cleanupPublishedStream():void{
			this.btnStartPublish.enabled = true;
			this.btnStopPublish.enabled = false;
			if(this.publishStream != null){
				this.publishStream.close();
			}
			if(this.publishConnection != null){
				this.publishConnection.close();
			}
			this.publishStream = null;
		}
		
		public var stream:NetStream;
		private var conn:NetConnection;
		private var video:Video;
		private function restartPlayback():void{
			// stream is playing, resume it.
			if(this.stream != null){
				this.stream.close();
			}
			
			conn = new NetConnection();
			
			conn.client = {};
			conn.client.onBWDone = function():void{};
			conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void{
				trace("[connection] code:" + evt.info.code + " desc:" + evt.info.description);
				
				switch(evt.info.code){
					case "NetConnection.Connect.Success":
						stream = new NetStream(conn);
						video.attachNetStream(stream);
						
						//stream.bufferTime = 3;
						stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void{
							trace("[NetStreamStatus]" + evt.info.code + " desc:" + evt.info.description);
						});
						stream.client = {};
						stream.client.onMetaData = function onMetadata(metadata:Object):void{
							var o:Object = {};
							for(var key:String in metadata){
								o[key] = metadata[key];
								trace("[metadata] " + "key=" + key + ", value=" + o[key]);
							}
							
							if(metadata.width == undefined){
								metadata.width = 10;
								trace("[warning] metadata.width is undefied, set to 10");
							}
							if(metadata.height == undefined){
								metadata.height = 10;
								trace("[warning] metadata.height is undefied, set to 10");
							}
							
							video.width = metadata.width;
							video.height = metadata.height;
						};
						
						if(!cbIsLive.selected){
							stream.play(fmsStream, 0);
						}
						else{
							stream.play(fmsStream);
						}
						break;
					case "NetConnection.Connect.Rejected":
					case "NetConnection.Connect.Failed":
						stream.close();
						stream = null;
						break;
				}
			});
			conn.connect(fmsUrl);
		}
		
		private function onMouseClickStopPublish(evt:MouseEvent):void{
			this.cleanupPublishedStream();
		}
		
		private var txtUrl:TextInput;
		private var btnStartPublish:Button;
		private var btnStopPublish:Button;
		private var cbAppLevel:ComboBox;
		private var cbIsLive:CheckBox;
		private function addUrlPanel(
			panel:Sprite, 
			onMouseClickStartPublish:Function, onMouseClickStopPublish:Function
		):void{
			var lblUrl:Label = new Label();
			lblUrl.text = "RTMP Url:";
			lblUrl.width = 50;
			panel.addChild(lblUrl);
			
			txtUrl = new TextInput();
			txtUrl.width = 380;
			txtUrl.x = lblUrl.x + lblUrl.width + 3;
			panel.addChild(txtUrl);
			
			cbIsLive = new CheckBox();
			cbIsLive.selected = true;
			cbIsLive.label = "Live";
			cbIsLive.width = 53;
			cbIsLive.x = txtUrl.x + txtUrl.width + 0;
			panel.addChild(cbIsLive);
			
			cbAppLevel = new ComboBox();
			cbAppLevel.addItem({label: "1级App"});
			cbAppLevel.addItem({label: "2级App"});
			cbAppLevel.addItem({label: "3级App"});
			cbAppLevel.addItem({label: "4级App"});
			cbAppLevel.width = 70;
			cbAppLevel.x = cbIsLive.x + cbIsLive.width + 0;
			panel.addChild(cbAppLevel);
			
			btnStartPublish = new Button();
			btnStartPublish.label = "发布流";
			btnStartPublish.width = 60;
			btnStartPublish.x = cbAppLevel.x + cbAppLevel.width + 3;
			btnStartPublish.addEventListener(MouseEvent.CLICK, onMouseClickStartPublish);
			panel.addChild(btnStartPublish);
			
			btnStopPublish = new Button();
			btnStopPublish.label = "停止发布";
			btnStopPublish.width = 60;
			btnStopPublish.enabled = false;
			btnStopPublish.x = btnStartPublish.x + btnStartPublish.width + 3;
			btnStopPublish.addEventListener(MouseEvent.CLICK, onMouseClickStopPublish);
			panel.addChild(btnStopPublish);
		}
		
		private var cbX264Profile:ComboBox;
		private var cbX264Level:ComboBox;
		private var cbX264KeyFrameInterval:ComboBox;
		private var cbCameraSize:ComboBox;
		private var cbCameraFps:ComboBox;
		private var cbCameraBitrate:ComboBox;
		private function addEncodingPanel(
			panel:Sprite
		):void{
			var lblX264Profile:Label = new Label();
			lblX264Profile.text = "Profile:";
			lblX264Profile.width = 38;
			lblX264Profile.y = 2;
			panel.addChild(lblX264Profile);
			
			cbX264Profile = new ComboBox();
			cbX264Profile.width = 72;
			cbX264Profile.x = lblX264Profile.x + lblX264Profile.width + 0;
			panel.addChild(cbX264Profile);
			cbX264Profile.addItem({label:"Baseline"});
			cbX264Profile.addItem({label:"Main"});
			
			var lblX264Level:Label = new Label();
			lblX264Level.text = "Level:";
			lblX264Level.width = 32;
			lblX264Level.y = 2;
			lblX264Level.x = cbX264Profile.x + cbX264Profile.width + 1;
			panel.addChild(lblX264Level);
			
			cbX264Level = new ComboBox();
			cbX264Level.width = 45;
			cbX264Level.x = lblX264Level.x + lblX264Level.width + 1;
			panel.addChild(cbX264Level);
			var x264Levels:Array = ["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:int = 0; i < x264Levels.length; i++){
				cbX264Level.addItem({label:x264Levels[i]});
			}
			cbX264Level.selectedIndex = 8;
			
			var lblX264KeyFrameInterval:Label = new Label();
			lblX264KeyFrameInterval.text = "GOP:";
			lblX264KeyFrameInterval.width = 29;
			lblX264KeyFrameInterval.y = 2;
			lblX264KeyFrameInterval.x = cbX264Level.x + cbX264Level.width + 1;
			panel.addChild(lblX264KeyFrameInterval);
			
			cbX264KeyFrameInterval = new ComboBox();
			cbX264KeyFrameInterval.width = 87;
			cbX264KeyFrameInterval.x = lblX264KeyFrameInterval.x + lblX264KeyFrameInterval.width + 1;
			panel.addChild(cbX264KeyFrameInterval);
			for(i = 0; i < 48; i++){
				cbX264KeyFrameInterval.addItem({label:String(i + 1) + " seconds"});
			}
			cbX264KeyFrameInterval.selectedIndex = 3;
			
			var lblCameraSize:Label = new Label();
			lblCameraSize.text = "Size:";
			lblCameraSize.width = 30;
			lblCameraSize.y = 2;
			lblCameraSize.x = cbX264KeyFrameInterval.x + cbX264KeyFrameInterval.width + 1;
			panel.addChild(lblCameraSize);
			
			cbCameraSize = new ComboBox();
			cbCameraSize.width = 82;
			cbCameraSize.x = lblCameraSize.x + lblCameraSize.width + 1;
			panel.addChild(cbCameraSize);
			var sizes:Array = ["176x144", "320x240", "352x240", "352x288", "640x480", "720x480", "720x576", "800x600", "1024x768", "1280x720", "1360x768", "1920x1080"];
			for(i = 0; i < sizes.length; i++){
				cbCameraSize.addItem({label:sizes[i]});
			}
			cbCameraSize.selectedIndex = 1;
			
			var lblCameraFps:Label = new Label();
			lblCameraFps.text = "FPS:";
			lblCameraFps.width = 28;
			lblCameraFps.y = 2;
			lblCameraFps.x = cbCameraSize.x + cbCameraSize.width + 1;
			panel.addChild(lblCameraFps);
			
			cbCameraFps = new ComboBox();
			cbCameraFps.width = 58;
			cbCameraFps.x = lblCameraFps.x + lblCameraFps.width + 1;
			panel.addChild(cbCameraFps);
			var fpses:Array = ["1.00", "4.00", "5.00", "6.00", "8.00", "10.00", "12.00", "14.98", "15.00", "20.00", "24.00", "25.00", "29.97", "30.00", "59.94", "60.00"];
			for(i = 0; i < fpses.length; i++){
				cbCameraFps.addItem({label:fpses[i]});
			}
			cbCameraFps.selectedIndex = 8;
			
			var lblCameraBitrate:Label = new Label();
			lblCameraBitrate.text = "Bitrate:";
			lblCameraBitrate.width = 40;
			lblCameraBitrate.y = 2;
			lblCameraBitrate.x = cbCameraFps.x + cbCameraFps.width + 1;
			panel.addChild(lblCameraBitrate);
			
			cbCameraBitrate = new ComboBox();
			cbCameraBitrate.width = 58;
			cbCameraBitrate.x = lblCameraBitrate.x + lblCameraBitrate.width + 1;
			panel.addChild(cbCameraBitrate);
			var bitrates:Array = ["10", "50", "100", "200", "350", "500", "650", "800", "950", "1000", "1200", "1500", "1800", "2000", "2500", "20000"];
			for(i = 0; i < bitrates.length; i++){
				cbCameraBitrate.addItem({label:bitrates[i]});
			}
			cbCameraBitrate.selectedIndex = 3;
		}
		
		private var cbCamera:ComboBox;
		private var cbMicrophone:ComboBox;
		private function addCameraPanel(
			panel:Sprite
		):void{
			// camera
			var lblCamera:Label = new Label();
			lblCamera.text = "Available Cameras:";
			lblCamera.width = 100;
			panel.addChild(lblCamera);
			
			cbCamera = new ComboBox();
			cbCamera.width = 160;
			cbCamera.x = lblCamera.x + lblCamera.width + 3;
			panel.addChild(cbCamera);
			
			var cameras:Array = Camera.names;
			for(var i:int = 0; i < cameras.length; i++){
				cbCamera.addItem({label:cameras[i]});
			}
			
			// microphone
			var lblMicrophone:Label = new Label();
			lblMicrophone.text = "Available Microphones:";
			lblMicrophone.width = 120;
			lblMicrophone.x = cbCamera.x + cbCamera.width + 10;
			panel.addChild(lblMicrophone);
			
			cbMicrophone = new ComboBox();
			cbMicrophone.width = 180;
			cbMicrophone.x = lblMicrophone.x + lblMicrophone.width + 3;
			panel.addChild(cbMicrophone);
			
			var microphones:Array = Microphone.names;
			for(i = 0; i < microphones.length; i++){
				cbMicrophone.addItem({label:microphones[i]});
			}
		}
	}
}

其中用到了FlashCS5的控件,可以将c:\Program Files (x86)\Adobe\Adobe Flash CS5\Common\Configuration\Components\User Interface.fla打开后导出swc,然后在actionscript3项目中引用这个swc就可以了。
1、资源项目源码均已通过严格测试验证,保证能够正常运行;、 2项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行;、 2项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值