Пример #1
0
            private static void PutPixel(FastBitmap bmp, int index, int color, BluRaySupPalette palette)
            {
                int x = index % bmp.Width;
                int y = index / bmp.Width;

                if (x < bmp.Width && y < bmp.Height)
                {
                    bmp.SetPixel(x, y, Color.FromArgb(palette.GetArgb(color)));
                }
            }
Пример #2
0
            public static BluRaySupPalette DecodePalette(IList <PaletteInfo> paletteInfos)
            {
                BluRaySupPalette palette = new BluRaySupPalette(256);
                // by definition, index 0xff is always completely transparent
                // also all entries must be fully transparent after initialization

                bool fadeOut = false;

                for (int j = 0; j < paletteInfos.Count; j++)
                {
                    PaletteInfo p     = paletteInfos[j];
                    int         index = 0;

                    for (int i = 0; i < p.PaletteSize; i++)
                    {
                        // each palette entry consists of 5 bytes
                        int palIndex = p.PaletteBuffer[index];
                        int y        = p.PaletteBuffer[++index];
                        int cr       = p.PaletteBuffer[++index];
                        int cb       = p.PaletteBuffer[++index];
                        int alpha    = p.PaletteBuffer[++index];

                        int alphaOld = palette.GetAlpha(palIndex);
                        // avoid fading out
                        if (alpha >= alphaOld)
                        {
                            if (alpha < AlphaCrop)
                            {// to not mess with scaling algorithms, make transparent color black
                                y  = 16;
                                cr = 128;
                                cb = 128;
                            }
                            palette.SetAlpha(palIndex, alpha);
                        }
                        else
                        {
                            fadeOut = true;
                        }

                        palette.SetYCbCr(palIndex, y, cb, cr);
                        index++;
                    }
                }
                if (fadeOut)
                {
                    System.Diagnostics.Debug.Print("fade out detected -> patched palette\n");
                }
                return(palette);
            }
Пример #3
0
            public static BluRaySupPalette DecodePalette(IList<PaletteInfo> paletteInfos)
            {
                BluRaySupPalette palette = new BluRaySupPalette(256);
                // by definition, index 0xff is always completely transparent
                // also all entries must be fully transparent after initialization

                bool fadeOut = false;
                for (int j = 0; j < paletteInfos.Count; j++)
                {
                    PaletteInfo p = paletteInfos[j];
                    int index = 0;

                    for (int i = 0; i < p.PaletteSize; i++)
                    {
                        // each palette entry consists of 5 bytes
                        int palIndex = p.PaletteBuffer[index];
                        int y = p.PaletteBuffer[++index];
                        int cr = p.PaletteBuffer[++index];
                        int cb = p.PaletteBuffer[++index];
                        int alpha = p.PaletteBuffer[++index];

                        int alphaOld = palette.GetAlpha(palIndex);
                        // avoid fading out
                        if (alpha >= alphaOld)
                        {
                            if (alpha < AlphaCrop)
                            {// to not mess with scaling algorithms, make transparent color black
                                y = 16;
                                cr = 128;
                                cb = 128;
                            }
                            palette.SetAlpha(palIndex, alpha);
                        }
                        else
                        {
                            fadeOut = true;
                        }

                        palette.SetYCbCr(palIndex, y, cb, cr);
                        index++;
                    }
                }
                if (fadeOut)
                {
                    System.Diagnostics.Debug.Print("fade out detected -> patched palette\n");
                }
                return palette;
            }
Пример #4
0
 private static void PutPixel(FastBitmap bmp, int index, int color, BluRaySupPalette palette)
 {
     int x = index % bmp.Width;
     int y = index / bmp.Width;
     if (x < bmp.Width && y < bmp.Height)
         bmp.SetPixel(x, y, Color.FromArgb(palette.GetArgb(color)));
 }
