private static bool CompletePcs(PcsData pcs, Dictionary <int, List <OdsData> > bitmapObjects, Dictionary <int, List <PaletteInfo> > palettes)
        {
            if (pcs?.PcsObjects == null || palettes == null)
            {
                return(false);
            }

            if (pcs.PcsObjects.Count == 0)
            {
                return(true);
            }

            if (!palettes.ContainsKey(pcs.PaletteId))
            {
                return(false);
            }

            pcs.PaletteInfos  = new List <PaletteInfo>(palettes[pcs.PaletteId]);
            pcs.BitmapObjects = new List <List <OdsData> >();
            for (int index = 0; index < pcs.PcsObjects.Count; index++)
            {
                int objId = pcs.PcsObjects[index].ObjectId;
                if (!bitmapObjects.ContainsKey(objId))
                {
                    return(false);
                }

                pcs.BitmapObjects.Add(bitmapObjects[objId]);
            }
            return(true);
        }
Exemple #2
0
        public static Image <Rgba32> GetRgba32(this PcsData pcsData)
        {
            if (pcsData.PcsObjects.Count == 1)
            {
                return(SupDecoder.DecodeImage(pcsData.PcsObjects[0], pcsData.BitmapObjects[0], pcsData.PaletteInfos));
            }

            var r = System.Drawing.Rectangle.Empty;

            for (var ioIndex = 0; ioIndex < pcsData.PcsObjects.Count; ioIndex++)
            {
                var ioRect = new System.Drawing.Rectangle(pcsData.PcsObjects[ioIndex].Origin, pcsData.BitmapObjects[ioIndex][0].Size);
                r = r.IsEmpty ? ioRect : System.Drawing.Rectangle.Union(r, ioRect);
            }

            var mergedBmp = new Image <Rgba32>(r.Width, r.Height);

            for (var ioIndex = 0; ioIndex < pcsData.PcsObjects.Count; ioIndex++)
            {
                var offset = pcsData.PcsObjects[ioIndex].Origin - new System.Drawing.Size(r.Location);
                using (var singleBmp = SupDecoder.DecodeImage(pcsData.PcsObjects[ioIndex], pcsData.BitmapObjects[ioIndex], pcsData.PaletteInfos))
                {
                    mergedBmp.Mutate(b => b.DrawImage(singleBmp, new SixLabors.ImageSharp.Point(offset.X, offset.Y), 0));
                }
            }
            return(mergedBmp);
        }
        private PcsData CreatePcsData(DaegunPacketClass _packet)
        {
            DaegunPacket packet = _packet.Packet;
            PcsData      pcs    = new PcsData();

            pcs.deviceId            = $"DS{packet.sSiteId}_PCS{packet.Pcs.PcsNumber}";
            pcs.groupId             = 1;
            pcs.siteId              = packet.sSiteId;
            pcs.groupName           = "PCS_SYSTEM";
            pcs.freq                = packet.Pcs.Frequency;
            pcs.acVltR              = packet.Pcs.AC_line_voltage.R;
            pcs.acVltS              = packet.Pcs.AC_line_voltage.S;
            pcs.acVltT              = packet.Pcs.AC_line_voltage.T;
            pcs.actPwrCmdLmtLowChg  = 0;
            pcs.actPwrCmdLmtLowDhg  = 0;
            pcs.actPwrCmdLmtHighChg = packet.Bsc.ChargePowerLimit;
            pcs.actPwrCmdLmtHighDhg = packet.Bsc.DischargePowerLimit;
            pcs.actPwr              = packet.Pcs.ActivePower;
            pcs.acCrtLow            = 0;
            pcs.acCrtHigh           = packet.Bsc.DischargeCurrentLimit;
            pcs.acCrtR              = packet.Pcs.AC_phase_current.R;
            pcs.acCrtS              = packet.Pcs.AC_phase_current.S;
            pcs.acCrtT              = packet.Pcs.AC_phase_current.T;
            pcs.rctPwr              = packet.Pcs.ReactivePower;
            pcs.pf        = packet.Pcs.PowerFactor;
            pcs.timestamp = _packet.Timestamp;
            return(pcs);
        }
        private async void StoreDb(CancellationToken token)
        {
            try
            {
                if (insertBatchList.Count == 0)
                {
                    return;
                }

                List <PcsData> pcsDatas = new List <PcsData>();
                List <BmsData> bmsDatas = new List <BmsData>();
                List <PvData>  pvDatas  = new List <PvData>();
                foreach (var packet in insertBatchList)
                {
                    PcsData pcs = CreatePcsData(packet);
                    pcsDatas.Add(pcs);

                    bmsDatas.Add(CreateBmsData(packet));
                    pvDatas.Add(CreatePvData(packet));
                }

                using (var session = _da.SessionFactory.OpenStatelessSession())
                {
                    using (var transact = session.BeginTransaction())
                    {
                        foreach (var pcs in pcsDatas)
                        {
                            await session.InsertAsync(pcs, token);
                        }

                        foreach (var bms in bmsDatas)
                        {
                            await session.InsertAsync(bms, token);
                        }

                        foreach (var pv in pvDatas)
                        {
                            await session.InsertAsync(pv, token);
                        }
                        await transact.CommitAsync(token);
                    }
                }

                insertBatchList.Clear();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, ex.Message);
            }
        }
        private static PcsData ParsePicture(byte[] buffer, SupSegment segment)
        {
            if (buffer.Length < 11)
            {
                return(new PcsData
                {
                    CompositionState = CompositionState.Invalid
                });
            }

            var sb  = new StringBuilder();
            var pcs = new PcsData
            {
                Size = new Size(BigEndianInt16(buffer, 0), BigEndianInt16(buffer, 2)),
                FramesPerSecondType = buffer[4],
                CompNum             = BigEndianInt16(buffer, 5),
                CompositionState    = GetCompositionState(buffer[7]),
                StartTime           = segment.PtsTimestamp,
                PaletteUpdate       = buffer[8] == 0x80,
                PaletteId           = buffer[9]
            };
            // hi nibble: frame_rate, lo nibble: reserved
            // 8bit  palette_update_flag (0x80), 7bit reserved
            // 8bit  palette_id_ref
            int compositionObjectCount = buffer[10];    // 8bit  number_of_composition_objects (0..2)

            sb.AppendFormat("CompNum: {0}, Pts: {1}, State: {2}, PalUpdate: {3}, PalId {4}", pcs.CompNum, ToolBox.PtsToTimeString(pcs.StartTime), pcs.CompositionState, pcs.PaletteUpdate, pcs.PaletteId);

            if (pcs.CompositionState == CompositionState.Invalid)
            {
                sb.Append("Illegal composition state Invalid");
            }
            else
            {
                int offset = 0;
                pcs.PcsObjects = new List <PcsObject>();
                for (int compObjIndex = 0; compObjIndex < compositionObjectCount; compObjIndex++)
                {
                    var pcsObj = ParsePcs(buffer, offset);
                    pcs.PcsObjects.Add(pcsObj);
                    sb.AppendLine();
                    sb.AppendFormat("ObjId: {0}, WinId: {1}, Forced: {2}, X: {3}, Y: {4}",
                                    pcsObj.ObjectId, pcsObj.WindowId, pcsObj.IsForced, pcsObj.Origin.X, pcsObj.Origin.Y);
                    offset += 8;
                }
            }
            pcs.Message = sb.ToString();
            return(pcs);
        }
        private static PcsData ParsePicture(byte[] buffer, SupSegment segment)
        {
            var sb = new StringBuilder();
            var pcs = new PcsData();
            pcs.Size = new Size(BigEndianInt16(buffer, 0), BigEndianInt16(buffer, 2));
            pcs.FramesPerSecondType = buffer[4];                // hi nibble: frame_rate, lo nibble: reserved
            pcs.CompNum = BigEndianInt16(buffer, 5);
            pcs.CompositionState = GetCompositionState(buffer[7]);
            pcs.StartTime = segment.PtsTimestamp;
            // 8bit  palette_update_flag (0x80), 7bit reserved
            pcs.PaletteUpdate = (buffer[8] == 0x80);
            pcs.PaletteId = buffer[9];  // 8bit  palette_id_ref
            int compositionObjectCount = buffer[10];    // 8bit  number_of_composition_objects (0..2)

            sb.AppendFormat("CompNum: {0}, Pts: {1}, State: {2}, PalUpdate: {3}, PalId {4}", pcs.CompNum, ToolBox.PtsToTimeString(pcs.StartTime), pcs.CompositionState, pcs.PaletteUpdate, pcs.PaletteId);

            if (pcs.CompositionState == CompositionState.Invalid)
            {
                sb.Append("Illegal composition state Invalid");
            }
            else
            {
                int offset = 0;
                pcs.PcsObjects = new List<PcsObject>();
                for (int compObjIndex = 0; compObjIndex < compositionObjectCount; compObjIndex++)
                {
                    PcsObject pcsObj = ParsePcs(buffer, offset);
                    pcs.PcsObjects.Add(pcsObj);
                    sb.AppendLine();
                    sb.AppendFormat("ObjId: {0}, WinId: {1}, Forced: {2}, X: {3}, Y: {4}",
                        pcsObj.ObjectId, pcsObj.WindowId, pcsObj.IsForced, pcsObj.Origin.X, pcsObj.Origin.Y);
                    offset += 8;
                }
            }
            pcs.Message = sb.ToString();
            return pcs;
        }
        private static bool CompletePcs(PcsData pcs, Dictionary<int, List<OdsData>> bitmapObjects, Dictionary<int, List<PaletteInfo>> palettes)
        {
            if (pcs == null || pcs.PcsObjects == null || palettes == null)
                return false;

            if (pcs.PcsObjects.Count == 0)
                return true;

            if (!palettes.ContainsKey(pcs.PaletteId))
                return false;

            pcs.PaletteInfos = new List<PaletteInfo>(palettes[pcs.PaletteId]);
            pcs.BitmapObjects = new List<List<OdsData>>();
            for (int index = 0; index < pcs.PcsObjects.Count; index++)
            {
                int objId = pcs.PcsObjects[index].ObjectId;
                if (!bitmapObjects.ContainsKey(objId))
                    return false;
                pcs.BitmapObjects.Add(bitmapObjects[objId]);
            }
            return true;
        }
        public static List <PcsData> ParseBluRaySup(Stream ms, StringBuilder log, bool fromMatroskaFile, Dictionary <int, List <PaletteInfo> > lastPalettes = null)
        {
            long    position      = ms.Position;
            int     segmentCount  = 0;
            var     palettes      = new Dictionary <int, List <PaletteInfo> >();
            bool    forceFirstOds = true;
            var     bitmapObjects = new Dictionary <int, List <OdsData> >();
            PcsData latestPcs     = null;
            var     pcsList       = new List <PcsData>();
            var     headerBuffer  = fromMatroskaFile ? new byte[3] : new byte[HeaderSize];

            while (ms.Read(headerBuffer, 0, headerBuffer.Length) == headerBuffer.Length)
            {
                var segment = fromMatroskaFile ? ParseSegmentHeaderFromMatroska(headerBuffer) : ParseSegmentHeader(headerBuffer, log);
                position += headerBuffer.Length;

                try
                {
                    // Read segment data
                    var buffer    = new byte[segment.Size];
                    var bytesRead = ms.Read(buffer, 0, buffer.Length);
                    if (bytesRead < buffer.Length)
                    {
                        break;
                    }


#if DEBUG
                    log.Append(segmentCount + ": ");
#endif

                    switch (segment.Type)
                    {
                    case 0x14:     // Palette
                        if (latestPcs != null)
                        {
#if DEBUG
                            log.AppendLine($"0x14 - Palette - PDS offset={position} size={segment.Size}");
#endif
                            var pds = ParsePds(buffer, segment);
#if DEBUG
                            log.AppendLine(pds.Message);
#endif
                            if (pds.PaletteInfo != null)
                            {
                                if (!palettes.ContainsKey(pds.PaletteId))
                                {
                                    palettes[pds.PaletteId] = new List <PaletteInfo>();
                                }
                                else
                                {
                                    if (latestPcs.PaletteUpdate)
                                    {
                                        palettes[pds.PaletteId].RemoveAt(palettes[pds.PaletteId].Count - 1);
                                    }
                                    else
                                    {
#if DEBUG
                                        log.AppendLine("Extra Palette");
#endif
                                    }
                                }
                                palettes[pds.PaletteId].Add(pds.PaletteInfo);
                            }
                        }
                        break;

                    case 0x15:     // Image bitmap data
                        if (latestPcs != null)
                        {
#if DEBUG
                            log.AppendLine($"0x15 - Bitmap data - ODS offset={position} size={segment.Size}");
#endif
                            var ods = ParseOds(buffer, segment, forceFirstOds);
#if DEBUG
                            log.AppendLine(ods.Message);
#endif
                            if (!latestPcs.PaletteUpdate)
                            {
                                List <OdsData> odsList;
                                if (ods.IsFirst)
                                {
                                    odsList = new List <OdsData> {
                                        ods
                                    };
                                    bitmapObjects[ods.ObjectId] = odsList;
                                }
                                else
                                {
                                    if (bitmapObjects.TryGetValue(ods.ObjectId, out odsList))
                                    {
                                        odsList.Add(ods);
                                    }
                                    else
                                    {
#if DEBUG
                                        log.AppendLine($"INVALID ObjectId {ods.ObjectId} in ODS, offset={position}");
#endif
                                    }
                                }
                            }
                            else
                            {
#if DEBUG
                                log.AppendLine($"Bitmap Data Ignore due to PaletteUpdate offset={position}");
#endif
                            }
                            forceFirstOds = false;
                        }
                        break;

                    case 0x16:     // Picture time codes
                        if (latestPcs != null)
                        {
                            if (CompletePcs(latestPcs, bitmapObjects, palettes.Count > 0 ? palettes : lastPalettes))
                            {
                                pcsList.Add(latestPcs);
                            }
                        }

#if DEBUG
                        log.AppendLine($"0x16 - Picture codes, offset={position} size={segment.Size}");
#endif
                        forceFirstOds = true;
                        var nextPcs = ParsePicture(buffer, segment);
                        if (nextPcs.StartTime > 0 && pcsList.Count > 0 && pcsList.Last().EndTime == 0)
                        {
                            pcsList.Last().EndTime = nextPcs.StartTime;
                        }
#if DEBUG
                        log.AppendLine(nextPcs.Message);
#endif
                        latestPcs = nextPcs;
                        if (latestPcs.CompositionState == CompositionState.EpochStart)
                        {
                            bitmapObjects.Clear();
                            palettes.Clear();
                        }
                        break;

                    case 0x17:     // Window display
                        if (latestPcs != null)
                        {
#if DEBUG
                            log.AppendLine($"0x17 - Window display offset={position} size={segment.Size}");
#endif
                            int windowCount = buffer[0];
                            int offset      = 0;
                            for (int nextWindow = 0; nextWindow < windowCount; nextWindow++)
                            {
                                int windowId = buffer[1 + offset];
                                int x        = BigEndianInt16(buffer, 2 + offset);
                                int y        = BigEndianInt16(buffer, 4 + offset);
                                int width    = BigEndianInt16(buffer, 6 + offset);
                                int height   = BigEndianInt16(buffer, 8 + offset);
                                log.AppendLine(string.Format("WinId: {4}, X: {0}, Y: {1}, Width: {2}, Height: {3}",
                                                             x, y, width, height, windowId));
                                offset += 9;
                            }
                        }
                        break;

                    case 0x80:
                        forceFirstOds = true;
#if DEBUG
                        log.AppendLine($"0x80 - END offset={position} size={segment.Size}");
#endif
                        if (latestPcs != null)
                        {
                            if (CompletePcs(latestPcs, bitmapObjects, palettes.Count > 0 ? palettes : lastPalettes))
                            {
                                pcsList.Add(latestPcs);
                            }
                            latestPcs = null;
                        }
                        break;

                    default:
#if DEBUG
                        log.AppendLine($"0x?? - END offset={position} UNKNOWN SEGMENT TYPE={segment.Type}");
#endif
                        break;
                    }
                }
                catch (IndexOutOfRangeException e)
                {
                    log.Append($"Index of of range at pos {position - headerBuffer.Length}: {e.StackTrace}");
                }
                position += segment.Size;
                segmentCount++;
            }

            if (latestPcs != null)
            {
                if (CompletePcs(latestPcs, bitmapObjects, palettes.Count > 0 ? palettes : lastPalettes))
                {
                    pcsList.Add(latestPcs);
                }
            }

            for (int pcsIndex = 1; pcsIndex < pcsList.Count; pcsIndex++)
            {
                var prev = pcsList[pcsIndex - 1];
                if (prev.EndTime == 0)
                {
                    prev.EndTime = pcsList[pcsIndex].StartTime;
                }
            }

            pcsList.RemoveAll(pcs => pcs.PcsObjects.Count == 0);

            foreach (var pcs in pcsList)
            {
                foreach (var odsList in pcs.BitmapObjects)
                {
                    if (odsList.Count > 1)
                    {
                        int bufSize = 0;
                        foreach (var ods in odsList)
                        {
                            bufSize += ods.Fragment.ImagePacketSize;
                        }

                        byte[] buf    = new byte[bufSize];
                        int    offset = 0;
                        foreach (var ods in odsList)
                        {
                            Buffer.BlockCopy(ods.Fragment.ImageBuffer, 0, buf, offset, ods.Fragment.ImagePacketSize);
                            offset += ods.Fragment.ImagePacketSize;
                        }
                        odsList[0].Fragment.ImageBuffer     = buf;
                        odsList[0].Fragment.ImagePacketSize = bufSize;
                        while (odsList.Count > 1)
                        {
                            odsList.RemoveAt(1);
                        }
                    }
                }
            }

            for (int pcsIndex = pcsList.Count - 1; pcsIndex > 0; pcsIndex--)
            {
                var cur  = pcsList[pcsIndex];
                var prev = pcsList[pcsIndex - 1];
                if (Math.Abs(prev.EndTime - cur.StartTime) < 10 && prev.Size.Width == cur.Size.Width && prev.Size.Height == cur.Size.Height)
                {
                    if (cur.BitmapObjects.Count > 0 && cur.BitmapObjects[0].Count > 0 &&
                        prev.BitmapObjects.Count > 0 && prev.BitmapObjects[0].Count > 0 &&
                        ByteArraysEqual(cur.BitmapObjects[0][0].Fragment.ImageBuffer, prev.BitmapObjects[0][0].Fragment.ImageBuffer))
                    {
                        prev.EndTime = cur.EndTime;
                        pcsList.RemoveAt(pcsIndex);
                    }
                }
            }

            // save last palette
            if (lastPalettes != null && palettes.Count > 0)
            {
                lastPalettes.Clear();
                foreach (var palette in palettes)
                {
                    lastPalettes.Add(palette.Key, palette.Value);
                }
            }

            return(pcsList);
        }
        private static bool CompletePcs(PcsData pcs, Dictionary<int, List<OdsData>> bitmapObjects, Dictionary<int, List<PaletteInfo>> palettes)
        {
            if (pcs == null || pcs.PcsObjects == null || palettes == null)
            {
                return false;
            }

            if (pcs.PcsObjects.Count == 0)
            {
                return true;
            }

            if (!palettes.ContainsKey(pcs.PaletteId))
            {
                return false;
            }

            pcs.PaletteInfos = new List<PaletteInfo>(palettes[pcs.PaletteId]);
            pcs.BitmapObjects = new List<List<OdsData>>();
            foreach (int objId in pcs.PcsObjects.Select(t => t.ObjectId))
            {
                if (!bitmapObjects.ContainsKey(objId))
                {
                    return false;
                }

                pcs.BitmapObjects.Add(bitmapObjects[objId]);
            }

            return true;
        }
