RED5 1.0 录制问题 NetStream.Buffer.Empty
Posted
技术标签:
【中文标题】RED5 1.0 录制问题 NetStream.Buffer.Empty【英文标题】:RED5 1.0 Recording Issue NetStream.Buffer.Empty 【发布时间】:2012-12-19 01:13:44 【问题描述】:我已经在我的 Windows 8 系统上设置了 RED5 1.0 Final,我正在尝试让录制正常工作。我读过的一个教程说要在客户端(在 Flash 中)缓冲数据,然后一旦缓冲区为空,就关闭连接。
我遇到的问题是,当我开始记录缓冲区时,立即报告它是空的 (NetStream.Buffer.Empty)。我已经让它在缓冲区实际填满的地方工作了一两次,但由于某种原因它停止了这种工作。
我可以看到客户端仍在向服务器发送数据,即使我从网络流中分离了相机,因为服务器端的文件继续增长。我目前的解决方案是在录制停止后等待 60 秒,然后再关闭连接。
有趣的是,当没有更多数据包要发送时,我在服务器端看到文件从 mystream.ser 切换到 mystream.flv 并且大小停止增长。我正在考虑在服务器端编写一些代码来等待这个事件发生,然后让客户端知道它可以关闭流。
这是我第一次涉足动作脚本,所以我可能在这里做一些完全错误的事情。请告诉我。
编辑这是客户端代码
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:ns1="*"
minWidth="955" minHeight="600" applicationComplete="init()" >
<fx:Script>
<![CDATA[
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.NetStatusEvent;
import flash.media.Camera;
import flash.media.H264Level;
import flash.media.H264Profile;
import flash.media.H264VideoStreamSettings;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
var cam:Camera = Camera.getCamera();
var mic:Microphone = Microphone.getMicrophone();
var nc:NetConnection = new NetConnection();
var activeStream:NetStream;
private var bufferCheckTimer:Timer;
var recordHalted:Boolean = false;
protected function init(): void
recordButton.enabled = false;
stopButton.enabled = false;
nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
nc.connect("rtmp://localhost/oflaDemo");
nc.client = this;
public function onMetaData(info:Object):void
trace("playback called onMetaData");
public function onBWDone(... rest) : void
// have to have this for an RTMP connection
trace('onBWDone');
public function onBWCheck(... rest) : uint
trace('onBWCheck');
//have to return something, so returning anything :)
return 0;
protected function onNetStatus(event:NetStatusEvent):void
trace(event.info.code);
if(nc.connected)
SetupCameraAndMic();
recordButton.enabled = true;
stopButton.enabled = true;
protected function SetupCameraAndMic(): void
activeStream = new NetStream(nc);
activeStream.bufferTime = 60;
activeStream.client = this;
activeStream.addEventListener(NetStatusEvent.NET_STATUS, handleStreamStatus,false,0,true);
var h264Settings:H264VideoStreamSettings = new H264VideoStreamSettings();
h264Settings.setProfileLevel(H264Profile.BASELINE, H264Level.LEVEL_2);
activeStream.videoStreamSettings = h264Settings;
cam.addEventListener(StatusEvent.STATUS, handleCameraStatus, false, 0, true);
mic.addEventListener(StatusEvent.STATUS, handleMicrophoneStatus, false, 0, true);
cam.setMode(320,240, 15);
cam.setQuality(0, 80);
cam.setKeyFrameInterval(7);
mic.rate = 44;
mic.gain = 75;
mic.setSilenceLevel(0);
mic.setUseEchoSuppression(true);
activeStream.attachCamera(cam);
activeStream.attachAudio(mic);
videoContainer.attachCamera(cam);
private function handleCameraStatus(e:StatusEvent):void
trace("handleCameraStatus - " + e.code);
switch(e.code)
case 'Camera.muted':
// Show a message
break;
case 'Camera.Unmuted':
//finishCamAndMicSetup();
break;
private function handleMicrophoneStatus(e:StatusEvent):void
trace("handleMicrophoneStatus - " + e.code);
switch(e.code)
case 'Microphone.Muted':
// Show a message
break;
case 'Microphone.Unmuted':
//finishCamAndMicSetup();
break;
private function handleStreamStatus(e:NetStatusEvent):void
switch(e.info.code)
case 'NetStream.Buffer.Empty':
trace("NetStream.Buffer.Empty");
break;
case 'NetStream.Buffer.Full':
trace("NetStream.Buffer.Full");
break;
case 'NetStream.Buffer.Flush':
trace("NetStream.Buffer.Flush");
break;
protected function recordButton_clickHandler(event:MouseEvent):void
if(activeStream == null)
SetupCameraAndMic();
if(activeStream != null)
var tempDate:Date = new Date();
var uniqueFileName:String = "RecordME_" + String(tempDate.getMinutes()) + String(tempDate.getMilliseconds());
bufferLabel.text = ""+ activeStream.bufferTime;
activeStream.publish(uniqueFileName, "record");
bufferCheckTimer = new Timer(100);
bufferCheckTimer.addEventListener(TimerEvent.TIMER, handleBufferCheck, false, 0, true);
bufferCheckTimer.start();
private function handleBufferCheck(e:TimerEvent):void
if(activeStream != null)
trace("Buffer: " + activeStream.bufferLength);
statusLabel.text = "Buffer: " + activeStream.bufferLength;
if (recordHalted == true)
if ( activeStream.bufferLength == 0 )
activeStream.close();
activeStream = null;
bufferCheckTimer.stop();
bufferCheckTimer.removeEventListener(TimerEvent.TIMER, handleBufferCheck);
bufferCheckTimer = null;
// OK - playback time
//doRecordingPlayback();
if (bufferCheckTimer != null)
bufferCheckTimer.reset();
bufferCheckTimer.start();
protected function stopButton_clickHandler(event:MouseEvent):void
activeStream.attachCamera(null);
activeStream.attachAudio(null);
videoContainer.attachCamera(null);
recordHalted = true;
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<mx:VideoDisplay id="videoContainer" x="158" y="53"
chromeColor="#3C2020" />
<s:Button id="recordButton" x="396" y="546" label="Record"
click="recordButton_clickHandler(event)"/>
<s:Button id="stopButton" x="491" y="546" label="Stop Recording"
click="stopButton_clickHandler(event)"/>
<s:Label id="statusLabel" x="158" y="555" />
<s:Label x="14" y="408" text="Buffer Set to:"/>
<s:Label id="bufferLabel" x="91" y="408" text="0"/>
</s:Application>
谢谢
【问题讨论】:
如果没有看到您的客户端代码,真的帮不上忙。我们需要看看你在做什么来设置/开始录制。 【参考方案1】:我现在没有正在运行的 RTMP 服务器,所以我只是评论一下我在您的代码中看到的内容。
我认为在发布(录制)流时,您获得的关于首先缓冲内容的建议可能不是一个好主意。也许本教程不是关于发布,而是订阅现有流,在这种情况下,缓冲是个好主意。
您将 bufferTime
设置为 60 秒。 docs 表示您应该将 bufferTime
设置为 0 以进行现场录制。也就是说,您希望在摄像头/麦克风生成数据后立即发送数据。
接下来是您正在使用的Timer
。这似乎是在检查缓冲区长度,以检测记录是否已停止。实际上只有两种情况会停止录制:
我建议使用您的 NetStatusEvent
处理程序方法 (handleStreamStatus()
) 来检查消息“NetStream.Record.Stop”,而不是使用计时器来检查 bufferLength
。这允许您的代码检测录制何时因用户单击“停止”以外的其他原因而停止。
计时器是问题的可能原因。即使您设置了一个很大的bufferTime
值,它也可能无法在 Red 5 服务器上工作或表现不同,或者它可能会被服务器端设置覆盖。无论如何,关键是不要使用bufferLength
来检测录制是否已停止。
有一堆useful messages 与NetStatusEvent
一起发送,我建议您仔细阅读它们,看看它们中的任何一个在您的场景中是否有用。它们非常可靠,似乎可以处理几乎所有可能出现的情况。
我注意到的最后一件事(不是问题,但值得纠正):您在麦克风上启用回声抑制,但除非您使用增强型麦克风,否则这将不起作用:
var mic:Microphone = Microphone.getEnhancedMicrophone();
【讨论】:
感谢 Sunil 的详细回复,非常感谢。我将尝试使用 NetStream.Record.Stop 而不是我拥有的计时器方法并很快回复。我正在关注gist.github.com/987076 和technogumbo.com/tutorials/… 这两个位置的教程,这两个位置都提到使用长度为 60 的缓冲区。 有趣。我在几个应用程序中工作过,我们在录制时没有缓冲任何东西。我不确定在这种情况下缓冲的好处是什么。例如,您当然不想在“实时视频聊天”应用程序中进行缓冲。但也许它在其他场景中也有一些用处。 问题是如果没有缓冲区,我只是关闭流,保存的视频只是完整录制的一小部分。我注意到的是,当我停止录制并设置 activeStream .attachAudio(null) 和 activeStream.attachCamera(null) 服务器停止接收数据包,但继续将 FLV 文件写入磁盘。所以问题可能不在客户端,因为 red5 拥有所有数据包。文件完成写入磁盘后,我才能关闭 activeStream。我尝试等待 Record.Stop 事件,但这仅在我执行 activeStream.close 后触发。【参考方案2】:我认为这是 RED5 最终 V1.0 版本中的一个错误造成的。我恢复到 0.80 版本,我可以很好地录制。
【讨论】:
【参考方案3】:通过更改队列阈值,Red5 V1.0.2 可以很好地进行录制。我看到一个叫jobma的video resume website录制的视频非常好。似乎他们正在使用red5
【讨论】:
以上是关于RED5 1.0 录制问题 NetStream.Buffer.Empty的主要内容,如果未能解决你的问题,请参考以下文章
使用 Red5 媒体服务器和 oflaDemo 应用程序提高录制视频的质量