Пример #5
0
        /// <summary>
        /// Create the binary stream representation of one caption
        /// </summary>
        /// <param name="pic">SubPicture object containing caption info</param>
        /// <param name="bm">bitmap</param>
        /// <param name="fps">frames per second</param>
        /// <param name="bottomMargin">image bottom margin</param>
        /// <returns>byte buffer containing the binary stream representation of one caption</returns>
        public static byte[] CreateSupFrame(BluRaySupPicture pic, Bitmap bmp, double fps, int bottomMargin, ContentAlignment alignment)
        {
            var bm = new NikseBitmap(bmp);
            var colorPalette = GetBitmapPalette(bm);
            var pal = new BluRaySupPalette(colorPalette.Count);
            int k = 0;
            foreach (var kvp in colorPalette)
            {
                pal.SetColor(k, kvp.Key);
                k++;
            }

            byte[] rleBuf = EncodeImage(bm, colorPalette);

            // for some obscure reason, a packet can be a maximum 0xfffc bytes
            // since 13 bytes are needed for the header("PG", PTS, DTS, ID, SIZE)
            // there are only 0xffef bytes available for the packet
            // since the first ODS packet needs an additional 11 bytes for info
            // and the following ODS packets need 4 additional bytes, the
            // first package can store only 0xffe4 RLE buffer bytes and the
            // following packets can store 0xffeb RLE buffer bytes
            int numAddPackets;
            if (rleBuf.Length <= 0xffe4)
                numAddPackets = 0; // no additional packets needed;
            else
                numAddPackets = 1 + (rleBuf.Length - 0xffe4) / 0xffeb;

            // a typical frame consists of 8 packets. It can be enlonged by additional
            // object frames
            int palSize = colorPalette.Count;

            var packetHeader = new byte[]
            {
                0x50, 0x47,             // 0:  "PG"
                0x00, 0x00, 0x00, 0x00, // 2:  PTS - presentation time stamp
                0x00, 0x00, 0x00, 0x00, // 6:  DTS - decoding time stamp
                0x00,                   // 10: segment_type
                0x00, 0x00,             // 11: segment_length (bytes following till next PG)
            };
            var headerPCSStart = new byte[]
            {
                0x00, 0x00, 0x00, 0x00, // 0: video_width, video_height
                0x10,                   // 4: hi nibble: frame_rate (0x10=24p), lo nibble: reserved
                0x00, 0x00,             // 5: composition_number (increased by start and end header)
                0x80,                   // 7: composition_state (0x80: epoch start)
                0x00,                   // 8: palette_update_flag (0x80), 7bit reserved
                0x00,                   // 9: palette_id_ref (0..7)
                0x01,                   // 10: number_of_composition_objects (0..2)
                0x00, 0x00,             // 11: 16bit object_id_ref
                0x00,                   // 13: window_id_ref (0..1)
                0x00,                   // 14: object_cropped_flag: 0x80, forced_on_flag = 0x040, 6bit reserved
                0x00, 0x00, 0x00, 0x00  // 15: composition_object_horizontal_position, composition_object_vertical_position
            };
            var headerPCSEnd = new byte[]
            {
                0x00, 0x00, 0x00, 0x00, // 0: video_width, video_height
                0x10,                   // 4: hi nibble: frame_rate (0x10=24p), lo nibble: reserved
                0x00, 0x00,             // 5: composition_number (increased by start and end header)
                0x00,                   // 7: composition_state (0x00: normal)
                0x00,                   // 8: palette_update_flag (0x80), 7bit reserved
                0x00,                   // 9: palette_id_ref (0..7)
                0x00,                   // 10: number_of_composition_objects (0..2)
            };
            var headerWds = new byte[]
            {
                0x01,                   // 0 : number of windows (currently assumed 1, 0..2 is legal)
                0x00,                   // 1 : window id (0..1)
                0x00, 0x00, 0x00, 0x00, // 2 : x-ofs, y-ofs
                0x00, 0x00, 0x00, 0x00  // 6 : width, height
             };
            var headerODSFirst = new byte[]
            {
                0x00, 0x00,             // 0: object_id
                0x00,                   // 2: object_version_number
                0xC0,                   // 3: first_in_sequence (0x80), last_in_sequence (0x40), 6bits reserved
                0x00, 0x00, 0x00,       // 4: object_data_length - full RLE buffer length (including 4 bytes size info)
                0x00, 0x00, 0x00, 0x00, // 7: object_width, object_height
            };
            var headerODSNext = new byte[]
            {
                0x00, 0x00,             // 0: object_id
                0x00,                   // 2: object_version_number
                0x40,                   // 3: first_in_sequence (0x80), last_in_sequence (0x40), 6bits reserved
            };

            int size = packetHeader.Length * (8 + numAddPackets);
            size += headerPCSStart.Length + headerPCSEnd.Length;
            size += 2 * headerWds.Length + headerODSFirst.Length;
            size += numAddPackets * headerODSNext.Length;
            size += (2 + palSize * 5) /* PDS */;
            size += rleBuf.Length;

            switch (alignment)
            {
                case ContentAlignment.BottomLeft:
                    pic.WindowXOffset = bottomMargin;
                    pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                    break;
                case ContentAlignment.BottomRight:
                    pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                    pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                    break;
                case ContentAlignment.MiddleCenter:
                    pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                    pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                    break;
                case ContentAlignment.MiddleLeft:
                    pic.WindowXOffset = bottomMargin;
                    pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                    break;
                case ContentAlignment.MiddleRight:
                    pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                    pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                    break;
                case ContentAlignment.TopCenter:
                    pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                    pic.WindowYOffset = bottomMargin;
                    break;
                case ContentAlignment.TopLeft:
                    pic.WindowXOffset = bottomMargin;
                    pic.WindowYOffset = bottomMargin;
                    break;
                case ContentAlignment.TopRight:
                    pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                    pic.WindowYOffset = bottomMargin;
                    break;
                default: // ContentAlignment.BottomCenter:
                    pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                    pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                    break;
            }

            int yOfs = pic.WindowYOffset - Core.CropOfsY;
            if (yOfs < 0)
            {
                yOfs = 0;
            }
            else
            {
                int yMax = pic.Height - pic.WindowHeight - 2 * Core.CropOfsY;
                if (yOfs > yMax)
                    yOfs = yMax;
            }

            int h = pic.Height - 2 * Core.CropOfsY;

            byte[] buf = new byte[size];
            int index = 0;

            int fpsId = GetFpsId(fps);

            /* time (in 90kHz resolution) needed to initialize (clear) the screen buffer
                based on the composition pixel rate of 256e6 bit/s - always rounded up */
            int frameInitTime = (pic.Width * pic.Height * 9 + 3199) / 3200; // better use default height here
            /* time (in 90kHz resolution) needed to initialize (clear) the window area
                based on the composition pixel rate of 256e6 bit/s - always rounded up
                Note: no cropping etc. -> window size == image size */
            int windowInitTime = (bm.Width * bm.Height * 9 + 3199) / 3200;
            /* time (in 90kHz resolution) needed to decode the image
                based on the decoding pixel rate of 128e6 bit/s - always rounded up  */
            int imageDecodeTime = (bm.Width * bm.Height * 9 + 1599) / 1600;
            // write PCS start
            packetHeader[10] = 0x16;                                            // ID
            int dts = pic.StartTimeForWrite - (frameInitTime + windowInitTime + imageDecodeTime); //            int dts = pic.StartTimeForWrite - windowInitTime; ????

            ToolBox.SetDWord(packetHeader, 2, pic.StartTimeForWrite);              // PTS
            ToolBox.SetDWord(packetHeader, 6, dts);                             // DTS
            ToolBox.SetWord(packetHeader, 11, headerPCSStart.Length);           // size
            for (int i = 0; i < packetHeader.Length; i++)
                buf[index++] = packetHeader[i];
            ToolBox.SetWord(headerPCSStart, 0, pic.Width);
            ToolBox.SetWord(headerPCSStart, 2, h);                              // cropped height
            ToolBox.SetByte(headerPCSStart, 4, fpsId);
            ToolBox.SetWord(headerPCSStart, 5, pic.CompositionNumber);
            headerPCSStart[14] = (byte)(pic.IsForced ? 0x40 : 0);
            ToolBox.SetWord(headerPCSStart, 15, pic.WindowXOffset);
            ToolBox.SetWord(headerPCSStart, 17, yOfs);
            for (int i = 0; i < headerPCSStart.Length; i++)
                buf[index++] = headerPCSStart[i];

            // write WDS
            packetHeader[10] = 0x17;                                            // ID
            int timeStamp = pic.StartTimeForWrite - windowInitTime;
            ToolBox.SetDWord(packetHeader, 2, timeStamp);                       // PTS (keep DTS)
            ToolBox.SetWord(packetHeader, 11, headerWds.Length);                // size
            for (int i = 0; i < packetHeader.Length; i++)
                buf[index++] = packetHeader[i];
            ToolBox.SetWord(headerWds, 2, pic.WindowXOffset);
            ToolBox.SetWord(headerWds, 4, yOfs);
            ToolBox.SetWord(headerWds, 6, bm.Width);
            ToolBox.SetWord(headerWds, 8, bm.Height);
            for (int i = 0; i < headerWds.Length; i++)
                buf[index++] = headerWds[i];

            // write PDS
            packetHeader[10] = 0x14;                                            // ID
            ToolBox.SetDWord(packetHeader, 2, dts);                             // PTS (=DTS of PCS/WDS)
            ToolBox.SetDWord(packetHeader, 6, 0);                               // DTS (0)
            ToolBox.SetWord(packetHeader, 11, (2 + palSize * 5));               // size
            for (int i = 0; i < packetHeader.Length; i++)
                buf[index++] = packetHeader[i];
            buf[index++] = 0;
            buf[index++] = 0;
            for (int i = 0; i < palSize; i++)
            {
                buf[index++] = (byte)i;                                         // index
                buf[index++] = pal.GetY()[i];                                   // Y
                buf[index++] = pal.GetCr()[i];                                  // Cr
                buf[index++] = pal.GetCb()[i];                                  // Cb
                buf[index++] = pal.GetAlpha()[i];                               // Alpha
            }

            // write first OBJ
            int bufSize = rleBuf.Length;
            int rleIndex = 0;
            if (bufSize > 0xffe4)
                bufSize = 0xffe4;
            packetHeader[10] = 0x15;                                            // ID
            timeStamp = dts + imageDecodeTime;
            ToolBox.SetDWord(packetHeader, 2, timeStamp);                       // PTS
            ToolBox.SetDWord(packetHeader, 6, dts);                             // DTS
            ToolBox.SetWord(packetHeader, 11, headerODSFirst.Length + bufSize); // size
            for (int i = 0; i < packetHeader.Length; i++)
                buf[index++] = packetHeader[i];
            int marker = (int)((numAddPackets == 0) ? 0xC0000000 : 0x80000000);
            ToolBox.SetDWord(headerODSFirst, 3, marker | (rleBuf.Length + 4));
            ToolBox.SetWord(headerODSFirst, 7, bm.Width);
            ToolBox.SetWord(headerODSFirst, 9, bm.Height);
            for (int i = 0; i < headerODSFirst.Length; i++)
                buf[index++] = headerODSFirst[i];
            for (int i = 0; i < bufSize; i++)
                buf[index++] = rleBuf[rleIndex++];

            // write additional OBJ packets
            bufSize = rleBuf.Length - bufSize; // remaining bytes to write
            for (int p = 0; p < numAddPackets; p++)
            {
                int psize = bufSize;
                if (psize > 0xffeb)
                    psize = 0xffeb;
                packetHeader[10] = 0x15;                                            // ID (keep DTS & PTS)
                ToolBox.SetWord(packetHeader, 11, headerODSNext.Length + psize);    // size
                for (int i = 0; i < packetHeader.Length; i++)
                    buf[index++] = packetHeader[i];
                for (int i = 0; i < headerODSNext.Length; i++)
                    buf[index++] = headerODSNext[i];
                for (int i = 0; i < psize; i++)
                    buf[index++] = rleBuf[rleIndex++];
                bufSize -= psize;
            }

            // write END
            packetHeader[10] = 0x80;                                      // ID
            ToolBox.SetDWord(packetHeader, 6, 0);                               // DTS (0) (keep PTS of ODS)
            ToolBox.SetWord(packetHeader, 11, 0);                               // size
            for (int i = 0; i < packetHeader.Length; i++)
                buf[index++] = packetHeader[i];

            // write PCS end
            packetHeader[10] = 0x16;                                            // ID
            ToolBox.SetDWord(packetHeader, 2, pic.EndTimeForWrite);             // PTS
            dts = pic.EndTimeForWrite - 1; //dts = pic.StartTimeForWrite - 1;
            ToolBox.SetDWord(packetHeader, 6, dts);                             // DTS
            ToolBox.SetWord(packetHeader, 11, headerPCSEnd.Length);             // size
            for (int i = 0; i < packetHeader.Length; i++)
                buf[index++] = packetHeader[i];
            ToolBox.SetWord(headerPCSEnd, 0, pic.Width);
            ToolBox.SetWord(headerPCSEnd, 2, h);                                // cropped height
            ToolBox.SetByte(headerPCSEnd, 4, fpsId);
            ToolBox.SetWord(headerPCSEnd, 5, pic.CompositionNumber + 1);
            for (int i = 0; i < headerPCSEnd.Length; i++)
                buf[index++] = headerPCSEnd[i];

            // write WDS
            packetHeader[10] = 0x17;                                            // ID
            timeStamp = pic.EndTimeForWrite - windowInitTime;
            ToolBox.SetDWord(packetHeader, 2, timeStamp);                       // PTS (keep DTS of PCS)
            ToolBox.SetWord(packetHeader, 11, headerWds.Length);                // size
            for (int i = 0; i < packetHeader.Length; i++)
                buf[index++] = packetHeader[i];
            ToolBox.SetWord(headerWds, 2, pic.WindowXOffset);
            ToolBox.SetWord(headerWds, 4, yOfs);
            ToolBox.SetWord(headerWds, 6, bm.Width);
            ToolBox.SetWord(headerWds, 8, bm.Height);
            for (int i = 0; i < headerWds.Length; i++)
                buf[index++] = headerWds[i];

            // write END
            packetHeader[10] = 0x80;                                      // ID
            ToolBox.SetDWord(packetHeader, 2, dts);                             // PTS (DTS of end PCS)
            ToolBox.SetDWord(packetHeader, 6, 0);                               // DTS (0)
            ToolBox.SetWord(packetHeader, 11, 0);                               // size
            for (int i = 0; i < packetHeader.Length; i++)
                buf[index++] = packetHeader[i];

            return buf;
        }
