AS3 NetStream AppendBytes Seek 问题 [英] AS3 NetStream AppendBytes Seek issue

查看:37
本文介绍了AS3 NetStream AppendBytes Seek 问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 AS3 中遇到 NetStream 问题.我正在从事的项目允许用户浏览视频(本地)并播放.我遇到的问题是 netStream.seek(0); 我可以告诉它什么都不做,尽管我进入了 NetStatusEvent 函数和 NetStream.Seek.Notify 被触发.我正在使用 NativeProcess 并且以下功能有什么不同.

I'm having trouble with NetStream in AS3. The project I am working on allows users to browse a video (locally) and play it back. The issue I am having is that netStream.seek(0); from what I can tell it doesn't do anything, although I get inside a NetStatusEvent function and NetStream.Seek.Notify is triggered. I'm using NativeProcess and the following function is this makes any difference.

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

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

Am I missing something here? I am pausing netStream before using netStream.seek(0);.

为了解决这个问题,我按照 VC.One 的说明进行了操作:

In an attempt to fix this issue I followed the instructions by VC.One I've done the following:

  • videoByteArray = new ByteArray(); 移动到我的 init 函数中,并在此函数中创建了 tempVideoByteArray = new ByteArray();.

  • Moved videoByteArray = new ByteArray(); to my init function and also created tempVideoByteArray = new ByteArray(); in this function.

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

Update my ProgressEventOutputHandler function so that it no longer created a new ByteArray for videoByteArray and changed this line - nativeProcess.standardOutput.readBytes(videoByteArray, videoByteArray.length, nativeProcess.standardOutput.bytesAvailable);

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

I have changed nothing else and now the video will not load. If I allow a new ByteArray to be created inside the ProgressEventOutputHandler function the video does load again.

推荐答案

简短版本:

试试我在这里粘贴的代码:Github Snippet 链接

Try the code I pasted here: Github Snippet link

长版:

这个有点长,但希望它能一劳永逸地帮助...不要担心砖墙的事情,墙是用来砸的.为了保持灵感,请查看 VC:One 实验室使用 appendBytes 的一些内部演示:

  • MP4 搜索实验:研究appendBytes 帧数据访问和时间/搜索处理.仅使用 AS3 代码将实时帧字节从 MP4 转换为 FLV 格式.
  • 音频和速度调整视频 : 用于视频分离中的实时 MP3 音频 &效果实验.需要在音轨中包含 MP3 数据的 MP4/FLV 文件.
  • 同步视频帧:用于多个视频以相同的帧数显示.
  • MP4 Seeking Experiment : research for appendBytes frame data access and time/seek handling. Real-time frame bytes convert from MP4 to FLV format using only AS3 code.
  • Speed Adjust of Audio & Video : for real-time MP3 audio in video separation & effecting experiment. Requires MP4/FLV file with MP3 data in the audio track.
  • Synchronised Video Frames : for multiple videos displaying by the same frame number.

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

PS: I'll be using URLStream method as that's a more useful answer to those loading local or online files. You could change from urlstream.progressEvent to your usual nativeProcess.progressEvent.
I know FFMPEG but only used AIR for making Android apps. So for this AIR/FFMPEG connection you know more than me.

此外,此答案还假设您使用的是 FLV 和 MPEG H.264 视频 &MP3 或 AAC 音频.

Also this answer assumes you're using FLV with MPEG H.264 video & MP3 or AAC audio.

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 的情况下,我们可以期待以下内容(在查找时):

This assumption matters because it affects what kind of bytes we look for. In the case of the above FLV with a H.264 video and AAC or MP3 audio we can expect the following (when seeking) :

  • 由于这是 MPEG,因此第一个视频标签将包含 AVC 解码器配置 字节,而第一个音频标签将包含 音频特定配置 字节.该数据不是实际的媒体帧,而是像音频/视频标签一样简单地打包.这些是 MPEG 播放所必需的.在 MP4 容器内的 STSD 元数据条目 (MOOV atom) 中可以找到相同的字节.现在下一个找到的视频标签将(或应该)是视频的实际第一帧.
  • 视频关键帧:从 0x09 开始,下一个第 11 个字节是 0x17 &第 12 个字节是 0x01
  • Audio TAG AAC:从 0x08 开始,接下来的第 11 个字节是 0xAF &第 12 个字节是 0x01
  • Audio TAG MP3:从 0x08 开始,下一个第 11 个字节是 0x2F &第 12 个字节是 0xFF
  • Since this is MPEG the first video tag will hold AVC Decoder Config bytes and the first audio tag holds the Audio Specific Config bytes. This data is not actual media frames but simply packaged like an audio/video tag. These are needed for MPEG playback. The same bytes can be found in the STSD metadata entry (MOOV atom) inside an MP4 container. Now the next found video tag will (or should) be the video's actual first frame.
  • Video keyframe : begins 0x09 and next 11th byte is 0x17 & 12th byte is 0x01
  • Audio TAG AAC : begins 0x08 and next 11th byte is 0xAF & 12th byte is 0x01
  • Audio TAG MP3 : begins 0x08 and next 11th byte is 0x2F & 12th byte is 0xFF

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

