Exemplo n.º 1
0
        internal void SearchFramesByMagicPhase2(OnSearchProgressDelegate progressCallback)
        {
            m_Index = new AdvFramesIndex();

            int  percentCompleted;
            int  lastPercentCompleted = -1;
            int  framesRecovered      = 0;
            long startTimeTicks       = -1;

            m_CandidateFrameOffsets.Add(new RecoveredFrameHeader()
            {
                Position = m_FileReader.BaseStream.Length
            });

            ushort[,] prevFramePixels = null;

            for (int i = 0; i < m_CandidateFrameOffsets.Count - 1; i++)
            {
                RecoveredFrameHeader frameInfo1 = m_CandidateFrameOffsets[i];
                RecoveredFrameHeader frameInfo2 = m_CandidateFrameOffsets[i + 1];

                if (startTimeTicks == -1)
                {
                    startTimeTicks = frameInfo1.StartExposureUT.Ticks;
                }

                m_Index.Index.Add(new AdvFramesIndexEntry()
                {
                    ElapsedTime = new TimeSpan(frameInfo1.StartExposureUT.Ticks - startTimeTicks),
                    Offset      = frameInfo1.Position,
                    Length      = (uint)(frameInfo2.Position - frameInfo1.Position)
                });

                try
                {
                    object[]      data        = GetFrameSectionData(framesRecovered, prevFramePixels);
                    AdvImageData  imageData   = (AdvImageData)data[0];
                    AdvStatusData statusData  = (AdvStatusData)data[1];
                    int           frameWidth  = imageData.ImageData.GetLength(0);
                    int           frameHeight = imageData.ImageData.GetLength(1);
                    Trace.WriteLine(string.Format("Recovered frame #{0} with LayoutId {1} and {2} status tags. Dimentions: {3}x{4} pixels", framesRecovered, imageData.LayoutId, statusData.TagValues.Count, frameWidth, frameHeight));

                    // NOTE: Doesn't have to be right, just need something so we read the pixels
                    prevFramePixels = imageData.ImageData;

                    framesRecovered++;
                }
                catch (Exception)
                {
                    m_Index.Index.RemoveAt(m_Index.Index.Count - 1);
                }

                percentCompleted = (int)(100.0 * i / (m_CandidateFrameOffsets.Count - 1));
                if (lastPercentCompleted < percentCompleted)
                {
                    progressCallback(percentCompleted, framesRecovered);
                    lastPercentCompleted = percentCompleted;
                }
            }
        }
Exemplo n.º 2
0
        internal void SearchFramesByMagicPhase2(OnSearchProgressDelegate progressCallback)
        {
            m_Index = new AdvFramesIndex();

            int percentCompleted;
            int lastPercentCompleted = -1;
            int framesRecovered = 0;
            long startTimeTicks = -1;

            m_CandidateFrameOffsets.Add(new RecoveredFrameHeader()
            {
                Position = m_FileReader.BaseStream.Length
            });

            ushort[,] prevFramePixels = null;

            for(int i = 0; i < m_CandidateFrameOffsets.Count - 1; i ++)
            {
                RecoveredFrameHeader frameInfo1 = m_CandidateFrameOffsets[i];
                RecoveredFrameHeader frameInfo2 = m_CandidateFrameOffsets[i + 1];

                if (startTimeTicks == -1)
                    startTimeTicks = frameInfo1.StartExposureUT.Ticks;

                m_Index.Index.Add(new AdvFramesIndexEntry()
                {
                    ElapsedTime = new TimeSpan(frameInfo1.StartExposureUT.Ticks - startTimeTicks),
                    Offset = frameInfo1.Position,
                    Length = (uint)(frameInfo2.Position - frameInfo1.Position)
                });

                try
                {
                    object[] data = GetFrameSectionData(framesRecovered, prevFramePixels);
                    AdvImageData imageData = (AdvImageData)data[0];
                    AdvStatusData statusData = (AdvStatusData)data[1];
                    int frameWidth = imageData.ImageData.GetLength(0);
                    int frameHeight = imageData.ImageData.GetLength(1);
                    Trace.WriteLine(string.Format("Recovered frame #{0} with LayoutId {1} and {2} status tags. Dimentions: {3}x{4} pixels", framesRecovered, imageData.LayoutId, statusData.TagValues.Count, frameWidth, frameHeight));

                    // NOTE: Doesn't have to be right, just need something so we read the pixels
                    prevFramePixels = imageData.ImageData;

                    framesRecovered++;
                }
                catch(Exception)
                {
                    m_Index.Index.RemoveAt(m_Index.Index.Count - 1);
                }

                percentCompleted = (int)(100.0 * i / (m_CandidateFrameOffsets.Count - 1));
                if (lastPercentCompleted < percentCompleted)
                {
                    progressCallback(percentCompleted, framesRecovered);
                    lastPercentCompleted = percentCompleted;
                }
            }
        }
