Package bdsup2sub.supstream.bd

Source Code of bdsup2sub.supstream.bd.SupBDWriter

package bdsup2sub.supstream.bd;

import bdsup2sub.bitmap.Bitmap;
import bdsup2sub.bitmap.Palette;
import bdsup2sub.core.Configuration;
import bdsup2sub.core.Framerate;
import bdsup2sub.core.Logger;
import bdsup2sub.supstream.SubPicture;
import bdsup2sub.tools.QuantizeFilter;

import java.util.ArrayList;
import java.util.Iterator;

import static bdsup2sub.utils.ByteUtils.setByte;
import static bdsup2sub.utils.ByteUtils.setDWord;
import static bdsup2sub.utils.ByteUtils.setWord;

public final class SupBDWriter {

    private static final Configuration configuration = Configuration.getInstance();
    private static final Logger logger = Logger.getInstance();

    private static final byte PACKET_HEADER[] = {
            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)
    };

    private static final byte HEADER_PCS_START[] = {
            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)
            (byte)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
    };

    private static final byte HEADER_PCS_END[] = {
            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)
    };


    private static final byte HEADER_ODS_FIRST[] = {
            0x00, 0x00,             // 0: object_id
            0x00,                   // 2: object_version_number
            (byte)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
    };

    private static final byte HEADER_ODS_NEXT[] = {
            0x00, 0x00,             // 0: object_id
            0x00,                   // 2: object_version_number
            (byte)0x40,             // 3: first_in_sequence (0x80), last_in_sequence (0x40), 6bits reserved
    };

    private static final byte HEADER_WDS[] = {
            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
    };

    private SupBDWriter() {
    }

    /**
     * Create the binary stream representation of one caption
     * @param pic SubPicture object containing caption info
     * @param bm bitmap
     * @param pal palette
     * @return byte buffer containing the binary stream representation of one caption
     */
    public static byte[] createSupFrame(SubPicture pic, Bitmap bm, Palette pal) {
        // the last palette entry must be transparent
        if (pal.getSize() > 255 && pal.getAlpha(255) > 0) {
            // quantize image
            QuantizeFilter qf = new QuantizeFilter();
            Bitmap bmQ = new Bitmap(bm.getWidth(), bm.getHeight());
            int[] ct = qf.quantize(bm.toARGB(pal), bmQ.getInternalBuffer(), bm.getWidth(), bm.getHeight(), 255, false, false);
            int size = ct.length;
            if (size > 255) {
                size = 255;
                logger.trace("Palette had to be reduced from " + pal.getSize() + " to " + size + " entries.\n");
                logger.warn("Quantizer failed.\n");
            } else {
                logger.trace("Palette had to be reduced from " + pal.getSize() + " to " + size + " entries.\n");
            }
            // create palette
            pal = new Palette(size);
            for (int i=0; i < size; i++) {
                pal.setARGB(i,ct[i]);
            }
            // use new bitmap
            bm = bmQ;
        }

        byte[] rleBuf = encodeImage(bm);

        // 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 = bm.getHighestVisibleColorIndex(pal.getAlpha()) + 1;
        int size = PACKET_HEADER.length * (8 + numAddPackets);
        size += HEADER_PCS_START.length + HEADER_PCS_END.length;
        size += 2* HEADER_WDS.length + HEADER_ODS_FIRST.length;
        size += numAddPackets * HEADER_ODS_NEXT.length;
        size += (2 + palSize * 5) /* PDS */;
        size += rleBuf.length;

        int yOfs = pic.getYOffset() - configuration.getCropOffsetY();
        if (yOfs < 0) {
            yOfs = 0;
        } else {
            int yMax = pic.getHeight() - pic.getImageHeight() - 2 * configuration.getCropOffsetY();
            if (yOfs > yMax) {
                yOfs = yMax;
            }
        }

        int h = pic.getHeight() -2 * configuration.getCropOffsetY();

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

        int fpsId = Framerate.idForFramerate(configuration.getFpsTrg());

        /* 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.getWidth() * pic.getHeight() * 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.getWidth() * bm.getHeight() * 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.getWidth() * bm.getHeight() * 9 + 1599) / 1600;
        // write PCS start
        PACKET_HEADER[10] = 0x16;                               // ID
        setDWord(PACKET_HEADER, 2, (int) pic.getStartTime());   // PTS
        setDWord(PACKET_HEADER, 6, 0);                          // DTS (0)
        setWord(PACKET_HEADER, 11, HEADER_PCS_START.length);    // size
        for (byte b : PACKET_HEADER) {
            buf[index++] = b;
        }
        setWord(HEADER_PCS_START,0, pic.getWidth());
        setWord(HEADER_PCS_START, 2, h);                        // cropped height
        setByte(HEADER_PCS_START, 4, fpsId);
        setWord(HEADER_PCS_START, 5, pic.getCompositionNumber());
        HEADER_PCS_START[14] = (pic.isForced() ? (byte)0x40 : 0);
        setWord(HEADER_PCS_START,15, pic.getXOffset());
        setWord(HEADER_PCS_START,17, yOfs);
        for (byte b : HEADER_PCS_START) {
            buf[index++] = b;
        }

        // write WDS
        PACKET_HEADER[10] = 0x17;                                   // ID
        int timeStamp = (int) pic.getStartTime() - windowInitTime;
        setDWord(PACKET_HEADER, 2, timeStamp);                      // PTS
        setDWord(PACKET_HEADER, 6, 0);                              // DTS (0)
        setWord(PACKET_HEADER, 11, HEADER_WDS.length);              // size
        for (byte b : PACKET_HEADER) {
            buf[index++] = b;
        }
        setWord(HEADER_WDS, 2, pic.getXOffset());
        setWord(HEADER_WDS, 4, yOfs);
        setWord(HEADER_WDS, 6, bm.getWidth());
        setWord(HEADER_WDS, 8, bm.getHeight());
        for (byte b : HEADER_WDS) {
            buf[index++] = b;
        }

        // write PDS
        PACKET_HEADER[10] = 0x14;                       // ID
        int dts = (int) pic.getStartTime() - (frameInitTime + windowInitTime);
        setDWord(PACKET_HEADER, 2, dts);                // PTS
        setDWord(PACKET_HEADER, 6, 0);                  // DTS (0)
        setWord(PACKET_HEADER, 11, (2 + palSize * 5))// size
        for (byte b : PACKET_HEADER) {
            buf[index++] = b;
        }
        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;
        }
        PACKET_HEADER[10] = 0x15;                                       // ID
        timeStamp = dts + imageDecodeTime;
        setDWord(PACKET_HEADER, 2, timeStamp);                          // PTS
        setDWord(PACKET_HEADER, 6, 0);                                  // DTS (0)
        setWord(PACKET_HEADER, 11, HEADER_ODS_FIRST.length + bufSize)// size
        for (byte b : PACKET_HEADER) {
            buf[index++] = b;
        }
        int marker = ((numAddPackets == 0) ? 0xC0000000 : 0x80000000);
        setDWord(HEADER_ODS_FIRST, 3, marker | (rleBuf.length + 4));
        setWord(HEADER_ODS_FIRST, 7, bm.getWidth());
        setWord(HEADER_ODS_FIRST, 9, bm.getHeight());
        for (byte b : HEADER_ODS_FIRST) {
            buf[index++] = b;
        }
        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;
            }
            PACKET_HEADER[10] = 0x15;                                   // ID (keep DTS & PTS)
            setWord(PACKET_HEADER, 11, HEADER_ODS_NEXT.length + psize); // size
            for (byte b : PACKET_HEADER) {
                buf[index++] = b;
            }
            for (byte b : HEADER_ODS_NEXT) {
                buf[index++] = b;
            }
            for (int i=0; i < psize; i++) {
                buf[index++] = rleBuf[rleIndex++];
            }
            bufSize -= psize;
        }

        // write END
        PACKET_HEADER[10] = (byte)0x80;                 // ID
        setDWord(PACKET_HEADER, 2, timeStamp);          // PTS
        setDWord(PACKET_HEADER, 6, 0);                  // DTS (0)
        setWord(PACKET_HEADER, 11, 0);                  // size
        for (byte b : PACKET_HEADER) {
            buf[index++] = b;
        }

        // write PCS end
        PACKET_HEADER[10] = 0x16;                               // ID
        setDWord(PACKET_HEADER, 2, (int) pic.getEndTime());     // PTS
        setDWord(PACKET_HEADER, 6, 0);                          // DTS (0)
        setWord(PACKET_HEADER, 11, HEADER_PCS_END.length);      // size
        for (byte b : PACKET_HEADER) {
            buf[index++] = b;
        }
        setWord(HEADER_PCS_END,0, pic.getWidth());
        setWord(HEADER_PCS_END, 2, h);                          // cropped height
        setByte(HEADER_PCS_END, 4, fpsId);
        setWord(HEADER_PCS_END, 5, pic.getCompositionNumber() + 1);
        for (byte b : HEADER_PCS_END) {
            buf[index++] = b;
        }

        // write WDS
        PACKET_HEADER[10] = 0x17;                               // ID
        timeStamp = (int) pic.getEndTime() - windowInitTime;
        setDWord(PACKET_HEADER, 2, timeStamp);                  // PTS
        setDWord(PACKET_HEADER, 6, 0);
        setWord(PACKET_HEADER, 11, HEADER_WDS.length);          // size
        for (byte b : PACKET_HEADER) {
            buf[index++] = b;
        }
        setWord(HEADER_WDS, 2, pic.getXOffset());
        setWord(HEADER_WDS, 4, yOfs);
        setWord(HEADER_WDS, 6, bm.getWidth());
        setWord(HEADER_WDS, 8, bm.getHeight());
        for (byte b : HEADER_WDS) {
            buf[index++] = b;
        }

        // write END
        PACKET_HEADER[10] = (byte)0x80;             // ID
        setDWord(PACKET_HEADER, 2, timeStamp);      // PTS (PTS of end PCS)
        setDWord(PACKET_HEADER, 6, 0);              // DTS (0)
        setWord(PACKET_HEADER, 11, 0);              // size
        for (byte b : PACKET_HEADER) {
            buf[index++] = b;
        }

        return buf;
    }

    /**
     * Create RLE buffer from bitmap
     * @param bm bitmap to compress
     * @return RLE buffer
     */
    private static byte[] encodeImage(Bitmap bm) {
        ArrayList<Byte> bytes = new ArrayList<Byte>();
        byte color;
        int ofs;
        int len;
        //boolean eol;

        for (int y=0; y < bm.getHeight(); y++) {
            ofs = y * bm.getWidth();
            //eol = false;
            int x;
            for (x=0; x < bm.getWidth(); x+=len, ofs+=len) {
                color = bm.getInternalBuffer()[ofs];
                for (len=1; x+len < bm.getWidth(); len++) {
                    if (bm.getInternalBuffer()[ofs+len] != color) {
                        break;
                    }
                }
                if (len<=2 && color != 0) {
                    // only a single occurrence -> add color
                    bytes.add(color);
                    if (len==2) {
                        bytes.add(color);
                    }
                } else {
                    if (len > 0x3fff) {
                        len = 0x3fff;
                    }
                    bytes.add((byte)0); // rle id
                    // commented out due to bug in SupRip
                    /*if (color == 0 && x+len == bm.getWidth()) {
                        bytes.add((byte)0);
                        eol = true;
                    } else*/
                    if (color == 0 && len < 0x40){
                        // 00 xx -> xx times 0
                        bytes.add((byte)len);
                    } else if (color == 0){
                        // 00 4x xx -> xxx zeroes
                        bytes.add((byte)(0x40|(len>>8)) );
                        bytes.add((byte)len);
                    } else if(len < 0x40) {
                        // 00 8x cc -> x times value cc
                        bytes.add((byte)(0x80|len) );
                        bytes.add(color);
                    } else {
                        // 00 cx yy cc -> xyy times value cc
                        bytes.add((byte)(0xc0|(len>>8)) );
                        bytes.add((byte)len);
                        bytes.add(color);
                    }
                }
            }
            if (/*!eol &&*/ x == bm.getWidth()) {
                bytes.add((byte)0); // rle id
                bytes.add((byte)0);
            }
        }
        int size =  bytes.size();
        byte[] retval = new byte[size];
        Iterator<Byte> it = bytes.iterator();
        for (int i=0; i < size; i++) {
            retval[i] = it.next();
        }
        return retval;
    }
}
TOP

Related Classes of bdsup2sub.supstream.bd.SupBDWriter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.