Пример #6
0
            /// <summary>
            /// Decode caption from the input stream
            /// </summary>
            /// <returns>bitmap of the decoded caption</returns>
            public static Bitmap DecodeImage(PcsObject pcs, IList <OdsData> data, List <PaletteInfo> palettes)
            {
                long ticks = DateTime.Now.Ticks;

                if (pcs == null || data == null || data.Count == 0)
                {
                    return(new Bitmap(1, 1));
                }

                int w = data[0].Size.Width;
                int h = data[0].Size.Height;

                var bm = new FastBitmap(new Bitmap(w, h));

                bm.LockImage();
                BluRaySupPalette pal = DecodePalette(palettes);

                int index = 0;
                int ofs   = 0;
                int xpos  = 0;

                byte[] buf = data[0].Fragment.ImageBuffer;
                index = 0;
                do
                {
                    int b = buf[index++] & 0xff;
                    if (b == 0 && index < buf.Length)
                    {
                        b = buf[index++] & 0xff;
                        if (b == 0)
                        {
                            // next line
                            ofs = (ofs / w) * w;
                            if (xpos < w)
                            {
                                ofs += w;
                            }
                            xpos = 0;
                        }
                        else
                        {
                            int size;
                            if ((b & 0xC0) == 0x40)
                            {
                                if (index < buf.Length)
                                {
                                    // 00 4x xx -> xxx zeroes
                                    size = ((b - 0x40) << 8) + (buf[index++] & 0xff);
                                    Color c = Color.FromArgb(pal.GetArgb(0));
                                    for (int i = 0; i < size; i++)
                                    {
                                        PutPixel(bm, ofs++, c);
                                    }
                                    xpos += size;
                                }
                            }
                            else if ((b & 0xC0) == 0x80)
                            {
                                if (index < buf.Length)
                                {
                                    // 00 8x yy -> x times value y
                                    size = (b - 0x80);
                                    b    = buf[index++] & 0xff;
                                    Color c = Color.FromArgb(pal.GetArgb(b));
                                    for (int i = 0; i < size; i++)
                                    {
                                        PutPixel(bm, ofs++, c);
                                    }
                                    xpos += size;
                                }
                            }
                            else if ((b & 0xC0) != 0)
                            {
                                if (index < buf.Length)
                                {
                                    // 00 cx yy zz -> xyy times value z
                                    size = ((b - 0xC0) << 8) + (buf[index++] & 0xff);
                                    b    = buf[index++] & 0xff;
                                    Color c = Color.FromArgb(pal.GetArgb(b));
                                    for (int i = 0; i < size; i++)
                                    {
                                        PutPixel(bm, ofs++, c);
                                    }
                                    xpos += size;
                                }
                            }
                            else
                            {
                                // 00 xx -> xx times 0
                                Color c = Color.FromArgb(pal.GetArgb(0));
                                for (int i = 0; i < b; i++)
                                {
                                    PutPixel(bm, ofs++, c);
                                }
                                xpos += b;
                            }
                        }
                    }
                    else
                    {
                        PutPixel(bm, ofs++, b, pal);
                        xpos++;
                    }
                } while (index < buf.Length);

                bm.UnlockImage();
                return(bm.GetBitmap());
            }
        /**
         * Ctor - construct new (independent) palette from existing one
         * @param p Palette to copy values from
         */
        public BluRaySupPalette(BluRaySupPalette p)
        {
            this.size = p.GetSize();
            this.useBT601 = p.UsesBt601();
            this.r = new byte[this.size];
            this.g = new byte[this.size];
            this.b = new byte[this.size];
            this.a = new byte[this.size];
            this.y = new byte[this.size];
            this.cb = new byte[this.size];
            this.cr = new byte[this.size];

            for (int i = 0; i < this.size; i++)
            {
                this.a[i] = p.a[i];
                this.r[i] = p.r[i];
                this.g[i] = p.g[i];
                this.b[i] = p.b[i];
                this.y[i] = p.y[i];
                this.cb[i] = p.cb[i];
                this.cr[i] = p.cr[i];
            }
        }
