public void BluRaySupWriteAndReadTwoBitmaps() { var fileName = Guid.NewGuid() + ".sup"; using (var binarySubtitleFile = new FileStream(fileName, FileMode.Create)) { var brSub = new BluRaySupPicture { StartTime = 0, EndTime = 1000, Width = 1080, Height = 720 }; var buffer = BluRaySupPicture.CreateSupFrame(brSub, new Bitmap(100, 20), 10, 25, 10, ContentAlignment.BottomCenter); binarySubtitleFile.Write(buffer, 0, buffer.Length); brSub.StartTime = 2000; brSub.EndTime = 3000; buffer = BluRaySupPicture.CreateSupFrame(brSub, new Bitmap(100, 20), 10, 25, 10, ContentAlignment.BottomCenter); binarySubtitleFile.Write(buffer, 0, buffer.Length); } var log = new StringBuilder(); var subtitles = BluRaySupParser.ParseBluRaySup(fileName, log); Assert.AreEqual(2, subtitles.Count); }
/// <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; }
private static void MakeBluRaySupImage(MakeBitmapParameter param) { var brSub = new BluRaySupPicture { StartTime = (long)param.P.StartTime.TotalMilliseconds, EndTime = (long)param.P.EndTime.TotalMilliseconds, Width = param.ScreenWidth, Height = param.ScreenHeight, IsForced = param.Forced }; if (param.FullFrame) { var nbmp = new NikseBitmap(param.Bitmap); nbmp.ReplaceTransparentWith(param.FullFrameBackgroundcolor); using (var bmp = nbmp.GetBitmap()) { int top = param.ScreenHeight - (param.Bitmap.Height + param.BottomMargin); int left = (param.ScreenWidth - param.Bitmap.Width) / 2; var b = new NikseBitmap(param.ScreenWidth, param.ScreenHeight); { b.Fill(param.FullFrameBackgroundcolor); using (var fullSize = b.GetBitmap()) { if (param.Alignment == ContentAlignment.BottomLeft || param.Alignment == ContentAlignment.MiddleLeft || param.Alignment == ContentAlignment.TopLeft) left = param.LeftRightMargin; else if (param.Alignment == ContentAlignment.BottomRight || param.Alignment == ContentAlignment.MiddleRight || param.Alignment == ContentAlignment.TopRight) left = param.ScreenWidth - param.Bitmap.Width - param.LeftRightMargin; if (param.Alignment == ContentAlignment.TopLeft || param.Alignment == ContentAlignment.TopCenter || param.Alignment == ContentAlignment.TopRight) top = param.BottomMargin; if (param.Alignment == ContentAlignment.MiddleLeft || param.Alignment == ContentAlignment.MiddleCenter || param.Alignment == ContentAlignment.MiddleRight) top = param.ScreenHeight - (param.Bitmap.Height / 2); using (var g = Graphics.FromImage(fullSize)) { g.DrawImage(bmp, left, top); g.Dispose(); } param.Buffer = BluRaySupPicture.CreateSupFrame(brSub, fullSize, param.FramesPerSeconds, 0, 0, ContentAlignment.BottomCenter); } } } } else { param.Buffer = BluRaySupPicture.CreateSupFrame(brSub, param.Bitmap, param.FramesPerSeconds, param.BottomMargin, param.LeftRightMargin, param.Alignment); } }
/// <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); }