AS3 NetStream AppendBytes寻求问题

时间:2015-08-24 12:40:14

标签: actionscript-3 flash ffmpeg air netstream

我在AS3中遇到NetStream问题。我正在开发的项目允许用户浏览视频(本地)并播放。我遇到的问题是netStream.seek(0);我可以告诉它没有做任何事情,虽然我进入了NetStatusEvent函数并触发了NetStream.Seek.Notify。我使用NativeProcess,以下功能对我们有所不同。

public function ProgressEventOutputHandler(e:ProgressEvent):void {
    videoByteArray = new ByteArray();
    nativeProcess.standardOutput.readBytes(videoByteArray, 0, nativeProcess.standardOutput.bytesAvailable);
    netStream.appendBytes(videoByteArray);
}

我在这里遗漏了什么吗?我在使用netStream.seek(0);之前暂停netStream。

修改

为了解决这个问题我遵循了VC的说明。我已经完成了以下工作:

  • videoByteArray = new ByteArray();移至我的初始化函数,并在此函数中创建了tempVideoByteArray = new ByteArray();

  • 更新我的ProgressEventOutputHandler函数,使其不再为videoByteArray创建新的ByteArray并更改此行 - nativeProcess.standardOutput.readBytes(videoByteArray, videoByteArray.length, nativeProcess.standardOutput.bytesAvailable);

我没有改变任何其他内容,现在视频无法加载。如果我允许在ProgressEventOutputHandler函数内创建新的ByteArray,则视频会再次加载。

1 个答案:

答案 0 :(得分:1)

简短版本:

尝试我粘贴的代码:Github Snippet链接

长版:

这个有点长,但希望它一劳永逸地帮助 ...不要担心砖墙的事情,墙壁是被粉碎了。为了让您受到启发,请查看VC的一些内部演示:使用appendBytes的一个实验室:

PS:我将使用URLStream方法,因为这对那些加载本地或在线文件的人来说是一个更有用的答案。您可以从urlstream.progressEvent更改为常规nativeProcess.progressEvent 我知道FFMPEG,但只使用AIR制作Android应用程序。所以对于这个AIR / FFMPEG连接,你比我更了解。

此答案假设您使用 FLV与MPEG H.264视频& MP3或AAC音频

ffmpeg -i input.mp4 -c:v copy -c:a mp3 -b:a 128k -ac 2 -ar 44100 FLV_with_MP3.flv

这个假设很重要,因为它影响我们寻找的字节类型。 对于具有H.264视频和AAC或MP3音频的上述FLV,我们可以预期以下(寻求时):

  • 由于这是MPEG,第一个视频标签将保存 AVC解码器配置字节,第一个音频标签保存音频特定配置字节。此数据不是实际的媒体帧,而是简单地打包为音频/视频标签。 MPEG播放需要这些。可以在MP4容器内的STSD元数据条目(MOOV atom)中找到相同的字节。现在,下一个找到的视频标签将(或应该)成为视频的实际第一帧。
  • 视频关键帧:开始 0x09 ,接下来的第11个字节 0x17 &第12个字节是 0x01
  • 音频标签AAC :开始 0x08 ,接下来的第11个字节 0xAF &第12个字节是 0x01
  • 音频标记MP3 :开始 0x08 ,接下来的第11个字节 0x2F &第12个字节是 0xFF

1)复制和检查值的字节:

您正在寻找代表视频"标记"的字节。除了元数据标签,您现在可以期待"标记"表示音频或视频帧的容器。有两种方法可以将标记字节放入"临时字节数组" (我们将其命名为temp_BA)。

  • ReadBytes(慢):在source_BA
  • 中的开始/结束范围内提取单个字节值
  • WriteBytes(快速):从source_BA
  • 即时复制字节的开始/结束范围

Readbytes解释告诉Source将其字节读入Target 。源将从其当前偏移(位置)向前读取长度。 在阅读之前转到正确的来源位置 ...

source_BA.readBytes( into Target_BA, Pos within Target_BA, length of bytes required );

执行上述行后,源位置现在已向前移动以考虑新行程长度。 (公式:Source new Pos = previousPos + BytesLengthRequired)。

Writebytes解释告诉Target从源复制字节范围。从已知信息(来自Source)复制后速度很快。 目标从当前位置向前写入 ...