Пример #8
0
        /// <summary>
        /// Create the binary stream representation of one caption
        /// </summary>
        /// <param name="pic">SubPicture object containing caption info</param>
        /// <param name="bm">bitmap</param>
        /// <param name="fps">frames per second</param>
        /// <param name="bottomMargin">image bottom margin</param>
        /// <returns>byte buffer containing the binary stream representation of one caption</returns>
        public static byte[] CreateSupFrame(BluRaySupPicture pic, Bitmap bmp, double fps, int bottomMargin, ContentAlignment alignment)
        {
            var bm           = new NikseBitmap(bmp);
            var colorPalette = GetBitmapPalette(bm);
            var pal          = new BluRaySupPalette(colorPalette.Count);
            int k            = 0;

            foreach (var kvp in colorPalette)
            {
                pal.SetColor(k, kvp.Key);
                k++;
            }

            byte[] rleBuf = EncodeImage(bm, colorPalette);

            // for some obscure reason, a packet can be a maximum 0xfffc bytes
            // since 13 bytes are needed for the header("PG", PTS, DTS, ID, SIZE)
            // there are only 0xffef bytes available for the packet
            // since the first ODS packet needs an additional 11 bytes for info
            // and the following ODS packets need 4 additional bytes, the
            // first package can store only 0xffe4 RLE buffer bytes and the
            // following packets can store 0xffeb RLE buffer bytes
            int numAddPackets;

            if (rleBuf.Length <= 0xffe4)
            {
                numAddPackets = 0; // no additional packets needed;
            }
            else
            {
                numAddPackets = 1 + (rleBuf.Length - 0xffe4) / 0xffeb;
            }

            // a typical frame consists of 8 packets. It can be enlonged by additional
            // object frames
            int palSize = colorPalette.Count;

            var packetHeader = new byte[]
            {
                0x50, 0x47,             // 0:  "PG"
                0x00, 0x00, 0x00, 0x00, // 2:  PTS - presentation time stamp
                0x00, 0x00, 0x00, 0x00, // 6:  DTS - decoding time stamp
                0x00,                   // 10: segment_type
                0x00, 0x00,             // 11: segment_length (bytes following till next PG)
            };
            var headerPCSStart = new byte[]
            {
                0x00, 0x00, 0x00, 0x00, // 0: video_width, video_height
                0x10,                   // 4: hi nibble: frame_rate (0x10=24p), lo nibble: reserved
                0x00, 0x00,             // 5: composition_number (increased by start and end header)
                0x80,                   // 7: composition_state (0x80: epoch start)
                0x00,                   // 8: palette_update_flag (0x80), 7bit reserved
                0x00,                   // 9: palette_id_ref (0..7)
                0x01,                   // 10: number_of_composition_objects (0..2)
                0x00, 0x00,             // 11: 16bit object_id_ref
                0x00,                   // 13: window_id_ref (0..1)
                0x00,                   // 14: object_cropped_flag: 0x80, forced_on_flag = 0x040, 6bit reserved
                0x00, 0x00, 0x00, 0x00  // 15: composition_object_horizontal_position, composition_object_vertical_position
            };
            var headerPCSEnd = new byte[]
            {
                0x00, 0x00, 0x00, 0x00, // 0: video_width, video_height
                0x10,                   // 4: hi nibble: frame_rate (0x10=24p), lo nibble: reserved
                0x00, 0x00,             // 5: composition_number (increased by start and end header)
                0x00,                   // 7: composition_state (0x00: normal)
                0x00,                   // 8: palette_update_flag (0x80), 7bit reserved
                0x00,                   // 9: palette_id_ref (0..7)
                0x00,                   // 10: number_of_composition_objects (0..2)
            };
            var headerWds = new byte[]
            {
                0x01,                   // 0 : number of windows (currently assumed 1, 0..2 is legal)
                0x00,                   // 1 : window id (0..1)
                0x00, 0x00, 0x00, 0x00, // 2 : x-ofs, y-ofs
                0x00, 0x00, 0x00, 0x00  // 6 : width, height
            };
            var headerODSFirst = new byte[]
            {
                0x00, 0x00,             // 0: object_id
                0x00,                   // 2: object_version_number
                0xC0,                   // 3: first_in_sequence (0x80), last_in_sequence (0x40), 6bits reserved
                0x00, 0x00, 0x00,       // 4: object_data_length - full RLE buffer length (including 4 bytes size info)
                0x00, 0x00, 0x00, 0x00, // 7: object_width, object_height
            };
            var headerODSNext = new byte[]
            {
                0x00, 0x00,             // 0: object_id
                0x00,                   // 2: object_version_number
                0x40,                   // 3: first_in_sequence (0x80), last_in_sequence (0x40), 6bits reserved
            };

            int size = packetHeader.Length * (8 + numAddPackets);

            size += headerPCSStart.Length + headerPCSEnd.Length;
            size += 2 * headerWds.Length + headerODSFirst.Length;
            size += numAddPackets * headerODSNext.Length;
            size += (2 + palSize * 5) /* PDS */;
            size += rleBuf.Length;

            switch (alignment)
            {
            case ContentAlignment.BottomLeft:
                pic.WindowXOffset = bottomMargin;
                pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                break;

            case ContentAlignment.BottomRight:
                pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                break;

            case ContentAlignment.MiddleCenter:
                pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                break;

            case ContentAlignment.MiddleLeft:
                pic.WindowXOffset = bottomMargin;
                pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                break;

            case ContentAlignment.MiddleRight:
                pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                break;

            case ContentAlignment.TopCenter:
                pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                pic.WindowYOffset = bottomMargin;
                break;

            case ContentAlignment.TopLeft:
                pic.WindowXOffset = bottomMargin;
                pic.WindowYOffset = bottomMargin;
                break;

            case ContentAlignment.TopRight:
                pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                pic.WindowYOffset = bottomMargin;
                break;

            default:     // ContentAlignment.BottomCenter:
                pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                break;
            }

            int yOfs = pic.WindowYOffset - Core.CropOfsY;

            if (yOfs < 0)
            {
                yOfs = 0;
            }
            else
            {
                int yMax = pic.Height - pic.WindowHeight - 2 * Core.CropOfsY;
                if (yOfs > yMax)
                {
                    yOfs = yMax;
                }
            }

            int h = pic.Height - 2 * Core.CropOfsY;

            byte[] buf   = new byte[size];
            int    index = 0;

            int fpsId = GetFpsId(fps);

            /* time (in 90kHz resolution) needed to initialize (clear) the screen buffer
             *  based on the composition pixel rate of 256e6 bit/s - always rounded up */
            int frameInitTime = (pic.Width * pic.Height * 9 + 3199) / 3200; // better use default height here

            /* time (in 90kHz resolution) needed to initialize (clear) the window area
             *  based on the composition pixel rate of 256e6 bit/s - always rounded up
             *  Note: no cropping etc. -> window size == image size */
            int windowInitTime = (bm.Width * bm.Height * 9 + 3199) / 3200;

            /* time (in 90kHz resolution) needed to decode the image
             *  based on the decoding pixel rate of 128e6 bit/s - always rounded up  */
            int imageDecodeTime = (bm.Width * bm.Height * 9 + 1599) / 1600;

            // write PCS start
            packetHeader[10] = 0x16;                                                              // ID
            int dts = pic.StartTimeForWrite - (frameInitTime + windowInitTime + imageDecodeTime); //            int dts = pic.StartTimeForWrite - windowInitTime; ????

            ToolBox.SetDWord(packetHeader, 2, pic.StartTimeForWrite);                             // PTS
            ToolBox.SetDWord(packetHeader, 6, dts);                                               // DTS
            ToolBox.SetWord(packetHeader, 11, headerPCSStart.Length);                             // size
            for (int i = 0; i < packetHeader.Length; i++)
            {
                buf[index++] = packetHeader[i];
            }
            ToolBox.SetWord(headerPCSStart, 0, pic.Width);
            ToolBox.SetWord(headerPCSStart, 2, h);                              // cropped height
            ToolBox.SetByte(headerPCSStart, 4, fpsId);
            ToolBox.SetWord(headerPCSStart, 5, pic.CompositionNumber);
            headerPCSStart[14] = (byte)(pic.IsForced ? 0x40 : 0);
            ToolBox.SetWord(headerPCSStart, 15, pic.WindowXOffset);
            ToolBox.SetWord(headerPCSStart, 17, yOfs);
            for (int i = 0; i < headerPCSStart.Length; i++)
            {
                buf[index++] = headerPCSStart[i];
            }

            // write WDS
            packetHeader[10] = 0x17;                                            // ID
            int timeStamp = pic.StartTimeForWrite - windowInitTime;

            ToolBox.SetDWord(packetHeader, 2, timeStamp);                       // PTS (keep DTS)
            ToolBox.SetWord(packetHeader, 11, headerWds.Length);                // size
            for (int i = 0; i < packetHeader.Length; i++)
            {
                buf[index++] = packetHeader[i];
            }
            ToolBox.SetWord(headerWds, 2, pic.WindowXOffset);
            ToolBox.SetWord(headerWds, 4, yOfs);
            ToolBox.SetWord(headerWds, 6, bm.Width);
            ToolBox.SetWord(headerWds, 8, bm.Height);
            for (int i = 0; i < headerWds.Length; i++)
            {
                buf[index++] = headerWds[i];
            }

            // write PDS
            packetHeader[10] = 0x14;                                            // ID
            ToolBox.SetDWord(packetHeader, 2, dts);                             // PTS (=DTS of PCS/WDS)
            ToolBox.SetDWord(packetHeader, 6, 0);                               // DTS (0)
            ToolBox.SetWord(packetHeader, 11, (2 + palSize * 5));               // size
            for (int i = 0; i < packetHeader.Length; i++)
            {
                buf[index++] = packetHeader[i];
            }
            buf[index++] = 0;
            buf[index++] = 0;
            for (int i = 0; i < palSize; i++)
            {
                buf[index++] = (byte)i;                                         // index
                buf[index++] = pal.GetY()[i];                                   // Y
                buf[index++] = pal.GetCr()[i];                                  // Cr
                buf[index++] = pal.GetCb()[i];                                  // Cb
                buf[index++] = pal.GetAlpha()[i];                               // Alpha
            }

            // write first OBJ
            int bufSize  = rleBuf.Length;
            int rleIndex = 0;

            if (bufSize > 0xffe4)
            {
                bufSize = 0xffe4;
            }
            packetHeader[10] = 0x15;                                            // ID
            timeStamp        = dts + imageDecodeTime;
            ToolBox.SetDWord(packetHeader, 2, timeStamp);                       // PTS
            ToolBox.SetDWord(packetHeader, 6, dts);                             // DTS
            ToolBox.SetWord(packetHeader, 11, headerODSFirst.Length + bufSize); // size
            for (int i = 0; i < packetHeader.Length; i++)
            {
                buf[index++] = packetHeader[i];
            }
            int marker = (int)((numAddPackets == 0) ? 0xC0000000 : 0x80000000);

            ToolBox.SetDWord(headerODSFirst, 3, marker | (rleBuf.Length + 4));
            ToolBox.SetWord(headerODSFirst, 7, bm.Width);
            ToolBox.SetWord(headerODSFirst, 9, bm.Height);
            for (int i = 0; i < headerODSFirst.Length; i++)
            {
                buf[index++] = headerODSFirst[i];
            }
            for (int i = 0; i < bufSize; i++)
            {
                buf[index++] = rleBuf[rleIndex++];
            }

            // write additional OBJ packets
            bufSize = rleBuf.Length - bufSize; // remaining bytes to write
            for (int p = 0; p < numAddPackets; p++)
            {
                int psize = bufSize;
                if (psize > 0xffeb)
                {
                    psize = 0xffeb;
                }
                packetHeader[10] = 0x15;                                            // ID (keep DTS & PTS)
                ToolBox.SetWord(packetHeader, 11, headerODSNext.Length + psize);    // size
                for (int i = 0; i < packetHeader.Length; i++)
                {
                    buf[index++] = packetHeader[i];
                }
                for (int i = 0; i < headerODSNext.Length; i++)
                {
                    buf[index++] = headerODSNext[i];
                }
                for (int i = 0; i < psize; i++)
                {
                    buf[index++] = rleBuf[rleIndex++];
                }
                bufSize -= psize;
            }

            // write END
            packetHeader[10] = 0x80;                                      // ID
            ToolBox.SetDWord(packetHeader, 6, 0);                         // DTS (0) (keep PTS of ODS)
            ToolBox.SetWord(packetHeader, 11, 0);                         // size
            for (int i = 0; i < packetHeader.Length; i++)
            {
                buf[index++] = packetHeader[i];
            }

            // write PCS end
            packetHeader[10] = 0x16;                                // ID
            ToolBox.SetDWord(packetHeader, 2, pic.EndTimeForWrite); // PTS
            dts = pic.EndTimeForWrite - 1;                          //dts = pic.StartTimeForWrite - 1;
            ToolBox.SetDWord(packetHeader, 6, dts);                 // DTS
            ToolBox.SetWord(packetHeader, 11, headerPCSEnd.Length); // size
            for (int i = 0; i < packetHeader.Length; i++)
            {
                buf[index++] = packetHeader[i];
            }
            ToolBox.SetWord(headerPCSEnd, 0, pic.Width);
            ToolBox.SetWord(headerPCSEnd, 2, h);                                // cropped height
            ToolBox.SetByte(headerPCSEnd, 4, fpsId);
            ToolBox.SetWord(headerPCSEnd, 5, pic.CompositionNumber + 1);
            for (int i = 0; i < headerPCSEnd.Length; i++)
            {
                buf[index++] = headerPCSEnd[i];
            }

            // write WDS
            packetHeader[10] = 0x17;                                            // ID
            timeStamp        = pic.EndTimeForWrite - windowInitTime;
            ToolBox.SetDWord(packetHeader, 2, timeStamp);                       // PTS (keep DTS of PCS)
            ToolBox.SetWord(packetHeader, 11, headerWds.Length);                // size
            for (int i = 0; i < packetHeader.Length; i++)
            {
                buf[index++] = packetHeader[i];
            }
            ToolBox.SetWord(headerWds, 2, pic.WindowXOffset);
            ToolBox.SetWord(headerWds, 4, yOfs);
            ToolBox.SetWord(headerWds, 6, bm.Width);
            ToolBox.SetWord(headerWds, 8, bm.Height);
            for (int i = 0; i < headerWds.Length; i++)
            {
                buf[index++] = headerWds[i];
            }

            // write END
            packetHeader[10] = 0x80;                                      // ID
            ToolBox.SetDWord(packetHeader, 2, dts);                       // PTS (DTS of end PCS)
            ToolBox.SetDWord(packetHeader, 6, 0);                         // DTS (0)
            ToolBox.SetWord(packetHeader, 11, 0);                         // size
            for (int i = 0; i < packetHeader.Length; i++)
            {
                buf[index++] = packetHeader[i];
            }

            return(buf);
        }