Exemplo n.º 3
0
        internal void SearchFramesByMagicPhase1(OnSearchProgressDelegate progressCallback)
        {
            long bytesToSearch = m_InputFile.Length - 5 - m_RecoveryOffset;

            m_CandidateFrameOffsets.Clear();

            int percentCompleted;
            int lastPercentCompleted = -1;

            int MIN_FRAME_SIZE = 12 + 4;

            for (long i = 0; i < bytesToSearch; i++)
            {
                m_InputFile.Seek(m_RecoveryOffset + i, SeekOrigin.Begin);

                uint frameDataMagic = m_FileReader.ReadUInt32();
                if(frameDataMagic == 0xEE0122FF)
                {
                    try
                    {
                        byte[] data = m_FileReader.ReadBytes(MIN_FRAME_SIZE);

                        // Read the timestamp and exposure
                        long frameTimeMsSince2010 =
                             (long)data[0] + (((long)data[1]) << 8) + (((long)data[2]) << 16) + (((long)data[3]) << 24) +
                            (((long)data[4]) << 32) + (((long)data[5]) << 40) + (((long)data[6]) << 48) + (((long)data[7]) << 56);
                        int exposure = data[8] + (data[9] << 8) + (data[10] << 16) + (data[11] << 24);

                        DateTime startExposure = ADV_ZERO_DATE_REF.AddMilliseconds(frameTimeMsSince2010);
                        double exposureMS = exposure / 10.0;

                        if (startExposure.Year < 2010 || startExposure.Year > 2100)
                            continue;
                        if (exposureMS < 0 || exposureMS > 60 * 1000)
                            continue;

                        m_CandidateFrameOffsets.Add(new RecoveredFrameHeader()
                        {
                            Position = m_RecoveryOffset + i,
                            StartExposureUT = startExposure,
                            ExposureMilliseconds = exposureMS
                        });

                        int sectionDataLength = data[12] + (data[13] << 8) + (data[14] << 16) + (data[15] << 24);

                        m_InputFile.Seek(m_RecoveryOffset + i + 4 + 12 + sectionDataLength + 4, SeekOrigin.Begin);

                        byte tagValuesCount = m_FileReader.ReadByte();

                        for (int j = 0; j < tagValuesCount; j++)
                        {
                            m_FileReader.ReadByte();
                            byte len = m_FileReader.ReadByte();
                            m_FileReader.ReadBytes(len);
                        }

                        // NOTE: The next frame should begin somewhere in the next 32 bytes
                        long nextFrameSearchStartPos = m_FileReader.BaseStream.Position;

                        for (int j = 0; j < 32; j++)
                        {
                            m_InputFile.Seek(nextFrameSearchStartPos + j, SeekOrigin.Begin);

                            uint frameDataMagic2 = m_FileReader.ReadUInt32();
                            if (frameDataMagic2 == 0xEE0122FF)
                            {
                                // We found it!
                                i = nextFrameSearchStartPos + j - m_RecoveryOffset - 1;
                                break;
                            }
                        }
                    }
                    catch { }
                }

                percentCompleted = (int)(100.0 * i / bytesToSearch);
                if (lastPercentCompleted < percentCompleted)
                {
                    progressCallback(percentCompleted, m_CandidateFrameOffsets.Count);
                    lastPercentCompleted = percentCompleted;
                }
            }
        }