You are looking for bytes that represent a video "tag". Apart from the Metadata tag, you can now expect "tag" to mean a container of an audio or video frame. There are two ways to get tag bytes into your "temporary byte array" (we'll name it as temp_BA ).

  • ReadBytes (slow) : 在 source_BA
  • 中提取起始/结束范围内的单个字节值
  • WriteBytes(快速):从 source_BA
  • 开始/结束字节范围的即时复制
  • ReadBytes (slow) : extracts the individual byte values within a start/end range in source_BA
  • WriteBytes (fast) : instant duplication of a start/end range of bytes from source_BA

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

source_BA.readBytes(到Target_BA,在Target_BA内的Pos,需要的字节长度);

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

Readbytes explained : tells Source to read its bytes into Target. Source will read forwards up to the length from its current offset (position). Go to correct Source position before reading onwards...

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

After the above line executes, Source position will now have moved forward to account for the new length travelled. (formula : Source new Pos = previousPos + BytesLengthRequired).

Writebytes 解释:告诉 Target 复制来自 Source 的一系列字节.由于从已知信息(来自 Source)复制,因此速度很快.目标从当前位置开始写入...

target_BA.writeBytes(来自source_BA,source_BA内的Pos,需要的字节长度);

执行上述代码后,请注意 Source 和 Target 位置都没有变化.

Writebytes explained : tells Target to duplicate a range of bytes from Source. Is fast since copying from already-known information (from Source). Target writes onwards from its current position...

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

After the above line executes, note that both Source and Target positions are unchanged.

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

Use above methods to get required tag bytes into temp_BA from a specific source_BA.position = x.

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

To check any byte (its value), use the methods below to update some variable of int type:

  • 阅读one-字节值:使用my_Integer = source_BA.readByte();
  • 阅读两个-字节值:使用my_Integer = source_BA.readUnsignedShort();
  • 阅读四字节值:使用my_Integer = source_BA.readUnsignedInt();
  • 变量 Number 用于 八字节值:使用 my_Number = source_BA.readDouble();
  • Read a one-byte value : use my_Integer = source_BA.readByte();
  • Read a two-byte value : use my_Integer = source_BA.readUnsignedShort();
  • Read a four-byte value : use my_Integer = source_BA.readUnsignedInt();
  • variable Number for eight-byte value : use my_Number = source_BA.readDouble();

注意:不要将提取数值(字节)的 .readByte(); 与类似的 .readBytes() 混淆code> 将一大块字节复制到另一个字节数组.

note : Don't confuse .readByte(); which extracts a numerical value (of byte) with the similar sounding .readBytes() which copies a chunk of bytes to another byte array.

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

[ illustration image of Video TAG with Keyframe H264/AAC ]

查找视频关键帧

  • 从起始偏移量开始,使用 while 循环现在 [forward] 遍历字节,在每个字节中搜索 9"的一字节值(十六进制:0x09),当发现时,我们进一步检查前面的字节以确认它确实是一个真正的关键帧,而不仅仅是9"的随机出现.
  • 对于 H.264 视频编解码器,在正确的9"字节位置 (xPos) 处,我们预计 11th &前面的第 12 个字节总是17"和01".
  • If== true 然后我们检查三个 Tag Size 字节并将 15 添加到此期望从 Source 写入 Target 的字节总长度的整数 (temp_BA).我们添加了 15 来说明 之前 的 11 个字节以及之后 4 个字节的预期标记数据.标签结尾的这 4 个字节是前一个标签大小",这个数量实际上包括 11 个前面的字节,但不计算这些结束 4 个字节本身.
  • 我们告诉temp_BA写入的字节(您的videoByteArray)开始strong>9"字节(xPos)的位置,长度标签大小"+ 15.您现在已经提取了一个 MPEG 关键帧.
    示例:temp_BA.writeBytes( videoByteArray, int (xPos), int (TAG_size) );
  • 这个带有关键帧标签的 temp_BA 现在可以使用:
    示例 : netStream.appendBytes( temp_BA );//显示单帧
  • From a starting offset, use a while loop to now travel [forward] through the bytes searching each byte for a one-byte value of "9" ( hex: 0x09), when found we check further ahead bytes to confirm that indeed it's a true keyframe and not just a random occurence of "9".
  • In the case of H.264 video codec, at the correct "9" byte position (xPos) we expect the 11th & 12th bytes ahead always to be "17" and "01" respectively.
  • If that is == true then we check the three Tag Size bytes and add 15 to this integer for the total length of bytes expected to be written from Source into Target ( temp_BA). We have added 15 to account for the 11 bytes before and also the 4 bytes after expected TAG DATA. These 4 bytes at tag ending are "Previous Tag Size" and this amount actually includes the 11 front bytes but not counting these end 4 bytes themselves.
  • We tell temp_BA to write bytes of Source (your videoByteArray) starting from pos of "9" byte (xPos) for a length of "Tag Size" + 15. You have now extracted an MPEG keyframe.
    example : temp_BA.writeBytes( videoByteArray, int (xPos), int (TAG_size) );
  • This temp_BA with tag of a Keyframe can now be appended using:
    example : netStream.appendBytes( temp_BA ); //displays a single frame

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

note : For reading 3 bytes of Tag Size I will show a custom converting bytes_toInt() function (since processors read either 1, 2 or 4 bytes at once for integers, reading 3 bytes here is an akward request).

搜索提示:标签总是在一个轨迹中相互跟随.我们还可以通过检查字节是否用于非关键帧(P 帧)视频标签甚至某些音频标签来更快地寻找.如果是,那么我们检查那个特定的 tag size 并且现在增加我们的 xPos 以跳过这个新的长度.这样我们可以跳过整个标签大小而不仅仅是单个字节.仅当我们有关键帧标签时才停止.

Searching tip : Tags always follow each other in a trail. We can seek faster by also checking if bytes are for a non-keyframe (P frame) video tag or even some audio tag. If so then we check that particular tag size and now increment our xPos to jump this new length. This way we can skip by whole tag sizes not just by single bytes. Stopping only when we have a keyframe tag.

仔细想想,播放就像是逐帧进行的自动搜索.其中获取每一帧的预期速度由视频的编码帧率定义.

When you think about it, play is simply like an auto-seek going on a frame by frame basis. Where the expected speed of getting each next frame is defined by the video's encoded framerate.

因此,您的播放功能可以简单地成为一个 Timer,每秒(或 1000 毫秒)获取 X 数量的视频标签(帧).您可以将其作为示例 my_Timer = new Timer ( video_FPS ).当计时器运行并达到每秒的每个 FPS 切片时,它将运行 append_PLAY(); 函数,该函数依次运行 get_frame_Tag(); 函数.

So your playback function can simply be a Timer that gets X-amount of video tags (frames) every second (or 1000 milisecs). You do that as example my_Timer = new Timer ( video_FPS ). When the timer runs and reaches each FPS slice of a second it will run the append_PLAY(); function which in turn runs a get_frame_Tag(); function.

  • NS.seek(0) :将 NetStream 置于搜索模式".(数字无关紧要,但必须存在于命令中).任何超前帧"缓冲区都被清除,并且它们将不会更新(图像)帧,直到......
  • RESET_SEEK :结束搜索模式",现在允许图像更新.使用 RESET_SEEK 命令后附加的第一个标签必须是带有视频关键帧的标签.(对于纯音频,这可以是任何标签,因为从技术上讲,所有音频标签都是音频关键帧)
  • END_SEQUENCE :(对于 MPEG H.264)播放任何剩余的前帧"(耗尽缓冲区).一旦耗尽,您现在可以附加任何类型的视频标签.记住 H.264 需要向前移动的时间戳,如果你看到 f**ked up 像素,那么你的下一个标签时间戳是错误的(太高或太低).如果您只附加一帧(海报图像?),您可以使用 END_SEQUEMCE 排空缓冲区并显示该帧(无需等待缓冲区先填充 x 帧数)...
  • NS.seek(0) : Puts NetStream into "seek mode". (the number doesn't matter but must exist in the command). Any "ahead frames" buffer is cleared and they'll be no (image) frame updates until..
  • RESET_SEEK : Ends the "seek mode" and now allows image updates. The first tag you append after using the RESET_SEEK command must be a tag with a video keyframe. (for audio-only this can be any tag since technically all audio tags are audio keyframes)
  • END_SEQUENCE : (for MPEG H.264) Plays out any remaining "ahead frames" (drains the buffer). Once drained you can now append any type of video tag. Remember H.264 expects forward-moving timestamps, If you see f**ked up pixels then your next tag timestamp is wrong (too high or too low). If you appending just one frame (poster image?) you could use END_SEQUEMCE to drain the buffer and display that one frame (without waiting for buffer to fill up to x-amount of frames first)...

play 函数 作为一个中间人函数来管理事物,而不会用 If 语句等使 get frame 函数混乱.意味着例如检查是否有足够的字节下载,甚至可以根据标签大小开始获取帧.

The play function acts as a middle-man function to manage things without cluttering the get frame function with If statements etc. Managing things means for example checking that there are enough bytes downloaded to even begin getting a frame according to Tag Size.

代码太长.. 请参阅下面的链接:https://gist.github.com/Valerio-Charles-VC1/657054b773dba9ba1cbc

Code is too long.. see this link below: https://gist.github.com/Valerio-Charles-VC1/657054b773dba9ba1cbc

希望有帮助.风险投资

这篇关于AS3 NetStream AppendBytes Seek 问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