Пример #9
0
        /// <summary>
        /// Create the binary stream representation of one caption
        /// </summary>
        /// <param name="pic">SubPicture object containing caption info</param>
        /// <param name="bm">bitmap</param>
        /// <param name="fps">frames per second</param>
        /// <param name="bottomMargin">image bottom margin</param>
        /// <returns>byte buffer containing the binary stream representation of one caption</returns>
        public static byte[] CreateSupFrame(BluRaySupPicture pic, Bitmap bmp, double fps, int bottomMargin, ContentAlignment alignment)
        {
            var bm           = new NikseBitmap(bmp);
            var colorPalette = GetBitmapPalette(bm);
            var pal          = new BluRaySupPalette(colorPalette.Count);
            int k            = 0;

            foreach (var kvp in colorPalette)
            {
                pal.SetColor(k, kvp.Key);
                k++;
            }

            byte[] rleBuf = EncodeImage(bm, colorPalette);

            // for some obscure reason, a packet can be a maximum 0xfffc bytes
            // since 13 bytes are needed for the header("PG", PTS, DTS, ID, SIZE)
            // there are only 0xffef bytes available for the packet
            // since the first ODS packet needs an additional 11 bytes for info
            // and the following ODS packets need 4 additional bytes, the
            // first package can store only 0xffe4 RLE buffer bytes and the
            // following packets can store 0xffeb RLE buffer bytes
            int numAddPackets;

            if (rleBuf.Length <= 0xffe4)
            {
                numAddPackets = 0; // no additional packets needed;
            }
            else
            {
                numAddPackets = 1 + (rleBuf.Length - 0xffe4) / 0xffeb;
            }

            // a typical frame consists of 8 packets. It can be enlonged by additional
            // object frames
            int palSize = colorPalette.Count;

            int size = PacketHeader.Length * (8 + numAddPackets);

            size += HeaderPCSStart.Length + HeaderPCSEnd.Length;
            size += 2 * HeaderWds.Length + HeaderODSFirst.Length;
            size += numAddPackets * HeaderODSNext.Length;
            size += (2 + palSize * 5) /* PDS */;
            size += rleBuf.Length;

            switch (alignment)
            {
            case ContentAlignment.BottomLeft:
                pic.WindowXOffset = bottomMargin;
                pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                break;

            case ContentAlignment.BottomRight:
                pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                break;

            case ContentAlignment.MiddleCenter:
                pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                break;

            case ContentAlignment.MiddleLeft:
                pic.WindowXOffset = bottomMargin;
                pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                break;

            case ContentAlignment.MiddleRight:
                pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                break;

            case ContentAlignment.TopCenter:
                pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                pic.WindowYOffset = bottomMargin;
                break;

            case ContentAlignment.TopLeft:
                pic.WindowXOffset = bottomMargin;
                pic.WindowYOffset = bottomMargin;
                break;

            case ContentAlignment.TopRight:
                pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                pic.WindowYOffset = bottomMargin;
                break;

            default:     // ContentAlignment.BottomCenter:
                pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                break;
            }

            int yOfs = pic.WindowYOffset - Core.CropOfsY;

            if (yOfs < 0)
            {
                yOfs = 0;
            }
            else
            {
                int yMax = pic.Height - pic.WindowHeight - 2 * Core.CropOfsY;
                if (yOfs > yMax)
                {
                    yOfs = yMax;
                }
            }

            int h = pic.Height - 2 * Core.CropOfsY;

            byte[] buf   = new byte[size];
            int    index = 0;


            int fpsId = GetFpsId(fps);

            /* time (in 90kHz resolution) needed to initialize (clear) the screen buffer
             *  based on the composition pixel rate of 256e6 bit/s - always rounded up */
            int frameInitTime = (pic.Width * pic.Height * 9 + 3199) / 3200; // better use default height here

            /* time (in 90kHz resolution) needed to initialize (clear) the window area
             *  based on the composition pixel rate of 256e6 bit/s - always rounded up
             *  Note: no cropping etc. -> window size == image size */
            int windowInitTime = (bm.Width * bm.Height * 9 + 3199) / 3200;

            /* time (in 90kHz resolution) needed to decode the image
             *  based on the decoding pixel rate of 128e6 bit/s - always rounded up  */
            int imageDecodeTime = (bm.Width * bm.Height * 9 + 1599) / 1600;

            // write PCS start
            PacketHeader[10] = 0x16;                                                              // ID
            int dts = pic.StartTimeForWrite - (frameInitTime + windowInitTime + imageDecodeTime); //            int dts = pic.StartTimeForWrite - windowInitTime; ????

            ToolBox.SetDWord(PacketHeader, 2, pic.StartTimeForWrite);                             // PTS
            ToolBox.SetDWord(PacketHeader, 6, dts);                                               // DTS
            ToolBox.SetWord(PacketHeader, 11, HeaderPCSStart.Length);                             // size
            for (int i = 0; i < PacketHeader.Length; i++)
            {
                buf[index++] = PacketHeader[i];
            }
            ToolBox.SetWord(HeaderPCSStart, 0, pic.Width);
            ToolBox.SetWord(HeaderPCSStart, 2, h);                              // cropped height
            ToolBox.SetByte(HeaderPCSStart, 4, fpsId);
            ToolBox.SetWord(HeaderPCSStart, 5, pic.CompositionNumber);
            HeaderPCSStart[14] = (byte)(pic.IsForced ? 0x40 : 0);
            ToolBox.SetWord(HeaderPCSStart, 15, pic.WindowXOffset);
            ToolBox.SetWord(HeaderPCSStart, 17, yOfs);
            for (int i = 0; i < HeaderPCSStart.Length; i++)
            {
                buf[index++] = HeaderPCSStart[i];
            }

            // write WDS
            PacketHeader[10] = 0x17;                                            // ID
            int timeStamp = pic.StartTimeForWrite - windowInitTime;

            ToolBox.SetDWord(PacketHeader, 2, timeStamp);                       // PTS (keep DTS)
            ToolBox.SetWord(PacketHeader, 11, HeaderWds.Length);                // size
            for (int i = 0; i < PacketHeader.Length; i++)
            {
                buf[index++] = PacketHeader[i];
            }
            ToolBox.SetWord(HeaderWds, 2, pic.WindowXOffset);
            ToolBox.SetWord(HeaderWds, 4, yOfs);
            ToolBox.SetWord(HeaderWds, 6, bm.Width);
            ToolBox.SetWord(HeaderWds, 8, bm.Height);
            for (int i = 0; i < HeaderWds.Length; i++)
            {
                buf[index++] = HeaderWds[i];
            }

            // write PDS
            PacketHeader[10] = 0x14;                                            // ID
            ToolBox.SetDWord(PacketHeader, 2, dts);                             // PTS (=DTS of PCS/WDS)
            ToolBox.SetDWord(PacketHeader, 6, 0);                               // DTS (0)
            ToolBox.SetWord(PacketHeader, 11, (2 + palSize * 5));               // size
            for (int i = 0; i < PacketHeader.Length; i++)
            {
                buf[index++] = PacketHeader[i];
            }
            buf[index++] = 0;
            buf[index++] = 0;
            for (int i = 0; i < palSize; i++)
            {
                buf[index++] = (byte)i;                                         // index
                buf[index++] = pal.GetY()[i];                                   // Y
                buf[index++] = pal.GetCr()[i];                                  // Cr
                buf[index++] = pal.GetCb()[i];                                  // Cb
                buf[index++] = pal.GetAlpha()[i];                               // Alpha
            }

            // write first OBJ
            int bufSize  = rleBuf.Length;
            int rleIndex = 0;

            if (bufSize > 0xffe4)
            {
                bufSize = 0xffe4;
            }
            PacketHeader[10] = 0x15;                                            // ID
            timeStamp        = dts + imageDecodeTime;
            ToolBox.SetDWord(PacketHeader, 2, timeStamp);                       // PTS
            ToolBox.SetDWord(PacketHeader, 6, dts);                             // DTS
            ToolBox.SetWord(PacketHeader, 11, HeaderODSFirst.Length + bufSize); // size
            for (int i = 0; i < PacketHeader.Length; i++)
            {
                buf[index++] = PacketHeader[i];
            }
            int marker = (int)((numAddPackets == 0) ? 0xC0000000 : 0x80000000);

            ToolBox.SetDWord(HeaderODSFirst, 3, marker | (rleBuf.Length + 4));
            ToolBox.SetWord(HeaderODSFirst, 7, bm.Width);
            ToolBox.SetWord(HeaderODSFirst, 9, bm.Height);
            for (int i = 0; i < HeaderODSFirst.Length; i++)
            {
                buf[index++] = HeaderODSFirst[i];
            }
            for (int i = 0; i < bufSize; i++)
            {
                buf[index++] = rleBuf[rleIndex++];
            }

            // write additional OBJ packets
            bufSize = rleBuf.Length - bufSize; // remaining bytes to write
            for (int p = 0; p < numAddPackets; p++)
            {
                int psize = bufSize;
                if (psize > 0xffeb)
                {
                    psize = 0xffeb;
                }
                PacketHeader[10] = 0x15;                                            // ID (keep DTS & PTS)
                ToolBox.SetWord(PacketHeader, 11, HeaderODSNext.Length + psize);    // size
                for (int i = 0; i < PacketHeader.Length; i++)
                {
                    buf[index++] = PacketHeader[i];
                }
                for (int i = 0; i < HeaderODSNext.Length; i++)
                {
                    buf[index++] = HeaderODSNext[i];
                }
                for (int i = 0; i < psize; i++)
                {
                    buf[index++] = rleBuf[rleIndex++];
                }
                bufSize -= psize;
            }

            // write END
            PacketHeader[10] = 0x80;                                      // ID
            ToolBox.SetDWord(PacketHeader, 6, 0);                         // DTS (0) (keep PTS of ODS)
            ToolBox.SetWord(PacketHeader, 11, 0);                         // size
            for (int i = 0; i < PacketHeader.Length; i++)
            {
                buf[index++] = PacketHeader[i];
            }

            // write PCS end
            PacketHeader[10] = 0x16;                                // ID
            ToolBox.SetDWord(PacketHeader, 2, pic.EndTimeForWrite); // PTS
            dts = pic.EndTimeForWrite - 1;                          //dts = pic.StartTimeForWrite - 1;
            ToolBox.SetDWord(PacketHeader, 6, dts);                 // DTS
            ToolBox.SetWord(PacketHeader, 11, HeaderPCSEnd.Length); // size
            for (int i = 0; i < PacketHeader.Length; i++)
            {
                buf[index++] = PacketHeader[i];
            }
            ToolBox.SetWord(HeaderPCSEnd, 0, pic.Width);
            ToolBox.SetWord(HeaderPCSEnd, 2, h);                                // cropped height
            ToolBox.SetByte(HeaderPCSEnd, 4, fpsId);
            ToolBox.SetWord(HeaderPCSEnd, 5, pic.CompositionNumber + 1);
            for (int i = 0; i < HeaderPCSEnd.Length; i++)
            {
                buf[index++] = HeaderPCSEnd[i];
            }

            // write WDS
            PacketHeader[10] = 0x17;                                            // ID
            timeStamp        = pic.EndTimeForWrite - windowInitTime;
            ToolBox.SetDWord(PacketHeader, 2, timeStamp);                       // PTS (keep DTS of PCS)
            ToolBox.SetWord(PacketHeader, 11, HeaderWds.Length);                // size
            for (int i = 0; i < PacketHeader.Length; i++)
            {
                buf[index++] = PacketHeader[i];
            }
            ToolBox.SetWord(HeaderWds, 2, pic.WindowXOffset);
            ToolBox.SetWord(HeaderWds, 4, yOfs);
            ToolBox.SetWord(HeaderWds, 6, bm.Width);
            ToolBox.SetWord(HeaderWds, 8, bm.Height);
            for (int i = 0; i < HeaderWds.Length; i++)
            {
                buf[index++] = HeaderWds[i];
            }

            // write END
            PacketHeader[10] = 0x80;                                      // ID
            ToolBox.SetDWord(PacketHeader, 2, dts);                       // PTS (DTS of end PCS)
            ToolBox.SetDWord(PacketHeader, 6, 0);                         // DTS (0)
            ToolBox.SetWord(PacketHeader, 11, 0);                         // size
            for (int i = 0; i < PacketHeader.Length; i++)
            {
                buf[index++] = PacketHeader[i];
            }

            return(buf);
        }