Exemple #10
0
        public static List <PcsData> ParseBluRaySup(Stream ms, StringBuilder log, bool fromMatroskaFile)
        {
            SupSegment segment;
            long       position      = ms.Position;
            int        segmentCount  = 0;
            var        palettes      = new Dictionary <int, List <PaletteInfo> >();
            bool       forceFirstOds = true;
            var        bitmapObjects = new Dictionary <int, List <OdsData> >();
            PcsData    latestPcs     = null;
            int        latestCompNum = -1;
            var        pcsList       = new List <PcsData>();

            byte[] buffer;
            byte[] headerBuffer;
            if (fromMatroskaFile)
            {
                headerBuffer = new byte[3];
            }
            else
            {
                headerBuffer = new byte[HeaderSize];
            }

            while (position < ms.Length)
            {
                ms.Seek(position, SeekOrigin.Begin);

                // Read segment header
                ms.Read(headerBuffer, 0, headerBuffer.Length);
                if (fromMatroskaFile)
                {
                    segment = ParseSegmentHeaderFromMatroska(headerBuffer);
                }
                else
                {
                    segment = ParseSegmentHeader(headerBuffer, log);
                }
                position += headerBuffer.Length;

                // Read segment data
                buffer = new byte[segment.Size];
                ms.Read(buffer, 0, buffer.Length);
                log.Append(segmentCount + ": ");

                switch (segment.Type)
                {
                case 0x14:     // Palette
                    if (latestPcs != null)
                    {
                        log.AppendLine(string.Format("0x14 - Palette - PDS offset={0} size={1}", position, segment.Size));
                        PdsData pds = ParsePds(buffer, segment);
                        log.AppendLine(pds.Message);
                        if (pds.PaletteInfo != null)
                        {
                            if (!palettes.ContainsKey(pds.PaletteId))
                            {
                                palettes[pds.PaletteId] = new List <PaletteInfo>();
                            }
                            else
                            {
                                if (latestPcs.PaletteUpdate)
                                {
                                    palettes[pds.PaletteId].RemoveAt(palettes[pds.PaletteId].Count - 1);
                                }
                                else
                                {
                                    log.AppendLine("Extra Palette");
                                }
                            }
                            palettes[pds.PaletteId].Add(pds.PaletteInfo);
                        }
                    }
                    break;

                case 0x15:     // Image bitmap data
                    if (latestPcs != null)
                    {
                        log.AppendLine(string.Format("0x15 - Bitmap data - ODS offset={0} size={1}", position, segment.Size));
                        OdsData ods = ParseOds(buffer, segment, forceFirstOds);
                        log.AppendLine(ods.Message);
                        if (!latestPcs.PaletteUpdate)
                        {
                            List <OdsData> odsList;
                            if (ods.IsFirst)
                            {
                                odsList = new List <OdsData>();
                                odsList.Add(ods);
                                bitmapObjects[ods.ObjectId] = odsList;
                            }
                            else
                            {
                                if (bitmapObjects.TryGetValue(ods.ObjectId, out odsList))
                                {
                                    odsList.Add(ods);
                                }
                                else
                                {
                                    log.AppendLine(string.Format("INVALID ObjectId {0} in ODS, offset={1}", ods.ObjectId, position));
                                }
                            }
                        }
                        else
                        {
                            log.AppendLine(string.Format("Bitmap Data Ignore due to PaletteUpdate offset={0}", position));
                        }
                        forceFirstOds = false;
                    }
                    break;

                case 0x16:     // Picture time codes
                    if (latestPcs != null)
                    {
                        if (CompletePcs(latestPcs, bitmapObjects, palettes))
                        {
                            pcsList.Add(latestPcs);
                        }
                        latestPcs = null;
                    }

                    log.AppendLine(string.Format("0x16 - Picture codes, offset={0} size={1}", position, segment.Size));
                    forceFirstOds = true;
                    PcsData nextPcs = ParsePicture(buffer, segment);
                    log.AppendLine(nextPcs.Message);
                    latestPcs     = nextPcs;
                    latestCompNum = nextPcs.CompNum;
                    if (latestPcs.CompositionState == CompositionState.EpochStart)
                    {
                        bitmapObjects.Clear();
                        palettes.Clear();
                    }
                    break;

                case 0x17:     // Window display
                    if (latestPcs != null)
                    {
                        log.AppendLine(string.Format("0x17 - Window display offset={0} size={1}", position, segment.Size));
                        int windowCount = buffer[0];
                        int offset      = 0;
                        for (int nextWindow = 0; nextWindow < windowCount; nextWindow++)
                        {
                            int windowId = buffer[1 + offset];
                            int x        = BigEndianInt16(buffer, 2 + offset);
                            int y        = BigEndianInt16(buffer, 4 + offset);
                            int width    = BigEndianInt16(buffer, 6 + offset);
                            int height   = BigEndianInt16(buffer, 8 + offset);
                            log.AppendLine(string.Format("WinId: {4}, X: {0}, Y: {1}, Width: {2}, Height: {3}",
                                                         x, y, width, height, windowId));
                            offset += 9;
                        }
                    }
                    break;

                case 0x80:
                    forceFirstOds = true;
                    log.AppendLine(string.Format("0x80 - END offset={0} size={1}", position, segment.Size));
                    if (latestPcs != null)
                    {
                        if (CompletePcs(latestPcs, bitmapObjects, palettes))
                        {
                            pcsList.Add(latestPcs);
                        }
                        latestPcs = null;
                    }
                    break;

                default:
                    log.AppendLine(string.Format("0x?? - END offset={0} UNKOWN SEGMENT TYPE={1}", position, segment.Type));
                    break;
                }
                position += segment.Size;
                segmentCount++;
            }

            if (latestPcs != null)
            {
                if (CompletePcs(latestPcs, bitmapObjects, palettes))
                {
                    pcsList.Add(latestPcs);
                }
                latestPcs = null;
            }

            for (int pcsIndex = 1; pcsIndex < pcsList.Count; pcsIndex++)
            {
                pcsList[pcsIndex - 1].EndTime = pcsList[pcsIndex].StartTime;
            }
            pcsList.RemoveAll(pcs => pcs.PcsObjects.Count == 0);

            foreach (PcsData pcs in pcsList)
            {
                foreach (List <OdsData> odsList in pcs.BitmapObjects)
                {
                    if (odsList.Count > 1)
                    {
                        int bufSize = 0;
                        foreach (OdsData ods in odsList)
                        {
                            bufSize += ods.Fragment.ImagePacketSize;
                        }
                        byte[] buf    = new byte[bufSize];
                        int    offset = 0;
                        foreach (OdsData ods in odsList)
                        {
                            Buffer.BlockCopy(ods.Fragment.ImageBuffer, 0, buf, offset, ods.Fragment.ImagePacketSize);
                            offset += ods.Fragment.ImagePacketSize;
                        }
                        odsList[0].Fragment.ImageBuffer     = buf;
                        odsList[0].Fragment.ImagePacketSize = bufSize;
                        while (odsList.Count > 1)
                        {
                            odsList.RemoveAt(1);
                        }
                    }
                }
            }

            for (int pcsIndex = pcsList.Count - 1; pcsIndex > 0; pcsIndex--)
            {
                var cur  = pcsList[pcsIndex];
                var prev = pcsList[pcsIndex - 1];
                if (Math.Abs(prev.EndTime - cur.StartTime) < 10 && prev.Size.Width == cur.Size.Width && prev.Size.Height == cur.Size.Height)
                {
                    if (cur.BitmapObjects.Count > 0 && cur.BitmapObjects[0].Count > 0 &&
                        prev.BitmapObjects.Count > 0 && prev.BitmapObjects[0].Count > 0 &&
                        ByteArraysEqual(cur.BitmapObjects[0][0].Fragment.ImageBuffer, prev.BitmapObjects[0][0].Fragment.ImageBuffer))
                    {
                        prev.EndTime = cur.EndTime;
                        pcsList.RemoveAt(pcsIndex);
                    }
                }
            }

            return(pcsList);
        }
        public static List <PcsData> ParseBluRaySup(Stream ms, StringBuilder log, bool fromMatroskaFile, Dictionary <int, List <PaletteInfo> > lastPalettes, Dictionary <int, List <OdsData> > bitmapObjects)
        {
            long    position      = ms.Position;
            int     segmentCount  = 0;
            var     palettes      = new Dictionary <int, List <PaletteInfo> >();
            bool    forceFirstOds = true;
            PcsData latestPcs     = null;
            var     pcsList       = new List <PcsData>();
            var     headerBuffer  = fromMatroskaFile ? new byte[3] : new byte[HeaderSize];

            while (ms.Read(headerBuffer, 0, headerBuffer.Length) == headerBuffer.Length)
            {
                var segment = fromMatroskaFile ? ParseSegmentHeaderFromMatroska(headerBuffer) : ParseSegmentHeader(headerBuffer, log);
                position += headerBuffer.Length;

                try
                {
                    // Read segment data
                    var buffer    = new byte[segment.Size];
                    var bytesRead = ms.Read(buffer, 0, buffer.Length);
                    if (bytesRead < buffer.Length)
                    {
                        break;
                    }


#if DEBUG
                    log.Append(segmentCount + ": ");
#endif

                    switch (segment.Type)
                    {
                    case 0x14:     // Palette
                        if (latestPcs != null)
                        {
#if DEBUG
                            log.AppendLine($"0x14 - Palette - PDS offset={position} size={segment.Size}");
#endif
                            var pds = ParsePds(buffer, segment);
#if DEBUG
                            log.AppendLine(pds.Message);
#endif
                            if (pds.PaletteInfo != null)
                            {
                                if (!palettes.ContainsKey(pds.PaletteId))
                                {
                                    palettes[pds.PaletteId] = new List <PaletteInfo>();
                                }
                                else
                                {
                                    if (latestPcs.PaletteUpdate)
                                    {
                                        palettes[pds.PaletteId].RemoveAt(palettes[pds.PaletteId].Count - 1);
                                    }
                                    else
                                    {
#if DEBUG
                                        log.AppendLine("Extra Palette");
#endif
                                    }
                                }
                                palettes[pds.PaletteId].Add(pds.PaletteInfo);
                            }
                        }
                        break;

                    case 0x15:     // Object Definition Segment (image bitmap data)
                        if (latestPcs != null)
                        {
#if DEBUG
                            log.AppendLine($"0x15 - Bitmap data - ODS offset={position} size={segment.Size}");
#endif
                            var ods = ParseOds(buffer, segment, forceFirstOds);
#if DEBUG
                            log.AppendLine(ods.Message);
#endif
                            if (!latestPcs.PaletteUpdate)
                            {
                                List <OdsData> odsList;
                                if (ods.IsFirst)
                                {
                                    odsList = new List <OdsData> {
                                        ods
                                    };
                                    bitmapObjects[ods.ObjectId] = odsList;
                                }
                                else
                                {
                                    if (bitmapObjects.TryGetValue(ods.ObjectId, out odsList))
                                    {
                                        odsList.Add(ods);
                                    }
                                    else
                                    {
#if DEBUG
                                        log.AppendLine($"INVALID ObjectId {ods.ObjectId} in ODS, offset={position}");
#endif
                                    }
                                }
                            }
                            else
                            {
#if DEBUG
                                log.AppendLine($"Bitmap Data Ignore due to PaletteUpdate offset={position}");
#endif
                            }
                            forceFirstOds = false;
                        }
                        break;

                    case 0x16:     // Picture time codes
                        if (latestPcs != null)
                        {
                            if (CompletePcs(latestPcs, bitmapObjects, palettes.Count > 0 ? palettes : lastPalettes))
                            {
                                pcsList.Add(latestPcs);
                            }
                        }

#if DEBUG
                        log.AppendLine($"0x16 - Picture codes, offset={position} size={segment.Size}");
#endif
                        forceFirstOds = true;
                        var nextPcs = ParsePicture(buffer, segment);
                        if (nextPcs.StartTime > 0 && pcsList.Count > 0 && pcsList.Last().EndTime == 0)
                        {
                            pcsList.Last().EndTime = nextPcs.StartTime;
                        }
#if DEBUG
                        log.AppendLine(nextPcs.Message);
#endif
                        latestPcs = nextPcs;
                        if (latestPcs.CompositionState == CompositionState.EpochStart)
                        {
                            bitmapObjects.Clear();
                            palettes.Clear();
                        }
                        break;

                    case 0x17:     // Window display
                        if (latestPcs != null)
                        {
#if DEBUG
                            log.AppendLine($"0x17 - Window display offset={position} size={segment.Size}");
#endif
                            int windowCount = buffer[0];
                            int offset      = 0;
                            for (int nextWindow = 0; nextWindow < windowCount; nextWindow++)
                            {
                                int windowId = buffer[1 + offset];
                                int x        = BigEndianInt16(buffer, 2 + offset);
                                int y        = BigEndianInt16(buffer, 4 + offset);
                                int width    = BigEndianInt16(buffer, 6 + offset);
                                int height   = BigEndianInt16(buffer, 8 + offset);
                                log.AppendLine(string.Format("WinId: {4}, X: {0}, Y: {1}, Width: {2}, Height: {3}", x, y, width, height, windowId));
                                offset += 9;
                            }
                        }
                        break;

                    case 0x80:
                        forceFirstOds = true;
#if DEBUG
                        log.AppendLine($"0x80 - END offset={position} size={segment.Size}");
#endif
                        if (latestPcs != null)
                        {
                            if (CompletePcs(latestPcs, bitmapObjects, palettes.Count > 0 ? palettes : lastPalettes))
                            {
                                pcsList.Add(latestPcs);
                            }
                            latestPcs = null;
                        }
                        break;

                    default:
#if DEBUG
                        log.AppendLine($"0x?? - END offset={position} UNKNOWN SEGMENT TYPE={segment.Type}");
#endif
                        break;
                    }
                }
                catch (IndexOutOfRangeException e)
                {
                    log.Append($"Index of of range at pos {position - headerBuffer.Length}: {e.StackTrace}");
                }
                position += segment.Size;
                segmentCount++;
            }

            if (latestPcs != null)
            {
                if (CompletePcs(latestPcs, bitmapObjects, palettes.Count > 0 ? palettes : lastPalettes))
                {
                    pcsList.Add(latestPcs);
                }
            }

            for (int pcsIndex = 1; pcsIndex < pcsList.Count; pcsIndex++)
            {
                var prev = pcsList[pcsIndex - 1];
                if (prev.EndTime == 0)
                {
                    prev.EndTime = pcsList[pcsIndex].StartTime;
                }
            }

            pcsList.RemoveAll(pcs => pcs.PcsObjects.Count == 0);

            foreach (var pcs in pcsList)
            {
                foreach (var odsList in pcs.BitmapObjects)
                {
                    if (odsList.Count > 1)
                    {
                        int bufSize = 0;
                        foreach (var ods in odsList)
                        {
                            bufSize += ods.Fragment.ImagePacketSize;
                        }

                        byte[] buf    = new byte[bufSize];
                        int    offset = 0;
                        foreach (var ods in odsList)
                        {
                            Buffer.BlockCopy(ods.Fragment.ImageBuffer, 0, buf, offset, ods.Fragment.ImagePacketSize);
                            offset += ods.Fragment.ImagePacketSize;
                        }
                        odsList[0].Fragment.ImageBuffer     = buf;
                        odsList[0].Fragment.ImagePacketSize = bufSize;
                        while (odsList.Count > 1)
                        {
                            odsList.RemoveAt(1);
                        }
                    }
                }
            }

            if (!Configuration.Settings.SubtitleSettings.BluRaySupSkipMerge || Configuration.Settings.SubtitleSettings.BluRaySupForceMergeAll)
            {
                // Merge images that are the same (probably due to fade)
                // First we find groups with same pixels
                var removeIndices = new List <DeleteIndex>();
                var deleteNo      = 0;
                for (var pcsIndex = pcsList.Count - 1; pcsIndex > 0; pcsIndex--)
                {
                    var cur  = pcsList[pcsIndex];
                    var prev = pcsList[pcsIndex - 1];
                    if (Math.Abs(prev.EndTime - cur.StartTime) < 10 && prev.Size.Width == cur.Size.Width && prev.Size.Height == cur.Size.Height)
                    {
                        if (cur.BitmapObjects.Count > 0 && cur.BitmapObjects[0].Count > 0 &&
                            prev.BitmapObjects.Count == cur.BitmapObjects.Count && prev.BitmapObjects[0].Count == cur.BitmapObjects[0].Count)
                        {
                            var remove = true;
                            for (var k = 0; k < cur.BitmapObjects.Count; k++)
                            {
                                var c = cur.BitmapObjects[k];
                                var p = prev.BitmapObjects[k];
                                if (p.Count == c.Count)
                                {
                                    for (var j = 0; j < c.Count; j++)
                                    {
                                        if (!ByteArraysEqual(c[j].Fragment.ImageBuffer, p[j].Fragment.ImageBuffer))
                                        {
                                            remove = false;
                                            break;
                                        }
                                    }
                                }
                                else
                                {
                                    remove = false;
                                    break;
                                }
                            }

                            if (remove)
                            {
                                if (!removeIndices.Any(p => p.Number == deleteNo && p.Index == pcsIndex - 1))
                                {
                                    removeIndices.Add(new DeleteIndex {
                                        Number = deleteNo, Index = pcsIndex - 1
                                    });
                                }
                                if (!removeIndices.Any(p => p.Number == deleteNo && p.Index == pcsIndex))
                                {
                                    removeIndices.Add(new DeleteIndex {
                                        Number = deleteNo, Index = pcsIndex
                                    });
                                }
                            }
                            else
                            {
                                deleteNo++;
                            }
                        }
                    }
                    else
                    {
                        deleteNo++;
                    }
                }

                // Use the middle image of each group with same pixels
                int mergeCount = removeIndices.GroupBy(p => p.Number).Count();
                foreach (var group in removeIndices.GroupBy(p => p.Number).OrderBy(p => p.Key))
                {
                    var arr           = group.OrderByDescending(p => p.Index).ToArray();
                    var middle        = (int)Math.Round(group.Count() / 2.0);
                    var middleElement = arr[middle];

                    if (!QualifiesForMerge(arr, pcsList, mergeCount))
                    {
                        // Don't know really how to do this... so "QualifiesForMerge" is mostly guessing
                        // See https://github.com/SubtitleEdit/subtitleedit/issues/5713
                        continue;
                    }

                    pcsList[middleElement.Index].StartTime = pcsList[arr.Last().Index].StartTime;
                    pcsList[middleElement.Index].EndTime   = pcsList[arr.First().Index].EndTime;
                    foreach (var deleteIndex in group.OrderByDescending(p => p.Index))
                    {
                        if (deleteIndex != middleElement)
                        {
                            pcsList.RemoveAt(deleteIndex.Index);
                        }
                    }
                }
            }

            // save last palette
            if (lastPalettes != null && palettes.Count > 0)
            {
                lastPalettes.Clear();
                foreach (var palette in palettes)
                {
                    lastPalettes.Add(palette.Key, palette.Value);
                }
            }

            return(pcsList);
        }