Exemplo n.º 4
0
        internal bool SaveAsAviFile(string fileName, int firstFrame, int lastFrame, AdvToAviConverter converter, bool tryCodec, double msPerFrame, double addedGamma, OnSearchProgressDelegate progressCallback)
        {
            IAviSaver saver = AdvToAviConverterFactory.CreateConverter(converter);

            saver.CloseAviFile();
            if (!saver.StartNewAviFile(fileName, (int) ImageSection.Width, (int) ImageSection.Height, 8, 25, tryCodec))
            {
                progressCallback(100, 0);
                return false;
            }
            try
            {
                int aviFrameNo = 0;
                AdvFramesIndexEntry firstFrameIdx = m_Index.Index[firstFrame];
                double startingTimeMilliseconds = firstFrameIdx.ElapsedTime.TotalMilliseconds;
                bool isAavFile = AdvFileTags["FSTF-TYPE"] == "AAV";

                double effFrameDuration = double.NaN;
                try
                {
                    effFrameDuration = 1000 / double.Parse(AdvFileTags["EFFECTIVE-FRAME-RATE"]);
                }
                catch
                { }

                if (isAavFile && m_Index.Index[lastFrame].ElapsedTime.Ticks == 0 && (double.IsNaN(effFrameDuration) || double.IsInfinity(effFrameDuration)))
                {
                    MessageBox.Show(
                        "This AAV video format is too old or the AAV file is corrupted. It cannot be converted to AVI",
                        "Tangra 3",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);

                    return false;
                }

                if ((isAavFile && !double.IsNaN(effFrameDuration)) ||
                    !isAavFile && m_Index.Index[lastFrame].ElapsedTime.Ticks != 0)
                {
                    // Sampling can be done as we have sufficient timing information
                }
                else
                {
                    MessageBox.Show(
                        "There is insufficient timing information in this file to convert it to AVI. This could be caused by an old file format.",
                        "Tangra 3",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);

                    return false;
                }

                progressCallback(5, 0);

                for (int i = firstFrame; i <= lastFrame; i++)
                {
                    AdvFramesIndexEntry frame = m_Index.Index[i];

                    AdvImageData currentImageData;
                    AdvStatusData currentStatusData;
                    Bitmap currentBitmap;

                    using (Pixelmap pixmap = GetFrameData(i, out currentImageData, out currentStatusData, out currentBitmap))
                    {
                        int lastRepeatedAviFrameNo = 0;

                        if (isAavFile && !double.IsNaN(effFrameDuration))
                        {
                            lastRepeatedAviFrameNo = (int)Math.Round(((i - firstFrame) * effFrameDuration) / msPerFrame);
                        }
                        else if (!isAavFile && frame.ElapsedTime.Ticks != 0)
                            lastRepeatedAviFrameNo = (int)Math.Round((frame.ElapsedTime.TotalMilliseconds - startingTimeMilliseconds) / msPerFrame);

                        while (aviFrameNo < lastRepeatedAviFrameNo)
                        {
                            if (!saver.AddAviVideoFrame(pixmap, addedGamma, ImageSection.Adv16NormalisationValue))
                            {
                                progressCallback(100, 0);
                                return false;
                            }
                            aviFrameNo++;
                        }

                        if (currentBitmap != null)
                            currentBitmap.Dispose();
                    }

                    int percDone = (int)Math.Min(90, 90 * (i - firstFrame) * 1.0 / (lastFrame - firstFrame + 1));
                    progressCallback(5 + percDone, 0);
                }
            }
            finally
            {
                saver.CloseAviFile();
                progressCallback(100, 0);
            }

            return false;
        }