target_BA.writeBytes( from source_BA, Pos within source_BA, length of bytes required );

执行上述行后,请注意,源和目标位置均未更改

使用上述方法从特定temp_BA获取source_BA.position = x所需的标记字节。

要检查任何字节(其值),请使用以下方法更新int类型的某些变量:

注意:不要混淆.readByte();,它会提取一个数字值(字节)和类似的声音.readBytes(),它将一块字节复制到另一个字节阵列。

2)查找视频关键帧(或I帧):

[带有关键帧H264 / AAC的视频标签的插图图片]

查找视频关键帧

  • 从起始偏移开始,使用while循环现在通过搜索每个字节的字节前进[#3}}" 9 &# 34; (hex:0x09),当发现我们进一步检查前面的字节以确认它确实是一个真正的关键帧而不仅仅是随机出现的" 9"。
  • 对于H.264视频编解码器,在正确的" 9 "字节位置(xPos)我们期望 11th&前面的第12个字节总是" 17 "和" 01 "分别。
  • If == true然后检查三个标记大小字节,并将 15 添加到此整数中,以获得预期的总字节长度要从Source写入Target(temp_BA)。我们已经添加了 15 来解释之前的11个字节以及预期的TAG DATA之后的4个字节。标签结尾处的这4个字节是"前一个标签大小"这个数量实际上包括11个前面的字节,但不计算这些结束4个字节本身。
  • 我们告诉temp_BA 写字节 来源(您的videoByteArray位置&#开始34;的 9 "字节(xPos)为长度"标签大小" + 15.您现在已经提取了一个MPEG关键帧。
    示例 temp_BA.writeBytes( videoByteArray, int (xPos), int (TAG_size) );
  • 现在可以使用以下方法附加带有关键帧标记的temp_BA
    示例 netStream.appendBytes( temp_BA ); //displays a single frame

注意:为了读取3个字节的标签大小,我将显示自定义转换bytes_toInt()功能(因为处理器一次读取1,2或4个字节的整数,读取3个字节这是一个akward请求)。

搜索提示:标记始终在跟踪中互相跟随。我们可以通过检查字节是否用于非关键帧(P帧)视频标签甚至某些音频标签来更快地寻求。如果是,那么我们检查特定的tag size,然后递增我们的xPos以跳过这个新的长度。这样我们就可以跳过整个标签大小而不仅仅是单个字节。仅在我们有关键帧标记时停止。

3)从视频关键帧播放:

当你考虑它时,游戏就像一个逐帧的自动搜索。获得每个下一帧的预期速度由视频的编码帧速率定义。

因此,播放功能可以简单地为Timer,每秒(或1000毫秒)获得X量的视频标签(帧)。你这样做是my_Timer = new Timer ( video_FPS )的例子。当计时器运行并到达每秒的每个FPS片时,它将运行append_PLAY();函数,该函数又运行get_frame_Tag();函数。

  • NS.seek(0):将NetStream置于"搜索模式"。 (这个数字并不重要,但必须存在于命令中)。任何"前面的框架"缓冲区被清除,它们将是(图像)帧更新,直到..
  • RESET_SEEK :结束"搜索模式"现在允许图像更新。使用RESET_SEEK命令后附加的第一个标记必须是带有视频关键帧的标记。 (对于仅音频,这可以是任何标签,因为从技术上讲,所有音频标签都是音频关键帧)
  • END_SEQUENCE :(对于MPEG H.264)播放任何剩余的"前面的帧" (排出缓冲区)。一旦耗尽,您现在可以附加任何类型的视频标签。记住H.264期望前移时间戳,如果你看到f ** ked up像素,那么你的下一个标签时间戳是错误的(太高或太低)。如果你只附加一帧(海报图片?),你可以使用END_SEQUEMCE来排空缓冲区并显示那一帧(无需等待缓冲区首先填充x量的帧)......

播放功能充当中间人功能来管理事物,而不会使用If语句等混淆 get frame 功能。管理事物意味着举例检查是否有足够的字节下载,甚至根据标签大小开始获取帧。

4)工作示例的源代码:

代码太长了..请参阅下面的one-byte valuelink

希望它有所帮助。 VC