public static BluRaySupPalette DecodePalette(IList <PaletteInfo> paletteInfos) { var palette = new BluRaySupPalette(256); // by definition, index 0xff is always completely transparent // also all entries must be fully transparent after initialization if (paletteInfos.Count == 0) { return(palette); } // always use last palette var p = paletteInfos[paletteInfos.Count - 1]; var fadeOut = false; var index = 0; for (var i = 0; i < p.PaletteSize; i++) { // each palette entry consists of 5 bytes var palIndex = p.PaletteBuffer[index]; var y = p.PaletteBuffer[++index]; var cr = p.PaletteBuffer[++index]; var cb = p.PaletteBuffer[++index]; var alpha = p.PaletteBuffer[++index]; var 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); }
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; }
/// <summary> /// Create the binary stream representation of one caption /// </summary> /// <param name="pic">SubPicture object containing caption info - note that first Composition Number should be 0, then 2, 4, 8, etc.</param> /// <param name="bmp">Bitmap</param> /// <param name="fps">Frames per second</param> /// <param name="bottomMargin">Image bottom margin</param> /// <param name="leftOrRightMargin">Image left/right margin</param> /// <param name="alignment">Alignment of image</param> /// <param name="overridePosition">Position that overrides alignment</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, int leftOrRightMargin, ContentAlignment alignment, Point? overridePosition = null) { 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 = leftOrRightMargin; pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin); break; case ContentAlignment.BottomRight: pic.WindowXOffset = pic.Width - bm.Width - bottomMargin; pic.WindowYOffset = pic.Height - (bm.Height + leftOrRightMargin); break; case ContentAlignment.MiddleCenter: pic.WindowXOffset = (pic.Width - bm.Width) / 2; pic.WindowYOffset = (pic.Height - bm.Height) / 2; break; case ContentAlignment.MiddleLeft: pic.WindowXOffset = leftOrRightMargin; pic.WindowYOffset = (pic.Height - bm.Height) / 2; break; case ContentAlignment.MiddleRight: pic.WindowXOffset = pic.Width - bm.Width - leftOrRightMargin; 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 = leftOrRightMargin; pic.WindowYOffset = bottomMargin; break; case ContentAlignment.TopRight: pic.WindowXOffset = pic.Width - bm.Width - leftOrRightMargin; pic.WindowYOffset = bottomMargin; break; default: // ContentAlignment.BottomCenter: pic.WindowXOffset = (pic.Width - bm.Width) / 2; pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin); break; } if (overridePosition != null && overridePosition.Value.X >= 0 && overridePosition.Value.X < bm.Width && overridePosition.Value.Y >= 0 && overridePosition.Value.Y < bm.Height) { pic.WindowXOffset = overridePosition.Value.X; pic.WindowYOffset = overridePosition.Value.Y; } 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; var 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; }
/// <summary> /// Create the binary stream representation of one caption /// </summary> /// <param name="pic">SubPicture object containing caption info - note that first Composition Number should be 0, then 2, 4, 8, etc.</param> /// <param name="bmp">Bitmap</param> /// <param name="fps">Frames per second</param> /// <param name="bottomMargin">Image bottom margin</param> /// <param name="leftOrRightMargin">Image left/right margin</param> /// <param name="alignment">Alignment of image</param> /// <param name="overridePosition">Position that overrides alignment</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, int leftOrRightMargin, ContentAlignment alignment, Point?overridePosition = null) { 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 = leftOrRightMargin; pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin); break; case ContentAlignment.BottomRight: pic.WindowXOffset = pic.Width - bm.Width - bottomMargin; pic.WindowYOffset = pic.Height - (bm.Height + leftOrRightMargin); break; case ContentAlignment.MiddleCenter: pic.WindowXOffset = (pic.Width - bm.Width) / 2; pic.WindowYOffset = (pic.Height - bm.Height) / 2; break; case ContentAlignment.MiddleLeft: pic.WindowXOffset = leftOrRightMargin; pic.WindowYOffset = (pic.Height - bm.Height) / 2; break; case ContentAlignment.MiddleRight: pic.WindowXOffset = pic.Width - bm.Width - leftOrRightMargin; 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 = leftOrRightMargin; pic.WindowYOffset = bottomMargin; break; case ContentAlignment.TopRight: pic.WindowXOffset = pic.Width - bm.Width - leftOrRightMargin; pic.WindowYOffset = bottomMargin; break; default: // ContentAlignment.BottomCenter: pic.WindowXOffset = (pic.Width - bm.Width) / 2; pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin); break; } if (overridePosition != null && overridePosition.Value.X >= 0 && overridePosition.Value.X < bm.Width && overridePosition.Value.Y >= 0 && overridePosition.Value.Y < bm.Height) { pic.WindowXOffset = overridePosition.Value.X; pic.WindowYOffset = overridePosition.Value.Y; } 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; var 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); }