Exemplo n.º 5
0
        internal void ExportStatusSectionToCSV(string fileName, int firstFrame, int lastFrame, OnSearchProgressDelegate progressCallback)
        {
            progressCallback(5, 0);

            //string folder = Path.GetDirectoryName(fileName);

            bool headerAppended = false;

            using (FileStream fsOutput = new FileStream(fileName, FileMode.CreateNew, FileAccess.Write))
            using(TextWriter writer = new StreamWriter(fsOutput))
            {
                for (int i = firstFrame; i <= lastFrame; i++)
                {
                    object[] data = GetFrameSectionData(i, null);
                    AdvImageData imageData = (AdvImageData)data[0];
                    AdvStatusData statusData = (AdvStatusData)data[1];

                    //using(FileStream fs = new FileStream(string.Format("{0}\\{1}.pix", folder, i), FileMode.CreateNew, FileAccess.Write))
                    //using (BinaryWriter wrt = new BinaryWriter(fs))
                    //{
                    //	for (int y = 0; y < ImageSection.Height; y++)
                    //	{
                    //		for (int x = 0; x < ImageSection.Width; x++)
                    //		{
                    //			ushort val = imageData.ImageData[x, y];
                    //			wrt.Write(val);
                    //		}
                    //	}
                    //}

                    string headerRow;
                    string nextRow = StatusDataToCsvRow(imageData, statusData, i, out headerRow);
                    if (!headerAppended)
                    {
                        writer.WriteLine(headerRow);
                        headerAppended = true;
                    }
                    writer.WriteLine(nextRow);

                    int percDone = (int)Math.Min(90, 90 * (i - firstFrame) * 1.0 / (lastFrame - firstFrame + 1));
                    progressCallback(5 + percDone, 0);
                }

                progressCallback(95, 0);

                writer.Flush();
            }
        }
Exemplo n.º 6
0
        internal bool CropAdvFile(string fileName, int firstFrame, int lastFrame, OnSearchProgressDelegate progressCallback)
        {
            using (var fsr = new FileStream(m_FileName, FileMode.Open, FileAccess.Read))
            using (var reader = new BinaryReader(fsr))
            using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
            using (var writer = new BinaryWriter(fs))
            {
                AdvFramesIndexEntry firstEntry = m_Index.Index[0];

                CopyRawBytes(reader, 0, firstEntry.Offset, writer);

                progressCallback(5, 0);

                AdvFramesIndexEntry firstEntryToCopy = m_Index.Index[firstFrame];
                long zeroTicks = firstEntryToCopy.ElapsedTime.Ticks;

                var newIndex = new List<AdvFramesIndexEntry>();

                for (int i = firstFrame; i <= lastFrame; i++)
                {
                    AdvFramesIndexEntry frameToCopy = m_Index.Index[i];

                    var copiedFrame = new AdvFramesIndexEntry()
                    {
                        Offset = writer.BaseStream.Position,
                        Length = frameToCopy.Length
                    };

                    long nextFrameStartPos = i == m_Index.NumberOfFrames - 1 ? m_Index.TableOffset : m_Index.Index[i + 1].Offset;

                    CopyRawBytes(reader, frameToCopy.Offset, nextFrameStartPos - frameToCopy.Offset, writer);

                    copiedFrame.ElapsedTime = new TimeSpan(frameToCopy.ElapsedTime.Ticks - zeroTicks);
                    newIndex.Add(copiedFrame);

                    int percDone = (int)Math.Min(90, 90 * (i - firstFrame) * 1.0 / (lastFrame - firstFrame + 1));
                    progressCallback(5 + percDone, 0);
                }

                long indexTableOffset = writer.BaseStream.Position;

                progressCallback(95, 0);

                writer.Write((uint)newIndex.Count);

                // Save the new INDEX
                foreach (AdvFramesIndexEntry newIndexEntry in newIndex)
                {
                    writer.Write((uint)Math.Round(newIndexEntry.ElapsedTime.TotalMilliseconds));
                    writer.Write((long)newIndexEntry.Offset);
                    writer.Write((uint)newIndexEntry.Length);
                }

                long userMetadataTablePosition = writer.BaseStream.Position;

                if (fsr.Length > m_UserMetadataTableOffset)
                {
                    CopyRawBytes(reader, m_UserMetadataTableOffset, fsr.Length - m_UserMetadataTableOffset, writer);
                }

                writer.BaseStream.Seek(5, SeekOrigin.Begin);

                writer.Write((uint)newIndex.Count);
                writer.Write(indexTableOffset);

                writer.BaseStream.Seek(8, SeekOrigin.Current);

                writer.Write(userMetadataTablePosition);

                writer.BaseStream.Flush();

                progressCallback(100, 0);
            }

            return false;
        }
