📄 parse.c
字号:
/***************************************************************************** * parse.c: SPU parser ***************************************************************************** * Copyright (C) 2000-2001, 2005 VideoLAN * $Id: parse.c 10538 2005-04-04 01:40:11Z rocky $ * * Authors: Samuel Hocevar <sam@zoy.org> * Laurent Aimar <fenrir@via.ecp.fr> * Gildas Bazin <gbazin@videolan.org> * * This program is free software; you can redistribute it 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, USA. *****************************************************************************//***************************************************************************** * Preamble *****************************************************************************/#include <vlc/vlc.h>#include <vlc/vout.h>#include <vlc/decoder.h>#include "spudec.h"/***************************************************************************** * Local prototypes. *****************************************************************************/static int ParseControlSeq( decoder_t *, subpicture_t *, subpicture_data_t *);static int ParseRLE ( decoder_t *, subpicture_t *, subpicture_data_t *);static void Render ( decoder_t *, subpicture_t *, subpicture_data_t *);/***************************************************************************** * AddNibble: read a nibble from a source packet and add it to our integer. *****************************************************************************/static inline unsigned int AddNibble( unsigned int i_code, uint8_t *p_src, int *pi_index ){ if( *pi_index & 0x1 ) { return( i_code << 4 | ( p_src[(*pi_index)++ >> 1] & 0xf ) ); } else { return( i_code << 4 | p_src[(*pi_index)++ >> 1] >> 4 ); }}/***************************************************************************** * ParsePacket: parse an SPU packet and send it to the video output ***************************************************************************** * This function parses the SPU packet and, if valid, sends it to the * video output. *****************************************************************************/subpicture_t * E_(ParsePacket)( decoder_t *p_dec ){ decoder_sys_t *p_sys = p_dec->p_sys; subpicture_data_t *p_spu_data; subpicture_t *p_spu; /* Allocate the subpicture internal data. */ p_spu = p_dec->pf_spu_buffer_new( p_dec ); if( !p_spu ) return NULL; /* Rationale for the "p_spudec->i_rle_size * 4": we are going to * expand the RLE stuff so that we won't need to read nibbles later * on. This will speed things up a lot. Plus, we'll only need to do * this stupid interlacing stuff once. */ p_spu_data = malloc( sizeof(subpicture_data_t) + 4 * p_sys->i_rle_size ); p_spu_data->p_data = (uint8_t *)p_spu_data + sizeof(subpicture_data_t); p_spu_data->b_palette = VLC_FALSE; p_spu_data->b_auto_crop = VLC_FALSE; p_spu_data->i_y_top_offset = 0; p_spu_data->i_y_bottom_offset = 0; p_spu_data->pi_alpha[0] = 0x00; p_spu_data->pi_alpha[1] = 0x0f; p_spu_data->pi_alpha[2] = 0x0f; p_spu_data->pi_alpha[3] = 0x0f; /* Get display time now. If we do it later, we may miss the PTS. */ p_spu_data->i_pts = p_sys->i_pts; p_spu->i_original_picture_width = p_dec->fmt_in.subs.spu.i_original_frame_width; p_spu->i_original_picture_height = p_dec->fmt_in.subs.spu.i_original_frame_height; /* Getting the control part */ if( ParseControlSeq( p_dec, p_spu, p_spu_data ) ) { /* There was a parse error, delete the subpicture */ p_dec->pf_spu_buffer_del( p_dec, p_spu ); return NULL; } /* We try to display it */ if( ParseRLE( p_dec, p_spu, p_spu_data ) ) { /* There was a parse error, delete the subpicture */ p_dec->pf_spu_buffer_del( p_dec, p_spu ); return NULL; } msg_Dbg( p_dec, "total size: 0x%x, RLE offsets: 0x%x 0x%x", p_sys->i_spu_size, p_spu_data->pi_offset[0], p_spu_data->pi_offset[1] ); Render( p_dec, p_spu, p_spu_data ); free( p_spu_data ); return p_spu;}/***************************************************************************** * ParseControlSeq: parse all SPU control sequences ***************************************************************************** * This is the most important part in SPU decoding. We get dates, palette * information, coordinates, and so on. For more information on the * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html *****************************************************************************/static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu, subpicture_data_t *p_spu_data ){ decoder_sys_t *p_sys = p_dec->p_sys; /* Our current index in the SPU packet */ unsigned int i_index = p_sys->i_rle_size + 4; /* The next start-of-control-sequence index and the previous one */ unsigned int i_next_seq = 0, i_cur_seq = 0; /* Command and date */ uint8_t i_command = SPU_CMD_END; mtime_t date = 0; unsigned int i, pi_alpha[4]; /* Initialize the structure */ p_spu->i_start = p_spu->i_stop = 0; p_spu->b_ephemer = VLC_FALSE; do { if( (int)i_index >= p_sys->i_spu_size + 1 ) { /* sanity * XXX only on test by loop as p_sys->buffer is bigger than needed * to avoid checking at each access */ break; } /* If we just read a command sequence, read the next one; * otherwise, go on with the commands of the current sequence. */ if( i_command == SPU_CMD_END ) { /* Get the control sequence date */ date = (mtime_t)GetWBE( &p_sys->buffer[i_index] ) * 11000; /* FIXME How to access i_rate * p_spudec->bit_stream.p_pes->i_rate / DEFAULT_RATE; */ /* Next offset */ i_cur_seq = i_index; i_next_seq = GetWBE( &p_sys->buffer[i_index+2] ); /* Skip what we just read */ i_index += 4; } i_command = p_sys->buffer[i_index++]; switch( i_command ) { case SPU_CMD_FORCE_DISPLAY: /* 00 (force displaying) */ p_spu->i_start = p_spu_data->i_pts + date; p_spu->b_ephemer = VLC_TRUE; break; /* Convert the dates in seconds to PTS values */ case SPU_CMD_START_DISPLAY: /* 01 (start displaying) */ p_spu->i_start = p_spu_data->i_pts + date; break; case SPU_CMD_STOP_DISPLAY: /* 02 (stop displaying) */ p_spu->i_stop = p_spu_data->i_pts + date; break; case SPU_CMD_SET_PALETTE: /* 03xxxx (palette) */ if( p_dec->fmt_in.subs.spu.palette[0] == 0xBeeF ) { unsigned int idx[4]; p_spu_data->b_palette = VLC_TRUE; idx[0] = (p_sys->buffer[i_index+0]>>4)&0x0f; idx[1] = (p_sys->buffer[i_index+0])&0x0f; idx[2] = (p_sys->buffer[i_index+1]>>4)&0x0f; idx[3] = (p_sys->buffer[i_index+1])&0x0f; for( i = 0; i < 4 ; i++ ) { uint32_t i_color = p_dec->fmt_in.subs.spu.palette[1+idx[i]]; /* FIXME: this job should be done sooner */ p_spu_data->pi_yuv[3-i][0] = (i_color>>16) & 0xff; p_spu_data->pi_yuv[3-i][1] = (i_color>>0) & 0xff; p_spu_data->pi_yuv[3-i][2] = (i_color>>8) & 0xff; } } i_index += 2; break; case SPU_CMD_SET_ALPHACHANNEL: /* 04xxxx (alpha channel) */ pi_alpha[3] = (p_sys->buffer[i_index+0]>>4)&0x0f; pi_alpha[2] = (p_sys->buffer[i_index+0])&0x0f; pi_alpha[1] = (p_sys->buffer[i_index+1]>>4)&0x0f; pi_alpha[0] = (p_sys->buffer[i_index+1])&0x0f; /* Ignore blank alpha palette. Sometimes spurious blank * alpha palettes are present - dunno why. */ if( pi_alpha[0] | pi_alpha[1] | pi_alpha[2] | pi_alpha[3] ) { p_spu_data->pi_alpha[0] = pi_alpha[0]; p_spu_data->pi_alpha[1] = pi_alpha[1]; p_spu_data->pi_alpha[2] = pi_alpha[2]; p_spu_data->pi_alpha[3] = pi_alpha[3]; } else { msg_Warn( p_dec, "ignoring blank alpha palette" ); } i_index += 2; break; case SPU_CMD_SET_COORDINATES: /* 05xxxyyyxxxyyy (coordinates) */ p_spu->i_x = (p_sys->buffer[i_index+0]<<4)| ((p_sys->buffer[i_index+1]>>4)&0x0f); p_spu->i_width = (((p_sys->buffer[i_index+1]&0x0f)<<8)| p_sys->buffer[i_index+2]) - p_spu->i_x + 1; p_spu->i_y = (p_sys->buffer[i_index+3]<<4)| ((p_sys->buffer[i_index+4]>>4)&0x0f); p_spu->i_height = (((p_sys->buffer[i_index+4]&0x0f)<<8)| p_sys->buffer[i_index+5]) - p_spu->i_y + 1; /* Auto crop fullscreen subtitles */ if( p_spu->i_height > 250 ) p_spu_data->b_auto_crop = VLC_TRUE; i_index += 6; break; case SPU_CMD_SET_OFFSETS: /* 06xxxxyyyy (byte offsets) */ p_spu_data->pi_offset[0] = GetWBE(&p_sys->buffer[i_index+0]) - 4; p_spu_data->pi_offset[1] = GetWBE(&p_sys->buffer[i_index+2]) - 4; i_index += 4; break; case SPU_CMD_END: /* ff (end) */ break; default: /* xx (unknown command) */ msg_Warn( p_dec, "unknown command 0x%.2x", i_command ); return VLC_EGENERIC; } /* We need to check for quit commands here */ if( p_dec->b_die ) { return VLC_EGENERIC; } } while( i_command != SPU_CMD_END || i_index == i_next_seq ); /* Check that the next sequence index matches the current one */ if( i_next_seq != i_cur_seq ) { msg_Err( p_dec, "index mismatch (0x%.4x != 0x%.4x)", i_next_seq, i_cur_seq ); return VLC_EGENERIC; } if( (int)i_index > p_sys->i_spu_size ) { msg_Err( p_dec, "uh-oh, we went too far (0x%.4x > 0x%.4x)", i_index, p_sys->i_spu_size ); return VLC_EGENERIC; } if( !p_spu->i_start ) { msg_Err( p_dec, "no `start display' command" ); } if( p_spu->i_stop <= p_spu->i_start && !p_spu->b_ephemer ) { /* This subtitle will live for 5 seconds or until the next subtitle */ p_spu->i_stop = p_spu->i_start + (mtime_t)500 * 11000; p_spu->b_ephemer = VLC_TRUE; } /* Get rid of padding bytes */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -