⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dvbsubpicture.java

📁 优秀的MPEG2-TS流分析软件
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
/*
 * @(#)DVBSubpicture.java - decodes DVB subtitles
 *
 * Copyright (c) 2004-2005 by dvb.matt, All rights reserved
 * 
 * This file is part of X, a free Java based demux utility.
 * X is intended for educational purposes only, as a non-commercial test project.
 * It may not be used otherwise. Most parts are only experimental.
 *  
 *
 * This program is free software; you can redistribute it free of charge
 * and/or modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/*
 * example of a basic implementation of a DVB subtitle decoder
 * 
 * it does not yet implement export of encoded string characters, only bitmapped pictures
 * 
 */

package net.sourceforge.dvb.projectx.subtitle;

//DM24042004 081.7 int02 introduced

import java.awt.*;
import java.awt.image.*;
import java.util.*;

import net.sourceforge.dvb.projectx.common.Resource;
import net.sourceforge.dvb.projectx.common.X;

public class DVBSubpicture
{
	private byte data[];
	private int BytePosition, BitPosition;
	private Graphics2D big;
	private BufferedImage bimg;
	private Epoch epoch;
	private Page page;
	private Region region;
	private CLUT clut;
	private OBJECT object;
	private Hashtable epoches = new Hashtable();
	private int table_CLUT_8bit[];
	private int IRD;
	private int epoch_id = 0;
	private boolean biglog;
	private int pixel_data[], preview_pixel_data[];
	private long pts;
	private int width = 720, height = 576;
	private int from_index, to_index;
	private boolean picture_saved;
	private boolean save;
	private boolean preview_visible = false;
	private int fix_page_id;

	//DM13062004 081.7 int04 add
	private Hashtable user_table = new Hashtable();
	private boolean user_table_enabled;

	//DM30072004 081.7 int07 add
	private boolean global_error = false;

	public DVBSubpicture()
	{
		table_CLUT_8bit = generateDefaultCLUT_8Bits();
		setIRD(8, user_table, false, ""); 	//DM13062004 081.7 int04 changed
	}

	public void setIRD(int val, Hashtable table, boolean log, String page_id_str)
	{
		IRD = val;  //2,4,8 = 4,16,256-color support

		//DM13062004 081.7 int04 add++
		user_table = table;
		user_table_enabled = !user_table.isEmpty(); //DM23062004 081.7 int05 changed

		if (user_table_enabled)
			IRD = Integer.parseInt(user_table.get("model").toString().trim());
		//DM13062004 081.7 int04 add--

		biglog = log;
		resetEpoch();

		if (page_id_str.trim().length() != 0)
			fix_page_id = Integer.parseInt(page_id_str.trim());
		else
			fix_page_id = -1;

		preview_visible = false;
	}

	private void addBigMessage(String msg)
	{
		if (!biglog)
			return;

		System.out.println(msg);
	}

	private void resetEpoch()
	{
		epoches.clear();
		epoch = setEpoch(epoch_id);
		preview_pixel_data = new int[width * height];
		picture_saved = false;
		save = false;
	}

	private int getBits(int N)
	{
		int Pos, Val;
		Pos = BitPosition>>>3;

		//DM03082004 081.7 int07 add
		if (Pos >= data.length - 4)
		{
			global_error = true;
			BitPosition += N;
			BytePosition = BitPosition>>>3;
			return 0;
		}

		Val =   (0xFF & data[Pos])<<24 |
			(0xFF & data[Pos+1])<<16 |
			(0xFF & data[Pos+2])<<8 |
			(0xFF & data[Pos+3]);

		Val <<= BitPosition & 7;
		Val >>>= 32-N;

		BitPosition += N;
		BytePosition = BitPosition>>>3;

		return Val;
	}

	private int nextBits(int N)
	{
		int Pos, Val;
		Pos = BitPosition>>>3;

		//DM03082004 081.7 int07 add
		if (Pos >= data.length - 4)
		{
			global_error = true;
			return 0;
		}

		Val =   (0xFF & data[Pos])<<24 |
			(0xFF & data[Pos+1])<<16 |
			(0xFF & data[Pos+2])<<8 |
			(0xFF & data[Pos+3]);

		Val <<= BitPosition & 7;
		Val >>>= 32-N;

		return Val;
	}

	private void flushBits(int N)
	{
		BitPosition += N;
		BytePosition = BitPosition>>>3;
	}