Exemplo n.º 7
0
        internal void ExportStatusSectionToCSV(string fileName, int firstFrame, int lastFrame, OnSearchProgressDelegate progressCallback)
        {
            progressCallback(5, 0);

            //string folder = Path.GetDirectoryName(fileName);

            bool headerAppended = false;

            using (FileStream fsOutput = new FileStream(fileName, FileMode.CreateNew, FileAccess.Write))
                using (TextWriter writer = new StreamWriter(fsOutput))
                {
                    for (int i = firstFrame; i <= lastFrame; i++)
                    {
                        object[]      data       = GetFrameSectionData(i, null);
                        AdvImageData  imageData  = (AdvImageData)data[0];
                        AdvStatusData statusData = (AdvStatusData)data[1];

                        //using(FileStream fs = new FileStream(string.Format("{0}\\{1}.pix", folder, i), FileMode.CreateNew, FileAccess.Write))
                        //using (BinaryWriter wrt = new BinaryWriter(fs))
                        //{
                        //	for (int y = 0; y < ImageSection.Height; y++)
                        //	{
                        //		for (int x = 0; x < ImageSection.Width; x++)
                        //		{
                        //			ushort val = imageData.ImageData[x, y];
                        //			wrt.Write(val);
                        //		}
                        //	}
                        //}

                        string headerRow;
                        string nextRow = StatusDataToCsvRow(imageData, statusData, i, out headerRow);
                        if (!headerAppended)
                        {
                            writer.WriteLine(headerRow);
                            headerAppended = true;
                        }
                        writer.WriteLine(nextRow);

                        int percDone = (int)Math.Min(90, 90 * (i - firstFrame) * 1.0 / (lastFrame - firstFrame + 1));
                        progressCallback(5 + percDone, 0);
                    }

                    progressCallback(95, 0);

                    writer.Flush();
                }
        }
Exemplo n.º 8
0
        internal bool CropAdvFile(string fileName, int firstFrame, int lastFrame, OnSearchProgressDelegate progressCallback)
        {
            using (var fsr = new FileStream(m_FileName, FileMode.Open, FileAccess.Read))
                using (var reader = new BinaryReader(fsr))
                    using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
                        using (var writer = new BinaryWriter(fs))
                        {
                            AdvFramesIndexEntry firstEntry = m_Index.Index[0];

                            CopyRawBytes(reader, 0, firstEntry.Offset, writer);

                            progressCallback(5, 0);

                            AdvFramesIndexEntry firstEntryToCopy = m_Index.Index[firstFrame];
                            long zeroTicks = firstEntryToCopy.ElapsedTime.Ticks;

                            var newIndex = new List <AdvFramesIndexEntry>();

                            for (int i = firstFrame; i <= lastFrame; i++)
                            {
                                AdvFramesIndexEntry frameToCopy = m_Index.Index[i];

                                var copiedFrame = new AdvFramesIndexEntry()
                                {
                                    Offset = writer.BaseStream.Position,
                                    Length = frameToCopy.Length
                                };

                                long nextFrameStartPos = i == m_Index.NumberOfFrames - 1 ? m_Index.TableOffset : m_Index.Index[i + 1].Offset;

                                CopyRawBytes(reader, frameToCopy.Offset, nextFrameStartPos - frameToCopy.Offset, writer);

                                copiedFrame.ElapsedTime = new TimeSpan(frameToCopy.ElapsedTime.Ticks - zeroTicks);
                                newIndex.Add(copiedFrame);

                                int percDone = (int)Math.Min(90, 90 * (i - firstFrame) * 1.0 / (lastFrame - firstFrame + 1));
                                progressCallback(5 + percDone, 0);
                            }

                            long indexTableOffset = writer.BaseStream.Position;

                            progressCallback(95, 0);

                            writer.Write((uint)newIndex.Count);

                            // Save the new INDEX
                            foreach (AdvFramesIndexEntry newIndexEntry in newIndex)
                            {
                                writer.Write((uint)Math.Round(newIndexEntry.ElapsedTime.TotalMilliseconds));
                                writer.Write((long)newIndexEntry.Offset);
                                writer.Write((uint)newIndexEntry.Length);
                            }

                            long userMetadataTablePosition = writer.BaseStream.Position;

                            if (fsr.Length > m_UserMetadataTableOffset)
                            {
                                CopyRawBytes(reader, m_UserMetadataTableOffset, fsr.Length - m_UserMetadataTableOffset, writer);
                            }

                            writer.BaseStream.Seek(5, SeekOrigin.Begin);

                            writer.Write((uint)newIndex.Count);
                            writer.Write(indexTableOffset);

                            writer.BaseStream.Seek(8, SeekOrigin.Current);

                            writer.Write(userMetadataTablePosition);

                            writer.BaseStream.Flush();

                            progressCallback(100, 0);
                        }

            return(false);
        }