Пример #10
0
        /// <summary>
        /// Create the binary stream representation of one caption
        /// </summary>
        /// <param name="pic">SubPicture object containing caption info</param>
        /// <param name="bm">bitmap</param>
        /// <param name="fps">frames per second</param>
        /// <param name="bottomMargin">image bottom margin</param>
        /// <returns>byte buffer containing the binary stream representation of one caption</returns>
        public static byte[] CreateSupFrame(BluRaySupPicture pic, Bitmap bmp, double fps, int bottomMargin, ContentAlignment alignment)
        {
            var bm = new NikseBitmap(bmp);
            var colorPalette = GetBitmapPalette(bm);
            var pal = new BluRaySupPalette(colorPalette.Count);
            int k = 0;
            foreach (var kvp in colorPalette)
            {
                pal.SetColor(k, kvp.Key);
                k++;
            }

            byte[] rleBuf = EncodeImage(bm, colorPalette);

            // for some obscure reason, a packet can be a maximum 0xfffc bytes
            // since 13 bytes are needed for the header("PG", PTS, DTS, ID, SIZE)
            // there are only 0xffef bytes available for the packet
            // since the first ODS packet needs an additional 11 bytes for info
            // and the following ODS packets need 4 additional bytes, the
            // first package can store only 0xffe4 RLE buffer bytes and the
            // following packets can store 0xffeb RLE buffer bytes
            int numAddPackets;
            if (rleBuf.Length <= 0xffe4)
                numAddPackets = 0; // no additional packets needed;
            else
                numAddPackets = 1 + (rleBuf.Length - 0xffe4) / 0xffeb;

            // a typical frame consists of 8 packets. It can be enlonged by additional
            // object frames
            int palSize = colorPalette.Count;

            int size = PacketHeader.Length * (8 + numAddPackets);
            size += HeaderPCSStart.Length + HeaderPCSEnd.Length;
            size += 2 * HeaderWds.Length + HeaderODSFirst.Length;
            size += numAddPackets * HeaderODSNext.Length;
            size += (2 + palSize * 5) /* PDS */;
            size += rleBuf.Length;

            switch (alignment)
            {
                case ContentAlignment.BottomLeft:
                    pic.WindowXOffset = bottomMargin;
                    pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                    break;
                case ContentAlignment.BottomRight:
                    pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                    pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                    break;
                case ContentAlignment.MiddleCenter:
                    pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                    pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                    break;
                case ContentAlignment.MiddleLeft:
                    pic.WindowXOffset = bottomMargin;
                    pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                    break;
                case ContentAlignment.MiddleRight:
                    pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                    pic.WindowYOffset = (pic.Height - bm.Height) / 2;
                    break;
                case ContentAlignment.TopCenter:
                    pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                    pic.WindowYOffset = bottomMargin;
                    break;
                case ContentAlignment.TopLeft:
                    pic.WindowXOffset = bottomMargin;
                    pic.WindowYOffset = bottomMargin;
                    break;
                case ContentAlignment.TopRight:
                    pic.WindowXOffset = pic.Width - bm.Width - bottomMargin;
                    pic.WindowYOffset = bottomMargin;
                    break;
                default: // ContentAlignment.BottomCenter:
                    pic.WindowXOffset = (pic.Width - bm.Width) / 2;
                    pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin);
                    break;
            }

            int yOfs = pic.WindowYOffset - Core.CropOfsY;
            if (yOfs < 0)
            {
                yOfs = 0;
            }
            else
            {
                int yMax = pic.Height - pic.WindowHeight - 2 * Core.CropOfsY;
                if (yOfs > yMax)
                    yOfs = yMax;
            }

            int h = pic.Height - 2 * Core.CropOfsY;

            byte[] buf = new byte[size];
            int index = 0;


            int fpsId = GetFpsId(fps);

            /* time (in 90kHz resolution) needed to initialize (clear) the screen buffer
                based on the composition pixel rate of 256e6 bit/s - always rounded up */
            int frameInitTime = (pic.Width * pic.Height * 9 + 3199) / 3200; // better use default height here
            /* time (in 90kHz resolution) needed to initialize (clear) the window area
                based on the composition pixel rate of 256e6 bit/s - always rounded up
                Note: no cropping etc. -> window size == image size */
            int windowInitTime = (bm.Width * bm.Height * 9 + 3199) / 3200;
            /* time (in 90kHz resolution) needed to decode the image
                based on the decoding pixel rate of 128e6 bit/s - always rounded up  */
            int imageDecodeTime = (bm.Width * bm.Height * 9 + 1599) / 1600;
            // write PCS start
            PacketHeader[10] = 0x16;                                            // ID
            int dts = pic.StartTimeForWrite - (frameInitTime + windowInitTime + imageDecodeTime); //            int dts = pic.StartTimeForWrite - windowInitTime; ????

            ToolBox.SetDWord(PacketHeader, 2, pic.StartTimeForWrite);              // PTS
            ToolBox.SetDWord(PacketHeader, 6, dts);                             // DTS
            ToolBox.SetWord(PacketHeader, 11, HeaderPCSStart.Length);           // size
            for (int i = 0; i < PacketHeader.Length; i++)
                buf[index++] = PacketHeader[i];
            ToolBox.SetWord(HeaderPCSStart, 0, pic.Width);
            ToolBox.SetWord(HeaderPCSStart, 2, h);                              // cropped height
            ToolBox.SetByte(HeaderPCSStart, 4, fpsId);
            ToolBox.SetWord(HeaderPCSStart, 5, pic.CompositionNumber);
            HeaderPCSStart[14] = (byte)(pic.IsForced ? 0x40 : 0);
            ToolBox.SetWord(HeaderPCSStart, 15, pic.WindowXOffset);
            ToolBox.SetWord(HeaderPCSStart, 17, yOfs);
            for (int i = 0; i < HeaderPCSStart.Length; i++)
                buf[index++] = HeaderPCSStart[i];

            // write WDS
            PacketHeader[10] = 0x17;                                            // ID
            int timeStamp = pic.StartTimeForWrite - windowInitTime;
            ToolBox.SetDWord(PacketHeader, 2, timeStamp);                       // PTS (keep DTS)
            ToolBox.SetWord(PacketHeader, 11, HeaderWds.Length);                // size
            for (int i = 0; i < PacketHeader.Length; i++)
                buf[index++] = PacketHeader[i];
            ToolBox.SetWord(HeaderWds, 2, pic.WindowXOffset);
            ToolBox.SetWord(HeaderWds, 4, yOfs);
            ToolBox.SetWord(HeaderWds, 6, bm.Width);
            ToolBox.SetWord(HeaderWds, 8, bm.Height);
            for (int i = 0; i < HeaderWds.Length; i++)
                buf[index++] = HeaderWds[i];

            // write PDS
            PacketHeader[10] = 0x14;                                            // ID
            ToolBox.SetDWord(PacketHeader, 2, dts);                             // PTS (=DTS of PCS/WDS)
            ToolBox.SetDWord(PacketHeader, 6, 0);                               // DTS (0)
            ToolBox.SetWord(PacketHeader, 11, (2 + palSize * 5));               // size
            for (int i = 0; i < PacketHeader.Length; i++)
                buf[index++] = PacketHeader[i];
            buf[index++] = 0;
            buf[index++] = 0;
            for (int i = 0; i < palSize; i++)
            {
                buf[index++] = (byte)i;                                         // index
                buf[index++] = pal.GetY()[i];                                   // Y
                buf[index++] = pal.GetCr()[i];                                  // Cr
                buf[index++] = pal.GetCb()[i];                                  // Cb
                buf[index++] = pal.GetAlpha()[i];                               // Alpha
            }

            // write first OBJ
            int bufSize = rleBuf.Length;
            int rleIndex = 0;
            if (bufSize > 0xffe4)
                bufSize = 0xffe4;
            PacketHeader[10] = 0x15;                                            // ID
            timeStamp = dts + imageDecodeTime;
            ToolBox.SetDWord(PacketHeader, 2, timeStamp);                       // PTS
            ToolBox.SetDWord(PacketHeader, 6, dts);                             // DTS
            ToolBox.SetWord(PacketHeader, 11, HeaderODSFirst.Length + bufSize); // size
            for (int i = 0; i < PacketHeader.Length; i++)
                buf[index++] = PacketHeader[i];
            int marker = (int)((numAddPackets == 0) ? 0xC0000000 : 0x80000000);
            ToolBox.SetDWord(HeaderODSFirst, 3, marker | (rleBuf.Length + 4));
            ToolBox.SetWord(HeaderODSFirst, 7, bm.Width);
            ToolBox.SetWord(HeaderODSFirst, 9, bm.Height);
            for (int i = 0; i < HeaderODSFirst.Length; i++)
                buf[index++] = HeaderODSFirst[i];
            for (int i = 0; i < bufSize; i++)
                buf[index++] = rleBuf[rleIndex++];

            // write additional OBJ packets
            bufSize = rleBuf.Length - bufSize; // remaining bytes to write
            for (int p = 0; p < numAddPackets; p++)
            {
                int psize = bufSize;
                if (psize > 0xffeb)
                    psize = 0xffeb;
                PacketHeader[10] = 0x15;                                            // ID (keep DTS & PTS)
                ToolBox.SetWord(PacketHeader, 11, HeaderODSNext.Length + psize);    // size
                for (int i = 0; i < PacketHeader.Length; i++)
                    buf[index++] = PacketHeader[i];
                for (int i = 0; i < HeaderODSNext.Length; i++)
                    buf[index++] = HeaderODSNext[i];
                for (int i = 0; i < psize; i++)
                    buf[index++] = rleBuf[rleIndex++];
                bufSize -= psize;
            }

            // write END
            PacketHeader[10] = 0x80;                                      // ID
            ToolBox.SetDWord(PacketHeader, 6, 0);                               // DTS (0) (keep PTS of ODS)
            ToolBox.SetWord(PacketHeader, 11, 0);                               // size
            for (int i = 0; i < PacketHeader.Length; i++)
                buf[index++] = PacketHeader[i];

            // write PCS end
            PacketHeader[10] = 0x16;                                            // ID
            ToolBox.SetDWord(PacketHeader, 2, pic.EndTimeForWrite);             // PTS
            dts = pic.EndTimeForWrite - 1; //dts = pic.StartTimeForWrite - 1;
            ToolBox.SetDWord(PacketHeader, 6, dts);                             // DTS
            ToolBox.SetWord(PacketHeader, 11, HeaderPCSEnd.Length);             // size
            for (int i = 0; i < PacketHeader.Length; i++)
                buf[index++] = PacketHeader[i];
            ToolBox.SetWord(HeaderPCSEnd, 0, pic.Width);
            ToolBox.SetWord(HeaderPCSEnd, 2, h);                                // cropped height
            ToolBox.SetByte(HeaderPCSEnd, 4, fpsId);
            ToolBox.SetWord(HeaderPCSEnd, 5, pic.CompositionNumber + 1);
            for (int i = 0; i < HeaderPCSEnd.Length; i++)
                buf[index++] = HeaderPCSEnd[i];

            // write WDS
            PacketHeader[10] = 0x17;                                            // ID
            timeStamp = pic.EndTimeForWrite - windowInitTime;
            ToolBox.SetDWord(PacketHeader, 2, timeStamp);                       // PTS (keep DTS of PCS)
            ToolBox.SetWord(PacketHeader, 11, HeaderWds.Length);                // size
            for (int i = 0; i < PacketHeader.Length; i++)
                buf[index++] = PacketHeader[i];
            ToolBox.SetWord(HeaderWds, 2, pic.WindowXOffset);
            ToolBox.SetWord(HeaderWds, 4, yOfs);
            ToolBox.SetWord(HeaderWds, 6, bm.Width);
            ToolBox.SetWord(HeaderWds, 8, bm.Height);
            for (int i = 0; i < HeaderWds.Length; i++)
                buf[index++] = HeaderWds[i];

            // write END
            PacketHeader[10] = 0x80;                                      // ID
            ToolBox.SetDWord(PacketHeader, 2, dts);                             // PTS (DTS of end PCS)
            ToolBox.SetDWord(PacketHeader, 6, 0);                               // DTS (0)
            ToolBox.SetWord(PacketHeader, 11, 0);                               // size
            for (int i = 0; i < PacketHeader.Length; i++)
                buf[index++] = PacketHeader[i];

            return buf;
        }