	private void alignToByte()
	{
		alignToByte(1);
	}

	private void alignToByte(int N)
	{
		while ( (7 & BitPosition) != 0 )
			flushBits(N);
	}

	private void alignToWord()
	{
		if ((1 & BytePosition) != 0 && nextBits(8) != 0x0F)
			flushBits(8);
	}

	public int getTimeOut()
	{
		return page.getTimeOut();
	}

	public int decodeDVBSubpicture(byte packet[], int BPos[], Graphics2D big, BufferedImage bimg, long pts, boolean save, boolean preview_visible)
	{
		data = packet;  //packet + 4 bytes overhead
		BytePosition = BPos[0];  //bytpos
		BitPosition = BPos[0]<<3;  //bitpos
		this.big = big;
		this.bimg = bimg;
		this.pts = pts;
		this.save = save;
		this.preview_visible = preview_visible;

		picture_saved = false;

		//DM30072004 081.7 int07 add
		global_error = false;

		flushBits(8); //padding
		int stream_ident = getBits(8); // 0 = std
		int segment_type = 0;
		int sync_byte = 0;

		//DM30072004 081.7 int07 changed
		//while ( nextBits(8) == 0x0F )
		for (int ret; BytePosition < data.length - 4; )
		{
			ret = nextBits(8);

			addBigMessage("ret " + Integer.toHexString(ret) + " /bi " + BitPosition + " /by " + BytePosition);

			if (ret == 0xFF)
				break;

			if (ret != 0x0F)
			{
				flushBits(8);
				continue;
			}

			segment_type = Subtitle_Segment();

			alignToByte();

			if (global_error && region != null)
				region.setError(4);
		}

		if ( nextBits(8) == 0xFF )
		{}

		if (picture_saved)
			return -1;

		return -2;
	}

	private int Subtitle_Segment()
	{
		if ( getBits(8) != 0x0F ) //syncbyte
			return -1;

		int segment_type = getBits(8);

		int page_id = getBits(16); //page_id

		if ((page_id & fix_page_id) == 0) // exclude unwanted pages
		{
			stuffing();
			return 0xFF;
		}

		page = epoch.setPage(page_id);

		addBigMessage("segm: 0x" + Integer.toHexString(segment_type) + " / " + pts);

		switch (segment_type)
		{
		case 0x10:
			return page_composition();
			//return segment_type;

		case 0x11:
			region_composition();
			return segment_type;

		case 0x12:
			CLUT_definition();
			return segment_type;

		case 0x13:
			object_data();
			return segment_type;

		case 0x80:
			end_display();
			return segment_type;

		case 0xFF:
			stuffing();
			return segment_type;

		default:
			stuffing();
			return segment_type;
		}
	}

	private void end_display()
	{
		int segment_length = getBits(16);
		flushBits(segment_length * 8); //DM18062004 081.7 int05 changed
	}

	private void stuffing()
	{
		int segment_length = getBits(16); //+ BytePosition;
		flushBits(segment_length * 8); //DM18062004 081.7 int05 changed
	}

	private void prepare_output()
	{
		//DM26052004 081.7 int03 changed
		//long new_time_out = (long)Math.round((pts - page.getTimeIn()) / 900.0);
		long new_time_out = 1L + ((pts - page.getTimeIn()) / 1000);

		if (page.getTimeOut() > 0 && new_time_out > page.getTimeOut())
			new_time_out = page.getTimeOut();

		int page_pixel_data[] = new int[page.getWidth() * page.getHeight()];

		for (int y = 0; y < page.getHeight(); y++)
				System.arraycopy(preview_pixel_data, page.getX() + ((page.getY() + y) * width), page_pixel_data, y * page.getWidth(), page.getWidth());

		if (page.getWriteStatus())
			BMP.savePixels( new Bitmap( page.getX(), page.getY(), page.getWidth(), page.getHeight(), page_pixel_data, IRD, page.getId(), region.getId(), 0, page.getTimeIn(), (int)new_time_out));

		if (preview_visible)
			bimg.setRGB(page.getX(), page.getY(), page.getWidth(), page.getHeight(), page_pixel_data, 0, page.getWidth());

		paintRegionBorder(page.getX(), page.getY(), page.getWidth(), page.getHeight());

		picture_saved = true;

		addBigMessage("time: in " + page.getTimeIn() + " /len " + new_time_out + " /save " + save + " /prev " + preview_visible);
	}