Exemplo n.º 9
0
        internal bool SaveAsAviFile(string fileName, int firstFrame, int lastFrame, AdvToAviConverter converter, bool tryCodec, double msPerFrame, double addedGamma, OnSearchProgressDelegate progressCallback)
        {
            IAviSaver saver = AdvToAviConverterFactory.CreateConverter(converter);

            saver.CloseAviFile();
            if (!saver.StartNewAviFile(fileName, (int)ImageSection.Width, (int)ImageSection.Height, 8, 25, tryCodec))
            {
                progressCallback(100, 0);
                return(false);
            }
            try
            {
                int aviFrameNo = 0;
                AdvFramesIndexEntry firstFrameIdx = m_Index.Index[firstFrame];
                double startingTimeMilliseconds   = firstFrameIdx.ElapsedTime.TotalMilliseconds;
                bool   isAavFile = AdvFileTags["FSTF-TYPE"] == "AAV";

                double effFrameDuration = double.NaN;
                try
                {
                    effFrameDuration = 1000 / double.Parse(AdvFileTags["EFFECTIVE-FRAME-RATE"]);
                }
                catch
                { }

                if (isAavFile && m_Index.Index[lastFrame].ElapsedTime.Ticks == 0 && (double.IsNaN(effFrameDuration) || double.IsInfinity(effFrameDuration)))
                {
                    MessageBox.Show(
                        "This AAV video format is too old or the AAV file is corrupted. It cannot be converted to AVI",
                        "Tangra 3",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);

                    return(false);
                }

                if ((isAavFile && !double.IsNaN(effFrameDuration)) ||
                    !isAavFile && m_Index.Index[lastFrame].ElapsedTime.Ticks != 0)
                {
                    // Sampling can be done as we have sufficient timing information
                }
                else
                {
                    MessageBox.Show(
                        "There is insufficient timing information in this file to convert it to AVI. This could be caused by an old file format.",
                        "Tangra 3",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);

                    return(false);
                }

                progressCallback(5, 0);

                for (int i = firstFrame; i <= lastFrame; i++)
                {
                    AdvFramesIndexEntry frame = m_Index.Index[i];

                    AdvImageData  currentImageData;
                    AdvStatusData currentStatusData;
                    Bitmap        currentBitmap;

                    using (Pixelmap pixmap = GetFrameData(i, out currentImageData, out currentStatusData, out currentBitmap))
                    {
                        int lastRepeatedAviFrameNo = 0;

                        if (isAavFile && !double.IsNaN(effFrameDuration))
                        {
                            lastRepeatedAviFrameNo = (int)Math.Round(((i - firstFrame) * effFrameDuration) / msPerFrame);
                        }
                        else if (!isAavFile && frame.ElapsedTime.Ticks != 0)
                        {
                            lastRepeatedAviFrameNo = (int)Math.Round((frame.ElapsedTime.TotalMilliseconds - startingTimeMilliseconds) / msPerFrame);
                        }

                        while (aviFrameNo < lastRepeatedAviFrameNo)
                        {
                            if (!saver.AddAviVideoFrame(pixmap, addedGamma, ImageSection.Adv16NormalisationValue))
                            {
                                progressCallback(100, 0);
                                return(false);
                            }
                            aviFrameNo++;
                        }

                        if (currentBitmap != null)
                        {
                            currentBitmap.Dispose();
                        }
                    }

                    int percDone = (int)Math.Min(90, 90 * (i - firstFrame) * 1.0 / (lastFrame - firstFrame + 1));
                    progressCallback(5 + percDone, 0);
                }
            }
            finally
            {
                saver.CloseAviFile();
                progressCallback(100, 0);
            }

            return(false);
        }
