奇怪的行为:使用.NET Stream.CopyTo(Stream destination,int bufferSize)复制更多bufferSize

时间:2013-07-22 20:07:03

标签: c# .net filestream

我有两个RIFF文件,我将块从一个文件复制到另一个文件。我正在使用的RIFF格式是:

  • 4字节ASCII标识符
  • 4字节整数帧块长度,以字节为单位
  • 数据(给定长度)

我使用以下代码从源文件复制到目标文件。块被称为帧。

public void CopySegmentTo(VPCaptureSession captureSession, int StartFrame, int EndFrame) {
        //captures several frames from this session onto the end of another one.
        //binary-styles

        Console.WriteLine(" captureSession.captureStream.Position: " + captureSession.captureStream.Position);
        long lastCaptureDestPosition = 0;

        captureSession.captureStream.Seek(0, SeekOrigin.End);
        int chunkLength; long timestamp; char[] charTag;

        for (int f = StartFrame; f <= EndFrame; f++)
        {
            Console.WriteLine("-----");

            this.captureStream.Seek(FrameTimes.ElementAt(f).Item3, SeekOrigin.Begin);

            Console.WriteLine("FrameIndex in this.captureStream: " + FrameTimes.ElementAt(f).Item3);

            BinaryReader reader = new BinaryReader(this.captureStream);
            byte[] tag = reader.ReadBytes(Tags.TAG_SIZE);
            charTag = TagChunk.encode.GetChars(tag);
            chunkLength = reader.ReadInt32();

            BinaryWriter writer = new BinaryWriter(captureSession.captureStream);
            writer.Write(tag);
            writer.Write(chunkLength);
            this.captureStream.CopyTo(captureSession.captureStream, chunkLength);

            long CaptureDestDelta = captureSession.captureStream.Position - lastCaptureDestPosition;

            Console.WriteLine("Loading " + f + " between " + StartFrame + "&" + EndFrame 
                + ". chunkLength: " + chunkLength
                + " this.captureStream.Position: " + this.captureStream.Position
                + " captureSession.captureStream.Position: " + captureSession.captureStream.Position
                + " CaptureDestDelta: " + CaptureDestDelta
                + " Delta Factor: " + CaptureDestDelta/chunkLength);

            lastCaptureDestPosition = captureSession.captureStream.Position;

        }

    }

复制速度很慢,结果文件似乎很大。以下是Console输出的示例:

captureSession.captureStream.Position: 0
-----
FrameIndex in this.captureStream: 0
Loading 0 between 0&5. chunkLength: 266736 this.captureStream.Position: 121225207 
captureSession.captureStream.Position: 121225207 CaptureDestDelta: 121225207 
Delta Factor: 454
-----
FrameIndex in this.captureStream: 266744
Loading 1 between 0&5. chunkLength: 311398 this.captureStream.Position: 121225207 
captureSession.captureStream.Position: 242183670 CaptureDestDelta: 120958463 
Delta Factor: 388
-----
FrameIndex in this.captureStream: 578150
Loading 2 between 0&5. chunkLength: 356578 this.captureStream.Position: 121225207 
captureSession.captureStream.Position: 362830727 CaptureDestDelta: 120647057 
Delta Factor: 338
-----
FrameIndex in this.captureStream: 934736
Loading 3 between 0&5. chunkLength: 430445 this.captureStream.Position: 121225207 
captureSession.captureStream.Position: 483121198 CaptureDestDelta: 120290471 
Delta Factor: 279
-----
FrameIndex in this.captureStream: 1365189
Loading 4 between 0&5. chunkLength: 437468 this.captureStream.Position: 121225207 
captureSession.captureStream.Position: 602981216 CaptureDestDelta: 119860018 
Delta Factor: 273
-----
FrameIndex in this.captureStream: 1802665
Loading 5 between 0&5. chunkLength: 439870 this.captureStream.Position: 121225207 
captureSession.captureStream.Position: 722403758 CaptureDestDelta: 119422542 
Delta Factor: 271

肯定会有一些奇怪的事情发生。这是我看到的主要内容:

  • captureSession.captureStream.Position每次都比chunkLength前进得更远。

  • this.captureStream.Position在每次复制后似乎总是相同。

如果还有更多我可以登录,请告诉我。我真的被困在这里了。

2 个答案:

答案 0 :(得分:2)

您有几个问题,主要问题是您正在调用的Stream.Copy方法复制整个流(从当前位置)。第二个参数是缓冲区大小,而不是要复制的字节数。

此外,您为循环的每次迭代创建一个新的BinaryReader和一个新的BinaryWriter。这两个类都分配了托管资源,应该在完成后处理。不幸的是,处理也会关闭底层流。但是留下这些对象只是在某些时候乞求资源匮乏问题,特别是如果你开始复制文件的大部分内容。

我建议您放弃BinaryReaderBinaryWriter并直接转到溪流。

您可以使用Stream.Read来读取数据,唯一的困难是读取int会要求您读取4个字节,然后调用BitConverter.ToInt32转换为整数。您也可以使用该方法从源复制数据(即读入大字节[]缓冲区),然后使用Stream.Write写入输出文件。

如果您使用的是.NET 4.5,则还有其他选择。在开始循环之前,您可以在输入上打开BinaryReader,在输出上打开BinaryWriter 。 .NET 4.5引入了一个新的构造函数,可以让您说明在处理读取器/写入器后是否要保持底层流的打开。在您的代码中,它看起来像这样:

using (var reader = new BinaryReader(this.CaptureStream, Encoding.Default, true))
{
    using (var writer = new BinaryWriter(captureSession.captureStream, Encoding.Default, true))
    {
        for (int f = StartFrame; f <= EndFrame; f++)
        {
        }
    }
}

您仍然可以对输入流进行搜索,读者也会做正确的事情。虽然您可能想写reader.BaseStream.Seek(...)

然后,您可以使用BinaryReader.ReadInt32阅读现有代码,BinaryReader.Read(byte[], int, int)BinaryWriter.Write(byte[], int32, int32)复制块数据。

(直到刚才我才意识到.NET 4.5最终解决了BinaryReaderBinaryWriter关闭流的问题。)

答案 1 :(得分:0)

我误解了Stream.CopyTo(Stream destination, int bufferSize)中bufferSize的含义。我认为这是要复制的总字节数,而不仅仅是复制时要使用的缓冲区。 Stream对象中没有任何功能只能复制特定数量的字节。我通过替换

来解决问题
this.captureStream.CopyTo(captureSession.captureStream, chunkLength);

writer.Write(reader.ReadBytes(chunkLength));