	private int page_composition()
	{
		int segment_end = getBits(16) + BytePosition;
		int segment_type = 0x10;

		int time_out = getBits(8) * 100; //page_time_out, milliseconds (here ticks) 'til disappearing without another erase event

		page.setVersionNumber(getBits(4)); //page_version_number, if number exists, update isn't necessary

		page.setState(getBits(2)); //page_state 
		//0 = only updates of this page contents
		//1 = new page instance, all contents with new definitions
		//2 = new epoch, see 1, reset all, complete dec. model (IRD) changes possible

		flushBits(2);

		addBigMessage("pagecomp: state " + page.getState() + " /page " + page.getId() + " /pv " + page.getVersionNumber() + " /to " + time_out);

		if (page.getState() > 0)
		{
			clearBackground();
			page.clearArea();
		}

		for (Enumeration e = epoch.getRegions(); e.hasMoreElements() ; )
		{
			region = epoch.setRegion(Integer.parseInt(e.nextElement().toString()));

			//DM23062004 081.7 int05 add
			if (region.getErrors() > 0)
			{
				X.Msg(Resource.getString("subpicture.msg.error.dvbdecoding", "" + region.getErrors(), "" + region.getId(), "" + page.getTimeIn()));
				//region.setActive(false);
			}

			addBigMessage("enum: region " + region.getId() + " /err " + region.getErrors() + " /acti " + region.isActive() + " /chng " + region.isChanged() + " /ti_o " + time_out);
//
			if ( page.getState() == 0 && time_out > 6000 )
				continue;
//

			region.setError(0); //DM23062004 081.7 int05 add

			if ( !region.isActive() || !region.isChanged() )
				continue;

			region.setChanged(false);
			pixel_data = region.getPixel();

			page.addArea(region.getXBound(), region.getYBound(), region.getWidth(), region.getHeight());

			for (int a = 0; a < region.getHeight(); a++)
				System.arraycopy(pixel_data, a * region.getWidth(), preview_pixel_data, region.getXBound() + ((region.getYBound() + a) * width), region.getWidth());

			segment_type = 0x80;

			addBigMessage("addToBMP: region " + region.getId());
			addBigMessage("newSize: x " + page.getX() + " y " + page.getY() + " w " + page.getWidth() + " h " + page.getHeight());
		}

		if (segment_type == 0x80)
			prepare_output();

		if (page.getState() > 0) //if not update, page content has to die
		{
			page = epoch.newPage(page.getId()); 
			page.setTimeIn(pts);
			page.setWriteStatus(save);
			epoch.clearRegions();
			epoch.clearObjects();
		}

		page.setTimeOut(time_out); //page_time_out, seconds to stand

		while (BytePosition < segment_end)
		{
			region = epoch.setRegion(getBits(8)); //region_ids of this epoch
			region.setActive(true);
			region.setChanged(true);

			flushBits(8);

			region.setHorizontalAddress(getBits(16)); // start_x
			region.setVerticalAddress(getBits(16));   // start_y

			addBigMessage("addreg: reg " + region.getId() + " /x " + region.getXBound() + " /y " + region.getYBound());
		}

		return segment_type;
	}

	private void region_composition()
	{
		int segment_end = getBits(16) + BytePosition;

		region = epoch.setRegion(getBits(8)); //region_id
		region.setVersionNumber(getBits(4)); //region_version_number, if number exists, update isn't necessary
		region.setFillFlag(getBits(1));

		flushBits(3);

		region.setWidth(getBits(16));
		region.setHeight(getBits(16));
		region.setLevelOfCompatibility(getBits(3));
		region.setDepth(getBits(3));

		flushBits(2);

		int CLUT_id = getBits(8);
		clut = epoch.setCLUT(CLUT_id); //CLUT_id

		region.setCLUT_id(CLUT_id);
		region.setPixelCode_8bit(getBits(8));
		region.setPixelCode_4bit(getBits(4));
		region.setPixelCode_2bit(getBits(2));

		pixel_data = region.initPixel();

		flushBits(2);

		paintRegionBackground();

		addBigMessage("regcomp: page " + page.getId() + " /reg " + region.getId() + " /rv " + region.getVersionNumber() + " /lv " + region.getCompatibility() + " /clut " + clut.getId() + " /activ " + region.isActive());

		while (BytePosition < segment_end)
		{
			object = epoch.setObject(getBits(16)); //object_id
			object.setRegionId(region.getId());
			object.setType(getBits(2));
			object.setProvider(getBits(2));
			object.setHorizontalPosition(getBits(12));

			flushBits(4);

			object.setVerticalPosition(getBits(12));

			if (object.getType() == 1 || object.getType() == 2) //character or ch.strings
			{
				object.setForegroundCode(getBits(8)); //foreground_pixel_code
				object.setBackgroundCode(getBits(8)); //background_pixel_code
			}

			addBigMessage("addobj: reg " + region.getId() + " /obj " + Integer.toHexString(object.getId()).toUpperCase() + " /x " + object.getHorizontalPosition() + " /y " + object.getVerticalPosition());
		}
	}