Exemplo n.º 10
0
        internal void SearchFramesByMagicPhase1(OnSearchProgressDelegate progressCallback)
        {
            long bytesToSearch = m_InputFile.Length - 5 - m_RecoveryOffset;

            m_CandidateFrameOffsets.Clear();

            int percentCompleted;
            int lastPercentCompleted = -1;

            int MIN_FRAME_SIZE = 12 + 4;

            for (long i = 0; i < bytesToSearch; i++)
            {
                m_InputFile.Seek(m_RecoveryOffset + i, SeekOrigin.Begin);

                uint frameDataMagic = m_FileReader.ReadUInt32();
                if (frameDataMagic == 0xEE0122FF)
                {
                    try
                    {
                        byte[] data = m_FileReader.ReadBytes(MIN_FRAME_SIZE);

                        // Read the timestamp and exposure
                        long frameTimeMsSince2010 =
                            (long)data[0] + (((long)data[1]) << 8) + (((long)data[2]) << 16) + (((long)data[3]) << 24) +
                            (((long)data[4]) << 32) + (((long)data[5]) << 40) + (((long)data[6]) << 48) + (((long)data[7]) << 56);
                        int exposure = data[8] + (data[9] << 8) + (data[10] << 16) + (data[11] << 24);

                        DateTime startExposure = ADV_ZERO_DATE_REF.AddMilliseconds(frameTimeMsSince2010);
                        double   exposureMS    = exposure / 10.0;

                        if (startExposure.Year < 2010 || startExposure.Year > 2100)
                        {
                            continue;
                        }
                        if (exposureMS < 0 || exposureMS > 60 * 1000)
                        {
                            continue;
                        }

                        m_CandidateFrameOffsets.Add(new RecoveredFrameHeader()
                        {
                            Position             = m_RecoveryOffset + i,
                            StartExposureUT      = startExposure,
                            ExposureMilliseconds = exposureMS
                        });

                        int sectionDataLength = data[12] + (data[13] << 8) + (data[14] << 16) + (data[15] << 24);

                        m_InputFile.Seek(m_RecoveryOffset + i + 4 + 12 + sectionDataLength + 4, SeekOrigin.Begin);

                        byte tagValuesCount = m_FileReader.ReadByte();

                        for (int j = 0; j < tagValuesCount; j++)
                        {
                            m_FileReader.ReadByte();
                            byte len = m_FileReader.ReadByte();
                            m_FileReader.ReadBytes(len);
                        }

                        // NOTE: The next frame should begin somewhere in the next 32 bytes
                        long nextFrameSearchStartPos = m_FileReader.BaseStream.Position;

                        for (int j = 0; j < 32; j++)
                        {
                            m_InputFile.Seek(nextFrameSearchStartPos + j, SeekOrigin.Begin);

                            uint frameDataMagic2 = m_FileReader.ReadUInt32();
                            if (frameDataMagic2 == 0xEE0122FF)
                            {
                                // We found it!
                                i = nextFrameSearchStartPos + j - m_RecoveryOffset - 1;
                                break;
                            }
                        }
                    }
                    catch { }
                }

                percentCompleted = (int)(100.0 * i / bytesToSearch);
                if (lastPercentCompleted < percentCompleted)
                {
                    progressCallback(percentCompleted, m_CandidateFrameOffsets.Count);
                    lastPercentCompleted = percentCompleted;
                }
            }
        }