	private void CLUT_definition()
	{
		int segment_end = getBits(16) + BytePosition;

		clut = epoch.setCLUT(getBits(8)); //CLUT_id
		clut.setVersionNumber(getBits(4)); //CLUT_version_number, if number exists, update isn't necessary

		addBigMessage("clutcomp: " + clut.getId() + " /v " + clut.getVersionNumber());

		flushBits(4);

		//user table
		//DM13062004 081.7 int04 add
		if (user_table_enabled)
		{
			setUserClut();
			flushBits( (segment_end - BytePosition) * 8);
			return;
		}

		while (BytePosition < segment_end)
		{
			int CLUT_entry_id = getBits(8);

			int CLUT_flag_2bit_entry = getBits(1);
			int CLUT_flag_4bit_entry = getBits(1);
			int CLUT_flag_8bit_entry = getBits(1);
			int flag = CLUT_flag_8bit_entry<<3 
					| CLUT_flag_4bit_entry<<2
					| CLUT_flag_2bit_entry<<1;

			flushBits(4);

			int full_range_flag = getBits(1);
			int ARGB, Y, Cr, Cb, T;

			if (full_range_flag == 1)
			{
				Y = getBits(8);
				Cr = getBits(8);
				Cb = getBits(8);
				T = getBits(8);
			}
			else //only MSB transmitted
			{
				Y = getBits(6)<<2;
				Cr = getBits(4)<<4;
				Cb = getBits(4)<<4;
				T = getBits(2)<<6;
			}

			ARGB = YUVtoRGB(Y, Cr, Cb, T);

			addBigMessage("addclut: " + CLUT_entry_id + " /flag " + Integer.toHexString(flag).toUpperCase() + " /ARGB " + Integer.toHexString(ARGB).toUpperCase() + " /range " + full_range_flag);

			for (int i=0; i<3; i++)
				clut.setClutEntry(mapColorIndex(CLUT_entry_id, region.getDepth(), 2<<i), (flag & 2<<i), ARGB);
		}
	}

	private void object_data()
	{
		int segment_end = getBits(16) + BytePosition;

		object = epoch.setObject(getBits(16)); //object_id

		int region_id = object.getRegionId();

		if (region_id < 0)
		{
			flushBits( (segment_end - BytePosition) * 8);  //DM18062004 081.7 int05 changed
			addBigMessage("object_id " + object.getId() + " with no region_id");
			return;
		}

		region = epoch.setRegion(region_id); //use the right region
		region.resetXY();

		pixel_data = region.getPixel();

		addBigMessage("objdata: reg " + region.getId() + " /obj " + Integer.toHexString(object.getId()).toUpperCase());

		object.setVersionNumber(getBits(4)); //object_version_number, if number exists, update isn't necessary
		int object_coding_method = getBits(2);

		object.setNonModify(getBits(1)); //non_modifying_colour_flag
		flushBits(1);

		/**
		 * modify user_table here, if no clut definition were sent
		 */
		if (user_table_enabled && clut.getModifyFlags() == 0)
			setUserClut();

		if (object_coding_method == 0) //pixels
		{
			int top_field_data_block_end = getBits(16);
			int bottom_field_data_block_end = getBits(16);
			object.setCopyTopFieldFlag(bottom_field_data_block_end);

			top_field_data_block_end += BytePosition;

			while (BytePosition < top_field_data_block_end)
				pixel_block();

			region.nextField();
			bottom_field_data_block_end += BytePosition;

			while (BytePosition < bottom_field_data_block_end) //if 0-length, copy top field line
				pixel_block();

			//alignToWord(); // flush 8 bits if not aligned
		}

		if (object_coding_method == 1) //text chars
		{
			int number_of_codes = getBits(8); //all chars in a line

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -