diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | configure.ac | 19 | ||||
-rw-r--r-- | src/demuxers/demux_ts.c | 483 | ||||
-rw-r--r-- | src/input/Makefile.am | 10 | ||||
-rw-r--r-- | src/input/input_bluray.c | 1744 | ||||
-rw-r--r-- | src/input/input_net.c | 15 | ||||
-rw-r--r-- | src/input/net_buf_ctrl.c | 4 | ||||
-rw-r--r-- | src/libspudvb/xine_spudvb_decoder.c | 62 | ||||
-rw-r--r-- | src/libspuhdmv/xine_hdmv_decoder.c | 2 | ||||
-rw-r--r-- | src/xine-engine/buffer.h | 2 | ||||
-rw-r--r-- | src/xine-utils/memcpy.c | 19 |
11 files changed, 2179 insertions, 186 deletions
@@ -1,3 +1,8 @@ +xine-lib (1.1.21) 2011-??-?? + * mpeg-ts fixes and enhancements + * Improved syncing of DVB subtitles + * BluRay support (requires libbluray) + xine-lib (1.1.20.1) 2012-01-01 * Various bug fixes. * Use the current ImageMagick API (if using ImageMagick). diff --git a/configure.ac b/configure.ac index b175301be..5501ef72a 100644 --- a/configure.ac +++ b/configure.ac @@ -1674,6 +1674,22 @@ fi AM_CONDITIONAL(HAVE_GDK_PIXBUF, test "x$no_gdkpixbuf" != "xyes") dnl --------------------------------------------- +dnl libbluray support +dnl --------------------------------------------- +AC_ARG_ENABLE([bluray], + AS_HELP_STRING([--disable-bluray], [Do not build BluRay support]), + [with_bluray=$enableval], [with_bluray=yes]) + +if test "x$with_bluray" != "xno"; then + PKG_CHECK_MODULES([LIBBLURAY], [libbluray >= 0.2.1], + [have_libbluray=yes], + AC_MSG_RESULT(*** All of the libbluray dependent parts will be disabled ***)) + AC_SUBST(LIBBLURAY_CFLAGS) + AC_SUBST(LIBBLURAY_LIBS) +fi +AM_CONDITIONAL(HAVE_LIBBLURAY, test "x$have_libbluray" = "xyes") + +dnl --------------------------------------------- dnl libsmbclient support dnl --------------------------------------------- @@ -2954,6 +2970,9 @@ fi if test "x$have_libsmbclient" = "xyes"; then echo " - smbclient" fi +if test "x$have_libbluray" = x"yes"; then + echo " - bluray" +fi echo "" dnl Demuxers diff --git a/src/demuxers/demux_ts.c b/src/demuxers/demux_ts.c index ef71bdfa5..88ccf49f4 100644 --- a/src/demuxers/demux_ts.c +++ b/src/demuxers/demux_ts.c @@ -182,8 +182,6 @@ #define BUF_SIZE (NPKT_PER_READ * (PKT_SIZE + 4)) -#define MAX_PES_BUF_SIZE 2048 - #define CORRUPT_PES_THRESHOLD 10 #define NULL_PID 0x1fff @@ -250,6 +248,10 @@ HDMV_SPU_INTERACTIVE = 0x91, HDMV_SPU_TEXT = 0x92, + /* pseudo tags */ + STREAM_AUDIO_EAC3 = (DESCRIPTOR_EAC3 << 8), + STREAM_AUDIO_DTS = (DESCRIPTOR_DTS << 8), + } streamType; #define WRAP_THRESHOLD 270000 @@ -285,13 +287,14 @@ typedef struct { unsigned int pid; fifo_buffer_t *fifo; - uint32_t size; uint32_t type; int64_t pts; buf_element_t *buf; unsigned int counter; uint16_t descriptor_tag; /* +0x100 for PES stream IDs (no available TS descriptor tag?) */ + uint8_t keep; /* used by demux_ts_dynamic_pmt_*() */ int corrupted_pes; + int pes_bytes_left; /* butes left if PES packet size is known */ int input_normpos; int input_time; @@ -329,6 +332,7 @@ typedef struct { fifo_buffer_t *video_fifo; input_plugin_t *input; + unsigned int read_retries; int status; @@ -408,6 +412,169 @@ typedef struct { config_values_t *config; } demux_ts_class_t; +static void reset_track_map(fifo_buffer_t *fifo) +{ + buf_element_t *buf = fifo->buffer_pool_alloc (fifo); + + buf->type = BUF_CONTROL_RESET_TRACK_MAP; + buf->decoder_info[1] = -1; + + fifo->put (fifo, buf); +} + +/* TJ. dynamic PMT support. The idea is: + First, reuse unchanged pids and add new ones. + Then, comb out those who are no longer referenced. + For example, the Kaffeine dvb frontend preserves original pids but only + sends the currently user selected ones, plus matching generated pat/pmt */ + +static int demux_ts_dynamic_pmt_find (demux_ts_t *this, + int pid, int type, unsigned int descriptor_tag) { + unsigned int i; + demux_ts_media *m; + for (i = 0; i < this->media_num; i++) { + m = &this->media[i]; + if ((m->pid == pid) && ((m->type & BUF_MAJOR_MASK) == type)) { + /* mark this media decriptor for reuse */ + m->keep = 1; + return i; + } + } + if (i < MAX_PIDS) { + /* prepare new media descriptor */ +#ifdef LOG_DYNAMIC_PMT + char *name = ""; + if (type == BUF_VIDEO_BASE) name = "video"; + else if (type == BUF_AUDIO_BASE) name = "audio"; + else if (type == BUF_SPU_BASE) name = "subtitle"; + printf ("demux_ts: new %s pid %d\n", name, pid); +#endif + m = &this->media[i]; + if (type == BUF_AUDIO_BASE) { + /* allocate new audio track as well */ + if (this->audio_tracks_count >= MAX_AUDIO_TRACKS) { + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + "demux_ts: too many audio PIDs, ignoring pid %d\n", pid); + return -1; + } + m->type = type | this->audio_tracks_count; + this->audio_tracks[this->audio_tracks_count].pid = pid; + this->audio_tracks[this->audio_tracks_count].media_index = i; + this->audio_tracks_count++; + m->fifo = this->stream->audio_fifo; + } else { + m->type = type; + m->fifo = this->stream->video_fifo; + } + m->pid = pid; + + if (m->buf) { + m->buf->free_buffer(m->buf); + m->buf = NULL; + } + m->counter = INVALID_CC; + m->corrupted_pes = 1; + m->pts = 0; + + m->descriptor_tag = descriptor_tag; + + m->keep = 1; + this->media_num++; + return i; + } + /* table full */ + return -1; +} + +static void demux_ts_dynamic_pmt_clean (demux_ts_t *this) { + int i, count = 0, tracks = 0, spus = 0; + /* densify media table */ + for (i = 0; i < this->media_num; i++) { + demux_ts_media *m = &this->media[i]; + int type = m->type & BUF_MAJOR_MASK; + int chan = m->type & 0xff; + if (m->keep) { + m->keep = 0; + if (type == BUF_VIDEO_BASE) { + /* adjust single video link */ + this->videoMedia = count; + } else if (type == BUF_AUDIO_BASE) { + /* densify audio track table */ + this->audio_tracks[chan].media_index = count; + if (chan > tracks) { + m->type = (m->type & ~0xff) | tracks; + this->audio_tracks[tracks] = this->audio_tracks[chan]; + } + tracks++; + } else if (type == BUF_SPU_BASE) { + /* spu language table has already been rebuilt from scratch. + Adjust backlinks only */ + while ((spus < this->spu_langs_count) && (this->spu_langs[spus].pid == m->pid)) { + this->spu_langs[spus].media_index = count; + spus++; + } + } + if (i > count) { + this->media[count] = *m; + m->buf = NULL; + m->pid = INVALID_PID; + } + count++; + } else { + /* drop this no longer needed media descriptor */ +#ifdef LOG_DYNAMIC_PMT + char *name = ""; + if (type == BUF_VIDEO_BASE) name = "video"; + else if (type == BUF_AUDIO_BASE) name = "audio"; + else if (type == BUF_SPU_BASE) name = "subtitle"; + printf ("demux_ts: dropped %s pid %d\n", name, m->pid); +#endif + if (m->buf) { + m->buf->free_buffer (m->buf); + m->buf = NULL; + } + m->pid = INVALID_PID; + } + } + if ((tracks < this->audio_tracks_count) && this->audio_fifo) { + /* at least 1 audio track removed, tell audio decoder loop */ + reset_track_map(this->audio_fifo); +#ifdef LOG_DYNAMIC_PMT + printf ("demux_ts: new audio track map\n"); +#endif + } +#ifdef LOG_DYNAMIC_PMT + printf ("demux_ts: using %d pids, %d audio %d subtitle channels\n", count, tracks, spus); +#endif + /* adjust table sizes */ + this->media_num = count; + this->audio_tracks_count = tracks; + /* should really have no effect */ + this->spu_langs_count = spus; +} + +static void demux_ts_dynamic_pmt_clear (demux_ts_t *this) { + unsigned int i; + for (i = 0; i < this->media_num; i++) { + if (this->media[i].buf) { + this->media[i].buf->free_buffer (this->media[i].buf); + this->media[i].buf = NULL; + } + } + this->media_num = 0; + + this->videoPid = INVALID_PID; + this->audio_tracks_count = 0; + this->spu_pid = INVALID_PID; + this->spu_langs_count = 0; + this->spu_media = 0; + + this->pcr_pid = INVALID_PID; + + this->last_pmt_crc = 0; +} + + static void demux_ts_tbre_reset (demux_ts_t *this) { if (this->tbre_time <= TBRE_TIME) { this->tbre_pid = INVALID_PID; @@ -576,6 +743,9 @@ static void demux_ts_update_spu_channel(demux_ts_t *this) this->spu_pid = lang->pid; this->spu_media = lang->media_index; + /* multiple spu langs can share same media descriptor */ + this->media[lang->media_index].type = + (this->media[lang->media_index].type & ~0xff) | this->current_spu_channel; #ifdef TS_LOG printf("demux_ts: DVBSUB: selecting lang: %s page %ld %ld\n", lang->desc.lang, lang->desc.comp_page_id, lang->desc.aux_page_id); @@ -630,6 +800,7 @@ static void demux_ts_flush(demux_ts_t *this) unsigned int i; for (i = 0; i < this->media_num; ++i) { demux_ts_flush_media(&this->media[i]); + this->media[i].corrupted_pes = 1; } } @@ -645,11 +816,13 @@ static void demux_ts_flush(demux_ts_t *this) */ static void demux_ts_parse_pat (demux_ts_t*this, unsigned char *original_pkt, unsigned char *pkt, unsigned int pusi) { +#ifdef TS_PAT_LOG uint32_t table_id; + uint32_t version_number; +#endif uint32_t section_syntax_indicator; int32_t section_length; uint32_t transport_stream_id; - uint32_t version_number; uint32_t current_next_indicator; uint32_t section_number; uint32_t last_section_number; @@ -680,11 +853,15 @@ static void demux_ts_parse_pat (demux_ts_t*this, unsigned char *original_pkt, "demux_ts: demux error! PAT with invalid pointer\n"); return; } +#ifdef TS_PAT_LOG table_id = (unsigned int)pkt[5] ; +#endif section_syntax_indicator = (((unsigned int)pkt[6] >> 7) & 1) ; section_length = (((unsigned int)pkt[6] & 0x03) << 8) | pkt[7]; transport_stream_id = ((uint32_t)pkt[8] << 8) | pkt[9]; +#ifdef TS_PAT_LOG version_number = ((uint32_t)pkt[10] >> 1) & 0x1f; +#endif current_next_indicator = ((uint32_t)pkt[10] & 0x01); section_number = (uint32_t)pkt[11]; last_section_number = (uint32_t)pkt[12]; @@ -779,11 +956,7 @@ static void demux_ts_parse_pat (demux_ts_t*this, unsigned char *original_pkt, /* force PMT reparsing when pmt_pid changes */ if (this->pmt_pid[program_count] != pmt_pid) { this->pmt_pid[program_count] = pmt_pid; - this->audio_tracks_count = 0; - this->last_pmt_crc = 0; - this->videoPid = INVALID_PID; - this->spu_pid = INVALID_PID; - this->pcr_pid = INVALID_PID; + demux_ts_dynamic_pmt_clear (this); if (this->pmt[program_count] != NULL) { free(this->pmt[program_count]); @@ -834,14 +1007,13 @@ static int demux_ts_parse_pes_header (xine_t *xine, demux_ts_media *m, return 0 ; } - /* packet_len = p[4] << 8 | p[5]; */ stream_id = p[3]; - header_len = p[8]; + header_len = p[8] + 9; /* sometimes corruption on header_len causes segfault in memcpy below */ - if (header_len + 9 > packet_len) { + if (header_len > packet_len) { xprintf (xine, XINE_VERBOSITY_DEBUG, - "demux_ts: illegal value for PES_header_data_length (0x%x)\n", header_len); + "demux_ts: illegal value for PES_header_data_length (0x%x)\n", header_len - 9); return 0; } @@ -852,7 +1024,7 @@ static int demux_ts_parse_pes_header (xine_t *xine, demux_ts_media *m, if (p[7] & 0x80) { /* pts avail */ - if (header_len < 5) { + if (header_len < 14) { return 0; } @@ -880,22 +1052,21 @@ static int demux_ts_parse_pes_header (xine_t *xine, demux_ts_media *m, m->pts = pts; - p += header_len + 9; - packet_len -= header_len + 9; + m->pes_bytes_left = (int)(p[4] << 8 | p[5]) - header_len + 6; + lprintf("PES packet payload left: %d bytes\n", m->pes_bytes_left); + + p += header_len; + packet_len -= header_len; if (m->descriptor_tag == STREAM_VIDEO_VC1) { - m->size = packet_len; m->type = BUF_VIDEO_VC1; - return 1; + return header_len; } if (m->descriptor_tag == HDMV_SPU_BITMAP) { - long payload_len = ((buf[4] << 8) | buf[5]) - header_len - 3; - - m->size = packet_len; m->type |= BUF_SPU_HDMV; - m->buf->decoder_info[2] = payload_len; - return 1; + m->buf->decoder_info[2] = m->pes_bytes_left; + return header_len; } else @@ -912,27 +1083,25 @@ static int demux_ts_parse_pes_header (xine_t *xine, demux_ts_media *m, * these "raw" streams may begin with a byte that looks like a stream type. * For audio streams, m->type already contains the stream no. */ - if(m->descriptor_tag == HDMV_AUDIO_84_EAC3) { - m->size = packet_len; + if(m->descriptor_tag == HDMV_AUDIO_84_EAC3 || + m->descriptor_tag == STREAM_AUDIO_EAC3) { m->type |= BUF_AUDIO_EAC3; - return 1; + return header_len; } else if(m->descriptor_tag == STREAM_AUDIO_AC3) { /* ac3 - raw */ - m->size = packet_len; m->type |= BUF_AUDIO_A52; - return 1; + return header_len; } else if (m->descriptor_tag == HDMV_AUDIO_83_TRUEHD) { /* TODO: separate AC3 and TrueHD streams ... */ - m->size = packet_len; m->type |= BUF_AUDIO_A52; - return 1; + return header_len; - } else if (m->descriptor_tag == HDMV_AUDIO_82_DTS || + } else if (m->descriptor_tag == STREAM_AUDIO_DTS || + m->descriptor_tag == HDMV_AUDIO_82_DTS || m->descriptor_tag == HDMV_AUDIO_86_DTS_HD_MA ) { - m->size = packet_len; m->type |= BUF_AUDIO_DTS; - return 1; + return header_len; } else if (packet_len < 2) { return 0; @@ -943,45 +1112,42 @@ static int demux_ts_parse_pes_header (xine_t *xine, demux_ts_media *m, return 0; } - m->size = packet_len - 4; m->type |= BUF_AUDIO_LPCM_BE; m->buf->decoder_flags |= BUF_FLAG_SPECIAL; m->buf->decoder_info[1] = BUF_SPECIAL_LPCM_CONFIG; m->buf->decoder_info[2] = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0]; - return 1; + m->pes_bytes_left -= 4; + return header_len + 4; } else if (m->descriptor_tag == ISO_13818_PES_PRIVATE && p[0] == 0x20 && p[1] == 0x00) { /* DVBSUB */ - long payload_len = ((buf[4] << 8) | buf[5]) - header_len - 3; - - m->size = packet_len; m->type |= BUF_SPU_DVB; - m->buf->decoder_info[2] = payload_len; - return 1; + m->buf->decoder_info[2] = m->pes_bytes_left; + return header_len; } else if (p[0] == 0x0B && p[1] == 0x77) { /* ac3 - syncword */ - m->size = packet_len; m->type |= BUF_AUDIO_A52; - return 1; + return header_len; } else if ((p[0] & 0xE0) == 0x20) { spu_id = (p[0] & 0x1f); - m->size = packet_len-1; m->type = BUF_SPU_DVD + spu_id; - return 1; + m->pes_bytes_left -= 1; + return header_len + 1; + } else if ((p[0] & 0xF0) == 0x80) { if (packet_len < 4) { return 0; } - m->size = packet_len - 4; m->type |= BUF_AUDIO_A52; - return 1; + m->pes_bytes_left -= 4; + return header_len + 4; #if 0 /* commented out: does not set PCM type. Decoder can't handle raw PCM stream without configuration. */ @@ -1000,15 +1166,14 @@ static int demux_ts_parse_pes_header (xine_t *xine, demux_ts_media *m, return 0; } - m->size = packet_len-pcm_offset; m->type |= BUF_AUDIO_LPCM_BE; - return 1; + m->pes_bytes_left -= pcm_offset; + return header_len + pcm_offset; #endif } } else if ((stream_id & 0xf0) == 0xe0) { - m->size = packet_len; switch (m->descriptor_tag) { case ISO_11172_VIDEO: case ISO_13818_VIDEO: @@ -1029,11 +1194,10 @@ static int demux_ts_parse_pes_header (xine_t *xine, demux_ts_media *m, m->type = BUF_VIDEO_MPEG; break; } - return 1; + return header_len; } else if ((stream_id & 0xe0) == 0xc0) { - m->size = packet_len; switch (m->descriptor_tag) { case ISO_11172_AUDIO: case ISO_13818_AUDIO: @@ -1053,7 +1217,7 @@ static int demux_ts_parse_pes_header (xine_t *xine, demux_ts_media *m, m->type |= BUF_AUDIO_MPEG; break; } - return 1; + return header_len; } else { #ifdef TS_LOG @@ -1114,7 +1278,9 @@ static void demux_ts_buffer_pes(demux_ts_t*this, unsigned char *ts, /* allocate the buffer here, as pes_header needs a valid buf for dvbsubs */ m->buf = m->fifo->buffer_pool_alloc(m->fifo); - if (!demux_ts_parse_pes_header(this->stream->xine, m, ts, len)) { + int pes_header_len = demux_ts_parse_pes_header(this->stream->xine, m, ts, len); + + if (pes_header_len <= 0) { m->buf->free_buffer(m->buf); m->buf = NULL; @@ -1124,8 +1290,10 @@ static void demux_ts_buffer_pes(demux_ts_t*this, unsigned char *ts, } else { m->corrupted_pes = 0; - memcpy(m->buf->mem, ts+len-m->size, m->size); - m->buf->size = m->size; + + /* skip PES header */ + ts += pes_header_len; + len -= pes_header_len; update_extra_info(this, m); @@ -1135,41 +1303,29 @@ static void demux_ts_buffer_pes(demux_ts_t*this, unsigned char *ts, if (m->pid == this->tbre_pid) demux_ts_tbre_update (this, TBRE_MODE_AUDIO_PTS, m->pts); } + } - } else if (!m->corrupted_pes) { /* no pus -- PES packet continuation */ + if (!m->corrupted_pes) { - if ((m->buf->size + len) > MAX_PES_BUF_SIZE) { + if ((m->buf->size + len) > m->buf->max_size) { + m->pes_bytes_left -= m->buf->size; demux_ts_send_buffer(m, 0); m->buf = m->fifo->buffer_pool_alloc(m->fifo); } + memcpy(m->buf->mem + m->buf->size, ts, len); m->buf->size += len; - } -} - -/* - * Create a buffer for a PES stream. - */ -static void demux_ts_pes_new(demux_ts_t*this, - unsigned int mediaIndex, - unsigned int pid, - fifo_buffer_t *fifo, - uint16_t descriptor) { - demux_ts_media *m = &this->media[mediaIndex]; - - /* new PID seen - initialise stuff */ - m->pid = pid; - m->fifo = fifo; - - if (m->buf != NULL) m->buf->free_buffer(m->buf); - m->buf = NULL; - m->counter = INVALID_CC; - m->descriptor_tag = descriptor; - m->corrupted_pes = 1; + if (m->pes_bytes_left > 0 && m->buf->size >= m->pes_bytes_left) { + /* PES payload complete */ + m->pes_bytes_left -= m->buf->size; + demux_ts_flush_media(m); + /* skip rest data - there shouldn't be any */ + m->corrupted_pes = 1; + } + } } - /* Find the first ISO 639 language descriptor (tag 10) and * store the 3-char code in dest, nullterminated. If no * code is found, zero out dest. @@ -1260,11 +1416,13 @@ static void demux_ts_parse_pmt (demux_ts_t *this, unsigned int pusi, uint32_t program_count) { +#ifdef TS_PMT_LOG uint32_t table_id; + uint32_t version_number; +#endif uint32_t section_syntax_indicator; uint32_t section_length = 0; /* to calm down gcc */ uint32_t program_number; - uint32_t version_number; uint32_t current_next_indicator; uint32_t section_number; uint32_t last_section_number; @@ -1279,6 +1437,7 @@ static void demux_ts_parse_pmt (demux_ts_t *this, char *ptr = NULL; unsigned char len; unsigned int offset=0; + int mi; /* * A new section should start with the payload unit start @@ -1293,11 +1452,15 @@ static void demux_ts_parse_pmt (demux_ts_t *this, this->pmt[program_count] = (uint8_t *) calloc(4096, sizeof(unsigned char)); this->pmt_write_ptr[program_count] = this->pmt[program_count]; +#ifdef TS_PMT_LOG table_id = pkt[5] ; +#endif section_syntax_indicator = (pkt[6] >> 7) & 0x01; section_length = (((uint32_t) pkt[6] << 8) | pkt[7]) & 0x03ff; program_number = ((uint32_t) pkt[8] << 8) | pkt[9]; +#ifdef TS_PMT_LOG version_number = (pkt[10] >> 1) & 0x1f; +#endif current_next_indicator = pkt[10] & 0x01; section_number = pkt[11]; last_section_number = pkt[12]; @@ -1425,10 +1588,12 @@ printf("Program Number is %i, looking for %i\n",program_number,this->program_num * PMT has changed (e.g. an IPTV streamer that's just changed its source), * we'll get new PIDs that we should follow. */ - this->audio_tracks_count = 0; this->videoPid = INVALID_PID; this->spu_pid = INVALID_PID; + this->spu_langs_count = 0; + reset_track_map(this->video_fifo); + /* * ES definitions start here...we are going to learn upto one video * PID and one audio PID. @@ -1454,7 +1619,6 @@ printf("Program Number is %i, looking for %i\n",program_number,this->program_num /* * Extract the elementary streams. */ - this->spu_langs_count = 0; while (section_length > 0) { unsigned int stream_info_length; @@ -1481,9 +1645,12 @@ printf("Program Number is %i, looking for %i\n",program_number,this->program_num #ifdef TS_PMT_LOG printf ("demux_ts: PMT video pid 0x%.4x type %2.2x\n", pid, stream[0]); #endif - demux_ts_pes_new(this, this->media_num, pid, this->video_fifo,stream[0]); - this->videoMedia = this->media_num; - this->videoPid = pid; + + mi = demux_ts_dynamic_pmt_find (this, pid, BUF_VIDEO_BASE, stream[0]); + if (mi >= 0) { + this->videoMedia = mi; + this->videoPid = pid; + } } break; @@ -1492,18 +1659,17 @@ printf("Program Number is %i, looking for %i\n",program_number,this->program_num case ISO_13818_PART7_AUDIO: case ISO_14496_PART3_AUDIO: if (this->audio_tracks_count < MAX_AUDIO_TRACKS) { - if (apid_check(this, pid) < 0) { + + mi = demux_ts_dynamic_pmt_find (this, pid, BUF_AUDIO_BASE, stream[0]); + if (mi >= 0) { #ifdef TS_PMT_LOG - printf ("demux_ts: PMT audio pid 0x%.4x type %2.2x\n", pid, stream[0]); + printf ("demux_ts: PMT audio pid 0x%.4x type %2.2x\n", pid, stream[0]); #endif - demux_ts_pes_new(this, this->media_num, pid, this->audio_fifo,stream[0]); - this->audio_tracks[this->audio_tracks_count].pid = pid; - this->audio_tracks[this->audio_tracks_count].media_index = this->media_num; - this->media[this->media_num].type = this->audio_tracks_count; - demux_ts_get_lang_desc(this, this->audio_tracks[this->audio_tracks_count].lang, - stream + 5, stream_info_length); - this->audio_tracks_count++; + demux_ts_get_lang_desc (this, + this->audio_tracks[this->media[mi].type & 0xff].lang, + stream + 5, stream_info_length); } + } break; case ISO_13818_PRIVATE: @@ -1522,28 +1688,23 @@ printf("Program Number is %i, looking for %i\n",program_number,this->program_num break; case ISO_13818_PES_PRIVATE: for (i = 5; i < coded_length; i += stream[i+1] + 2) { - if (((stream[i] == DESCRIPTOR_AC3) || (stream[i] == DESCRIPTOR_EAC3)) && - (this->audio_tracks_count < MAX_AUDIO_TRACKS)) { - if (apid_check(this, pid) < 0) { + + if ((stream[i] == DESCRIPTOR_AC3) || (stream[i] == DESCRIPTOR_EAC3) || (stream[i] == DESCRIPTOR_DTS)) { + mi = demux_ts_dynamic_pmt_find (this, pid, BUF_AUDIO_BASE, + stream[i] == DESCRIPTOR_AC3 ? STREAM_AUDIO_AC3 : + stream[i] == DESCRIPTOR_DTS ? STREAM_AUDIO_DTS : + STREAM_AUDIO_EAC3); + if (mi >= 0) { #ifdef TS_PMT_LOG printf ("demux_ts: PMT AC3 audio pid 0x%.4x type %2.2x\n", pid, stream[0]); #endif - if (stream[i] == DESCRIPTOR_AC3) - demux_ts_pes_new(this, this->media_num, pid, - this->audio_fifo, STREAM_AUDIO_AC3); - else - demux_ts_pes_new(this, this->media_num, pid, - this->audio_fifo, HDMV_AUDIO_84_EAC3); - - this->audio_tracks[this->audio_tracks_count].pid = pid; - this->audio_tracks[this->audio_tracks_count].media_index = this->media_num; - this->media[this->media_num].type = this->audio_tracks_count; - demux_ts_get_lang_desc(this, this->audio_tracks[this->audio_tracks_count].lang, - stream + 5, stream_info_length); - this->audio_tracks_count++; + demux_ts_get_lang_desc (this, + this->audio_tracks[this->media[mi].type & 0xff].lang, + stream + 5, stream_info_length); break; } } + /* Teletext */ else if (stream[i] == DESCRIPTOR_TELETEXT) { @@ -1561,6 +1722,10 @@ printf("Program Number is %i, looking for %i\n",program_number,this->program_num else if (stream[i] == DESCRIPTOR_DVBSUB) { int pos; + + mi = demux_ts_dynamic_pmt_find (this, pid, BUF_SPU_BASE, stream[0]); + if (mi < 0) break; + for (pos = i + 2; pos + 8 <= i + 2 + stream[i + 1] && this->spu_langs_count < MAX_SPU_LANGS; @@ -1578,9 +1743,7 @@ printf("Program Number is %i, looking for %i\n",program_number,this->program_num lang->desc.aux_page_id = (stream[pos + 6] << 8) | stream[pos + 7]; lang->pid = pid; - lang->media_index = this->media_num; - this->media[this->media_num].type = no; - demux_ts_pes_new(this, this->media_num, pid, this->video_fifo, stream[0]); + lang->media_index = mi; demux_send_special_spu_buf( this, BUF_SPU_DVB, no ); #ifdef TS_LOG printf("demux_ts: DVBSUB: pid 0x%.4x: %s page %ld %ld type %2.2x\n", @@ -1615,15 +1778,17 @@ printf("Program Number is %i, looking for %i\n",program_number,this->program_num break; } + mi = demux_ts_dynamic_pmt_find (this, pid, BUF_SPU_BASE, stream[0]); + if (mi < 0) break; + + demux_ts_spu_lang *lang = &this->spu_langs[this->spu_langs_count]; memset(lang->desc.lang, 0, sizeof(lang->desc.lang)); /*memcpy(lang->desc.lang, &stream[pos], 3);*/ /*lang->desc.lang[3] = 0;*/ lang->pid = pid; - lang->media_index = this->media_num; - this->media[this->media_num].type = this->spu_langs_count; - demux_ts_pes_new(this, this->media_num, pid, this->video_fifo, stream[0]); + lang->media_index = mi; demux_send_special_spu_buf( this, BUF_SPU_HDMV, this->spu_langs_count ); this->spu_langs_count++; #ifdef TS_PMT_LOG @@ -1642,43 +1807,43 @@ printf("Program Number is %i, looking for %i\n",program_number,this->program_num * if is does, we tag this as an audio stream. * FIXME: This will need expanding if we ever see a DTS or other media format here. */ - if ((this->audio_tracks_count < MAX_AUDIO_TRACKS) && (stream[0] >= 0x80) ) { - if (apid_check(this,pid) < 0) { - uint32_t format_identifier=0; - demux_ts_get_reg_desc(this, &format_identifier, - stream + 5, stream_info_length); - /* If no format identifier, assume A52 */ - if (( format_identifier == 0x41432d33) || - ( format_identifier == 0) || - ((format_identifier == 0x48444d56 || this->hdmv>0) && stream[0] == HDMV_AUDIO_80_PCM) /* BluRay PCM */) { - - demux_ts_pes_new(this, this->media_num, pid, this->audio_fifo, stream[0]); - this->audio_tracks[this->audio_tracks_count].pid = pid; - this->audio_tracks[this->audio_tracks_count].media_index = this->media_num; - this->media[this->media_num].type = this->audio_tracks_count; - demux_ts_get_lang_desc(this, this->audio_tracks[this->audio_tracks_count].lang, - stream + 5, stream_info_length); - this->audio_tracks_count++; - break; - } + if ((this->audio_tracks_count < MAX_AUDIO_TRACKS) && (stream[0] >= 0x80) ) { + + uint32_t format_identifier=0; + demux_ts_get_reg_desc(this, &format_identifier, stream + 5, stream_info_length); + /* If no format identifier, assume A52 */ + if (( format_identifier == 0x41432d33) || + ( format_identifier == 0) || + ((format_identifier == 0x48444d56 || this->hdmv>0) && stream[0] == HDMV_AUDIO_80_PCM) /* BluRay PCM */) { + + mi = demux_ts_dynamic_pmt_find (this, pid, BUF_AUDIO_BASE, stream[0]); + if (mi >= 0) { + demux_ts_get_lang_desc (this, + this->audio_tracks[this->media[mi].type & 0xff].lang, + stream + 5, stream_info_length); +#ifdef TS_PMT_LOG + printf ("demux_ts: PMT audio pid 0x%.4x type %2.2x\n", pid, stream[0]); +#endif + break; + } } - } else { + } #ifdef TS_PMT_LOG - printf ("demux_ts: PMT unknown stream_type: 0x%.2x pid: 0x%.4x\n", - stream[0], pid); + printf ("demux_ts: PMT unknown stream_type: 0x%.2x pid: 0x%.4x\n", + stream[0], pid); - for (i = 5; i < coded_length; i++) - printf ("%.2x ", stream[i]); - printf ("\n"); + for (i = 5; i < coded_length; i++) + printf ("%.2x ", stream[i]); + printf ("\n"); #endif - } break; } - this->media_num++; stream += coded_length; section_length -= coded_length; } + demux_ts_dynamic_pmt_clean (this); + /* * Get the current PCR PID. */ @@ -1812,8 +1977,19 @@ static unsigned char * demux_synchronise(demux_ts_t* this) { this->frame_pos = this->input->get_current_pos (this->input); read_length = this->input->read(this->input, this->buf, - this->pkt_size * NPKT_PER_READ); - if (read_length < 0 || read_length % this->pkt_size) { + this->pkt_size * NPKT_PER_READ); + + if (read_length < 0) { + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + "demux_ts: read returned %d\n", read_length); + if (this->read_retries > 2) + this->status = DEMUX_FINISHED; + this->read_retries++; + return NULL; + } + this->read_retries = 0; + + if (read_length % this->pkt_size) { xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: read returned %d bytes (not a multiple of %d!)\n", read_length, this->pkt_size); @@ -1832,6 +2008,7 @@ static unsigned char * demux_synchronise(demux_ts_t* this) { */ if (this->npkt_read == 0) { + demux_ts_flush(this); xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: read 0 packets\n"); this->status = DEMUX_FINISHED; return NULL; @@ -1964,7 +2141,9 @@ static void demux_ts_parse_packet (demux_ts_t*this) { unsigned int sync_byte; unsigned int transport_error_indicator; unsigned int payload_unit_start_indicator; +#ifdef TS_HEADER_LOG unsigned int transport_priority; +#endif unsigned int pid; unsigned int transport_scrambling_control; unsigned int adaptation_field_control; @@ -1982,7 +2161,9 @@ static void demux_ts_parse_packet (demux_ts_t*this) { sync_byte = originalPkt[0]; transport_error_indicator = (originalPkt[1] >> 7) & 0x01; payload_unit_start_indicator = (originalPkt[1] >> 6) & 0x01; +#ifdef TS_HEADER_LOG transport_priority = (originalPkt[1] >> 5) & 0x01; +#endif pid = ((originalPkt[1] << 8) | originalPkt[2]) & 0x1fff; transport_scrambling_control = (originalPkt[3] >> 6) & 0x03; @@ -2154,15 +2335,8 @@ static void demux_ts_event_handler (demux_ts_t *this) { case XINE_EVENT_PIDS_CHANGE: - this->videoPid = INVALID_PID; - this->pcr_pid = INVALID_PID; - this->audio_tracks_count = 0; - this->media_num = 0; + demux_ts_dynamic_pmt_clear(this); this->send_newpts = 1; - this->spu_pid = INVALID_PID; - this->spu_media = 0; - this->spu_langs_count= 0; - this->last_pmt_crc = 0; _x_demux_control_start (this->stream); break; @@ -2297,6 +2471,7 @@ static int demux_ts_seek (demux_plugin_t *this_gen, m->buf = NULL; m->counter = INVALID_CC; m->corrupted_pes = 1; + m->pts = 0; } if( !playing ) { diff --git a/src/input/Makefile.am b/src/input/Makefile.am index 10c4f15ec..9700f6f58 100644 --- a/src/input/Makefile.am +++ b/src/input/Makefile.am @@ -56,6 +56,10 @@ if DVB in_dvb = xineplug_inp_dvb.la endif +if HAVE_LIBBLURAY +in_bluray = xineplug_inp_bluray.la +endif + AM_CFLAGS = -D_LARGEFILE64_SOURCE $(GNOME_VFS_CFLAGS) $(ALSA_CFLAGS) $(DVD_CFLAGS) xineplug_LTLIBRARIES = \ @@ -75,6 +79,7 @@ xineplug_LTLIBRARIES = \ xineplug_inp_net.la \ $(in_pvr) \ $(in_dvb) \ + $(in_bluray) \ xineplug_inp_cdda.la @@ -163,6 +168,11 @@ xineplug_inp_pvr_la_LIBADD = $(XINE_LIB) $(PTHREAD_LIBS) $(LTLIBINTL) xineplug_inp_pvr_la_CFLAGS = $(VISIBILITY_FLAG) $(AM_CFLAGS) xineplug_inp_pvr_la_LDFLAGS = $(xineplug_ldflags) +xineplug_inp_bluray_la_SOURCES = input_bluray.c media_helper.c +xineplug_inp_bluray_la_LIBADD = $(XINE_LIB) $(LIBBLURAY_LIBS) $(PTHREAD_LIBS) $(LTLIBINTL) +xineplug_inp_bluray_la_CFLAGS = $(VISIBILITY_FLAG) $(AM_CFLAGS) $(LIBBLURAY_CFLAGS) +xineplug_inp_bluray_la_LDFLAGS = $(xineplug_ldflags) + xineinclude_HEADERS = input_plugin.h noinst_HEADERS = net_buf_ctrl.h mms.h mmsh.h pnm.h media_helper.h http_helper.h diff --git a/src/input/input_bluray.c b/src/input/input_bluray.c new file mode 100644 index 000000000..a05307a4f --- /dev/null +++ b/src/input/input_bluray.c @@ -0,0 +1,1744 @@ +/* + * Copyright (C) 2000-2011 the xine project + * Copyright (C) 2009-2011 Petri Hintukainen <phintuka@users.sourceforge.net> + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * Input plugin for BluRay discs / images + * + * Requires libbluray 0.2.1 or later: + * http://www.videolan.org/developers/libbluray.html + * git://git.videolan.org/libbluray.git + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <pthread.h> + +/* libbluray */ +#include <libbluray/bluray.h> +#include <libbluray/bluray-version.h> +#include <libbluray/keys.h> +#include <libbluray/overlay.h> +#include <libbluray/meta_data.h> + +/* xine */ + +#define LOG_MODULE "input_bluray" +#define LOG_VERBOSE + +#define LOG + +#define LOGMSG(x...) xine_log (this->stream->xine, XINE_LOG_MSG, "input_bluray: " x); + +#define XINE_ENGINE_INTERNAL + +#include "xine_internal.h" +#include "xineutils.h" +#include "input_plugin.h" +#include "media_helper.h" + +/* */ + +#ifndef MIN +# define MIN(a,b) ((a)<(b)?(a):(b)) +#endif +#ifndef MAX +# define MAX(a,b) ((a)>(b)?(a):(b)) +#endif + +#define ALIGNED_UNIT_SIZE 6144 +#define PKT_SIZE 192 +#define TICKS_IN_MS 45 + +#define MIN_TITLE_LENGTH 180 + +/* */ + +typedef struct { + + input_class_t input_class; + + xine_t *xine; + + xine_mrl_t **xine_playlist; + int xine_playlist_size; + + /* config */ + char *mountpoint; + char *device; + char *language; + char *country; + int region; + int parental; +} bluray_input_class_t; + +typedef struct { + input_plugin_t input_plugin; + + bluray_input_class_t *class; + + xine_stream_t *stream; + xine_event_queue_t *event_queue; + xine_osd_t *osd[2]; + + char *mrl; + char *disc_root; + char *disc_name; + + BLURAY *bdh; + + const BLURAY_DISC_INFO *disc_info; + const META_DL *meta_dl; /* disc library meta data */ + + int num_title_idx; /* number of relevant playlists */ + int current_title_idx; + int num_titles; /* navigation mode, number of titles in disc index */ + int current_title; /* navigation mode, title from disc index */ + BLURAY_TITLE_INFO *title_info; + pthread_mutex_t title_info_mutex; /* lock this when accessing title_info outside of input/demux thread */ + unsigned int current_clip; + time_t still_end_time; + int pg_stream; + + uint8_t nav_mode : 1; + uint8_t error : 1; + uint8_t menu_open : 1; + uint8_t stream_flushed : 1; + uint8_t demux_action_req : 1; + uint8_t end_of_title : 1; + uint8_t pg_enable : 1; + int mouse_inside_button; +} bluray_input_plugin_t; + +/* + * overlay + */ + +#define PALETTE_INDEX_BACKGROUND 0xff + +static void send_num_buttons(bluray_input_plugin_t *this, int n) +{ + xine_event_t event; + xine_ui_data_t data; + + event.type = XINE_EVENT_UI_NUM_BUTTONS; + event.data = &data; + event.data_length = sizeof(data); + data.num_buttons = n; + + xine_event_send(this->stream, &event); +} + +static void clear_overlay(xine_osd_t *osd) +{ + /* palette entry 0xff is background --> can't use xine_osd_clear(). */ + memset(osd->osd.area, PALETTE_INDEX_BACKGROUND, osd->osd.width * osd->osd.height); + osd->osd.x1 = osd->osd.width; + osd->osd.y1 = osd->osd.height; + osd->osd.x2 = 0; + osd->osd.y2 = 0; +} + +static xine_osd_t *get_overlay(bluray_input_plugin_t *this, int plane) +{ + if (!this->osd[plane]) { + this->osd[plane] = xine_osd_new(this->stream, 0, 0, 1920, 1080); + clear_overlay(this->osd[plane]); + } + if (!this->pg_enable) { + _x_select_spu_channel(this->stream, -1); + } + return this->osd[plane]; +} + +static void close_overlay(bluray_input_plugin_t *this, int plane) +{ + if (plane < 0) { + close_overlay(this, 0); + close_overlay(this, 1); + return; + } + + if (plane < 2 && this->osd[plane]) { + xine_osd_free(this->osd[plane]); + this->osd[plane] = NULL; + if (plane == 1) { + send_num_buttons(this, 0); + this->menu_open = 0; + } + } +} + +static void open_overlay(bluray_input_plugin_t *this, const BD_OVERLAY * const ov) +{ + lprintf("open_overlay(%d,%d)\n", ov->w, ov->h); + + if (this->osd[ov->plane]) { + close_overlay(this, ov->plane); + } + + this->osd[ov->plane] = xine_osd_new(this->stream, ov->x, ov->y, ov->w, ov->h); + clear_overlay(this->osd[ov->plane]); +} + +static void draw_bitmap(xine_osd_t *osd, const BD_OVERLAY * const ov) +{ + unsigned i; + + /* convert and set palette */ + if (ov->palette) { + uint32_t color[256]; + uint8_t trans[256]; + for(i = 0; i < 256; i++) { + trans[i] = ov->palette[i].T; + color[i] = (ov->palette[i].Y << 16) | (ov->palette[i].Cr << 8) | ov->palette[i].Cb; + } + + xine_osd_set_palette(osd, color, trans); + } + + /* uncompress and draw bitmap */ + if (ov->img) { + const BD_PG_RLE_ELEM *rlep = ov->img; + uint8_t *img = malloc(ov->w * ov->h); + unsigned pixels = ov->w * ov->h; + + for (i = 0; i < pixels; i += rlep->len, rlep++) { + memset(img + i, rlep->color, rlep->len); + } + + xine_osd_draw_bitmap(osd, img, ov->x, ov->y, ov->w, ov->h, NULL); + + free(img); + } +} + +static void overlay_proc(void *this_gen, const BD_OVERLAY * const ov) +{ + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + xine_osd_t *osd; + + if (!this) { + return; + } + + if (!ov) { + /* hide OSD */ + close_overlay(this, -1); + return; + } + + if (ov->plane > 1) { + return; + } + + switch (ov->cmd) { + case BD_OVERLAY_INIT: + open_overlay(this, ov); + return; + case BD_OVERLAY_CLOSE: + close_overlay(this, ov->plane); + return; + } + + osd = get_overlay(this, ov->plane); + + switch (ov->cmd) { + case BD_OVERLAY_DRAW: + draw_bitmap(osd, ov); + return; + + case BD_OVERLAY_WIPE: + xine_osd_draw_rect(osd, ov->x, ov->y, ov->x + ov->w - 1, ov->y + ov->h - 1, PALETTE_INDEX_BACKGROUND, 1); + return; + + case BD_OVERLAY_CLEAR: + xine_osd_hide(osd, 0); + clear_overlay(osd); + return; + + case BD_OVERLAY_FLUSH: + xine_osd_show(osd, 0); + + if (ov->plane == 1) { + this->menu_open = 1; + send_num_buttons(this, 1); + } + return; + + default: + lprintf("unknown overlay command %d\n", ov->cmd); + return; + } +} + +/* + * stream info + */ + +static void update_stream_info(bluray_input_plugin_t *this) +{ + if (this->title_info) { + /* set stream info */ + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_ANGLE_COUNT, this->title_info->angle_count); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_ANGLE_NUMBER, bd_get_current_angle(this->bdh)); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_CHAPTERS, this->title_info->chapter_count > 0); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_CHAPTER_COUNT, this->title_info->chapter_count); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_CHAPTER_NUMBER, bd_get_current_chapter(this->bdh) + 1); + } +} + +static void update_title_name(bluray_input_plugin_t *this) +{ + char title_name[64] = ""; + xine_ui_data_t udata; + xine_event_t uevent = { + .type = XINE_EVENT_UI_SET_TITLE, + .stream = this->stream, + .data = &udata, + .data_length = sizeof(udata) + }; + + /* check disc library metadata */ + if (this->meta_dl) { + unsigned i; + for (i = 0; i < this->meta_dl->toc_count; i++) + if (this->meta_dl->toc_entries[i].title_number == (unsigned)this->current_title) + if (this->meta_dl->toc_entries[i].title_name) + if (strlen(this->meta_dl->toc_entries[i].title_name) > 2) + strncpy(title_name, this->meta_dl->toc_entries[i].title_name, sizeof(title_name)); + } + + /* title name */ + if (title_name[0]) { + } else if (this->current_title == BLURAY_TITLE_TOP_MENU) { + strcpy(title_name, "Top Menu"); + } else if (this->current_title == BLURAY_TITLE_FIRST_PLAY) { + strcpy(title_name, "First Play"); + } else if (this->nav_mode) { + snprintf(title_name, sizeof(title_name), "Title %d/%d (PL %d/%d)", + this->current_title, this->num_titles, + this->current_title_idx + 1, this->num_title_idx); + } else { + snprintf(title_name, sizeof(title_name), "Title %d/%d", + this->current_title_idx + 1, this->num_title_idx); + } + + /* disc name */ + if (this->disc_name && this->disc_name[0]) { + udata.str_len = snprintf(udata.str, sizeof(udata.str), "%s, %s", + this->disc_name, title_name); + } else { + udata.str_len = snprintf(udata.str, sizeof(udata.str), "%s", + title_name); + } + + _x_meta_info_set(this->stream, XINE_META_INFO_TITLE, udata.str); + + xine_event_send(this->stream, &uevent); +} + +static void update_title_info(bluray_input_plugin_t *this, int playlist_id) +{ + /* update title_info */ + + pthread_mutex_lock(&this->title_info_mutex); + + if (this->title_info) + bd_free_title_info(this->title_info); + + if (playlist_id < 0) + this->title_info = bd_get_title_info(this->bdh, this->current_title_idx, 0); + else + this->title_info = bd_get_playlist_info(this->bdh, playlist_id, 0); + + pthread_mutex_unlock(&this->title_info_mutex); + + if (!this->title_info) { + LOGMSG("bd_get_title_info(%d) failed\n", this->current_title_idx); + return; + } + +#ifdef LOG + int ms = this->title_info->duration / INT64_C(90); + lprintf("Opened title %d. Length %"PRId64" bytes / %02d:%02d:%02d.%03d\n", + this->current_title_idx, bd_get_title_size(this->bdh), + ms / 3600000, (ms % 3600000 / 60000), (ms % 60000) / 1000, ms % 1000); +#endif + + /* calculate and set stream rate */ + + uint64_t rate = bd_get_title_size(this->bdh) * UINT64_C(8) // bits + * INT64_C(90000) + / (uint64_t)(this->title_info->duration); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_BITRATE, rate); + + /* set stream info */ + + if (this->nav_mode) { + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_TITLE_COUNT, this->num_titles); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_TITLE_NUMBER, this->current_title); + } else { + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_TITLE_COUNT, this->num_title_idx); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_TITLE_NUMBER, this->current_title_idx + 1); + } + + update_stream_info(this); + + /* set title name */ + + update_title_name(this); +} + +/* + * libbluray event handling + */ + +static void stream_flush(bluray_input_plugin_t *this) +{ + if (this->stream_flushed || !this->stream) + return; + + lprintf("Stream flush\n"); + + this->stream_flushed = 1; + + xine_event_t event = { + .type = XINE_EVENT_END_OF_CLIP, + .stream = this->stream, + .data = NULL, + .data_length = 0, + }; + xine_event_send (this->stream, &event); + + this->demux_action_req = 1; +} + +static void stream_reset(bluray_input_plugin_t *this) +{ + if (!this || !this->stream) + return; + + lprintf("Stream reset\n"); + + xine_event_t event = { + .type = XINE_EVENT_PIDS_CHANGE, + .stream = this->stream, + .data = NULL, + .data_length = 0, + }; + + if (!this->end_of_title) { + _x_demux_flush_engine(this->stream); + } + + xine_event_send (this->stream, &event); + + this->demux_action_req = 1; +} + +static void wait_secs(bluray_input_plugin_t *this, unsigned seconds) +{ + stream_flush(this); + + if (this->still_end_time) { + if (time(NULL) >= this->still_end_time) { + lprintf("pause end\n"); + this->still_end_time = 0; + bd_read_skip_still(this->bdh); + stream_reset(this); + return; + } + } + + else if (seconds) { + if (seconds > 300) { + seconds = 300; + } + + lprintf("still image, pause for %d seconds\n", seconds); + this->still_end_time = time(NULL) + seconds; + } + + xine_usec_sleep(40*1000); +} + +static void update_spu_channel(bluray_input_plugin_t *this, int channel) +{ + if (this->stream->video_fifo) { + buf_element_t *buf = this->stream->video_fifo->buffer_pool_alloc(this->stream->video_fifo); + buf->type = BUF_CONTROL_SPU_CHANNEL; + buf->decoder_info[0] = channel; + buf->decoder_info[1] = channel; + buf->decoder_info[2] = channel; + + this->stream->video_fifo->put(this->stream->video_fifo, buf); + } +} + +static void update_audio_channel(bluray_input_plugin_t *this, int channel) +{ + if (this->stream->audio_fifo) { + buf_element_t *buf = this->stream->audio_fifo->buffer_pool_alloc(this->stream->audio_fifo); + buf->type = BUF_CONTROL_AUDIO_CHANNEL; + buf->decoder_info[0] = channel; + + this->stream->audio_fifo->put(this->stream->audio_fifo, buf); + } +} + +static void handle_libbluray_event(bluray_input_plugin_t *this, BD_EVENT ev) +{ + switch ((bd_event_e)ev.event) { + + case BD_EVENT_NONE: + break; + + case BD_EVENT_ERROR: + lprintf("BD_EVENT_ERROR\n"); + _x_message (this->stream, XINE_MSG_GENERAL_WARNING, + "Error playing BluRay disc", NULL); + this->error = 1; + return; + + case BD_EVENT_READ_ERROR: + LOGMSG("BD_EVENT_READ_ERROR\n"); + return; + + case BD_EVENT_ENCRYPTED: + lprintf("BD_EVENT_ENCRYPTED\n"); + _x_message (this->stream, XINE_MSG_ENCRYPTED_SOURCE, + "Media stream scrambled/encrypted", NULL); + this->error = 1; + return; + + /* sound effects */ +#if BLURAY_VERSION >= 202 + case BD_EVENT_SOUND_EFFECT: + lprintf("BD_EVENT_SOUND_EFFECT %d\n", ev.param); + break; +#endif + + /* playback control */ + + case BD_EVENT_SEEK: + lprintf("BD_EVENT_SEEK\n"); + this->still_end_time = 0; + stream_reset(this); + break; + + case BD_EVENT_STILL_TIME: + wait_secs(this, ev.param); + break; + + case BD_EVENT_STILL: + lprintf("BD_EVENT_STILL %d\n", ev.param); + int paused = _x_get_fine_speed(this->stream) == XINE_SPEED_PAUSE; + if (paused != ev.param) { + _x_set_fine_speed(this->stream, ev.param ? XINE_SPEED_PAUSE : XINE_SPEED_NORMAL); + } + break; + + /* playback position */ + + case BD_EVENT_ANGLE: + lprintf("BD_EVENT_ANGLE_NUMBER %d\n", ev.param); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_ANGLE_NUMBER, ev.param); + break; + + case BD_EVENT_END_OF_TITLE: + lprintf("BD_EVENT_END_OF_TITLE\n"); + stream_flush(this); + this->end_of_title = 1; + break; + + case BD_EVENT_TITLE: + if (this->nav_mode) { + lprintf("BD_EVENT_TITLE %d\n", ev.param); + this->current_title = ev.param; + } + break; + + case BD_EVENT_PLAYLIST: + lprintf("BD_EVENT_PLAYLIST %d\n", ev.param); + this->current_title_idx = bd_get_current_title(this->bdh); + this->current_clip = 0; + update_title_info(this, ev.param); + stream_reset(this); + this->end_of_title = 0; + break; + + case BD_EVENT_PLAYITEM: + lprintf("BD_EVENT_PLAYITEM %d\n", ev.param); + this->current_clip = ev.param; + this->still_end_time = 0; + break; + + case BD_EVENT_CHAPTER: + lprintf("BD_EVENT_CHAPTER %d\n", ev.param); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_CHAPTER_NUMBER, ev.param); + break; + + /* stream selection */ + + case BD_EVENT_AUDIO_STREAM: + lprintf("BD_EVENT_AUDIO_STREAM %d\n", ev.param); + if (ev.param < 32) { + update_audio_channel(this, ev.param - 1); + } else { + update_audio_channel(this, 0); + } + break; + + case BD_EVENT_PG_TEXTST: + lprintf("BD_EVENT_PG_TEXTST %s\n", ev.param ? "ON" : "OFF"); + this->pg_enable = !!ev.param; + update_spu_channel(this, this->pg_enable ? this->pg_stream : -1); + break; + + case BD_EVENT_PG_TEXTST_STREAM: + lprintf("BD_EVENT_PG_TEXTST_STREAM %d\n", ev.param); + if (ev.param < 64) { + this->pg_stream = ev.param - 1; + } else { + this->pg_stream = -1; + } + if (this->pg_enable) { + update_spu_channel(this, this->pg_stream); + } + break; + + default: + lprintf("unhandled libbluray event %d [param %d]\n", ev.event, ev.param); + break; + } +} + +static void handle_libbluray_events(bluray_input_plugin_t *this) +{ + BD_EVENT ev; + while (bd_get_event(this->bdh, &ev)) { + handle_libbluray_event(this, ev); + if (this->error || ev.event == BD_EVENT_NONE || ev.event == BD_EVENT_ERROR) + break; + } +} + +/* + * xine event handling + */ + +static int open_title (bluray_input_plugin_t *this, int title_idx) +{ + if (bd_select_title(this->bdh, title_idx) <= 0) { + LOGMSG("bd_select_title(%d) failed\n", title_idx); + return 0; + } + + this->current_title_idx = title_idx; + + update_title_info(this, -1); + + return 1; +} + +static void send_mouse_enter_leave_event(bluray_input_plugin_t *this, int direction) +{ + if (direction != this->mouse_inside_button) { + xine_event_t event; + xine_spu_button_t spu_event; + + spu_event.direction = direction; + spu_event.button = 1; + + event.type = XINE_EVENT_SPU_BUTTON; + event.stream = this->stream; + event.data = &spu_event; + event.data_length = sizeof(spu_event); + xine_event_send(this->stream, &event); + + this->mouse_inside_button = direction; + } +} + +static void handle_events(bluray_input_plugin_t *this) +{ + xine_event_t *event; + + if (!this->event_queue) + return; + + while (NULL != (event = xine_event_get(this->event_queue))) { + + if (!this->bdh || !this->title_info) { + xine_event_free(event); + return; + } + + int64_t pts = xine_get_current_vpts(this->stream) - + this->stream->metronom->get_option(this->stream->metronom, METRONOM_VPTS_OFFSET); + + if (this->menu_open) { + switch (event->type) { + case XINE_EVENT_INPUT_LEFT: bd_user_input(this->bdh, pts, BD_VK_LEFT); break; + case XINE_EVENT_INPUT_RIGHT: bd_user_input(this->bdh, pts, BD_VK_RIGHT); break; + } + } else { + switch (event->type) { + + case XINE_EVENT_INPUT_LEFT: + lprintf("XINE_EVENT_INPUT_LEFT: previous title\n"); + if (!this->nav_mode) { + open_title(this, MAX(0, this->current_title_idx - 1)); + } else { + bd_play_title(this->bdh, MAX(1, this->current_title - 1)); + } + stream_reset(this); + break; + + case XINE_EVENT_INPUT_RIGHT: + lprintf("XINE_EVENT_INPUT_RIGHT: next title\n"); + if (!this->nav_mode) { + open_title(this, MIN(this->num_title_idx - 1, this->current_title_idx + 1)); + } else { + bd_play_title(this->bdh, MIN(this->num_titles, this->current_title + 1)); + } + stream_reset(this); + break; + } + } + + switch (event->type) { + + case XINE_EVENT_INPUT_MOUSE_BUTTON: { + xine_input_data_t *input = event->data; + lprintf("mouse click: button %d at (%d,%d)\n", input->button, input->x, input->y); + if (input->button == 1) { + bd_mouse_select(this->bdh, pts, input->x, input->y); + bd_user_input(this->bdh, pts, BD_VK_MOUSE_ACTIVATE); + send_mouse_enter_leave_event(this, 0); + } + break; + } + + case XINE_EVENT_INPUT_MOUSE_MOVE: { + xine_input_data_t *input = event->data; + if (bd_mouse_select(this->bdh, pts, input->x, input->y) > 0) { + send_mouse_enter_leave_event(this, 1); + } else { + send_mouse_enter_leave_event(this, 0); + } + break; + } + + case XINE_EVENT_INPUT_MENU1: + if (!this->disc_info->top_menu_supported) { + _x_message (this->stream, XINE_MSG_GENERAL_WARNING, + "Can't open Top Menu", + "Top Menu title not supported", NULL); + } + bd_menu_call(this->bdh, pts); + break; + + case XINE_EVENT_INPUT_MENU2: bd_user_input(this->bdh, pts, BD_VK_POPUP); break; + case XINE_EVENT_INPUT_UP: bd_user_input(this->bdh, pts, BD_VK_UP); break; + case XINE_EVENT_INPUT_DOWN: bd_user_input(this->bdh, pts, BD_VK_DOWN); break; + case XINE_EVENT_INPUT_SELECT: bd_user_input(this->bdh, pts, BD_VK_ENTER); break; + case XINE_EVENT_INPUT_NUMBER_0: bd_user_input(this->bdh, pts, BD_VK_0); break; + case XINE_EVENT_INPUT_NUMBER_1: bd_user_input(this->bdh, pts, BD_VK_1); break; + case XINE_EVENT_INPUT_NUMBER_2: bd_user_input(this->bdh, pts, BD_VK_2); break; + case XINE_EVENT_INPUT_NUMBER_3: bd_user_input(this->bdh, pts, BD_VK_3); break; + case XINE_EVENT_INPUT_NUMBER_4: bd_user_input(this->bdh, pts, BD_VK_4); break; + case XINE_EVENT_INPUT_NUMBER_5: bd_user_input(this->bdh, pts, BD_VK_5); break; + case XINE_EVENT_INPUT_NUMBER_6: bd_user_input(this->bdh, pts, BD_VK_6); break; + case XINE_EVENT_INPUT_NUMBER_7: bd_user_input(this->bdh, pts, BD_VK_7); break; + case XINE_EVENT_INPUT_NUMBER_8: bd_user_input(this->bdh, pts, BD_VK_8); break; + case XINE_EVENT_INPUT_NUMBER_9: bd_user_input(this->bdh, pts, BD_VK_9); break; + + case XINE_EVENT_INPUT_NEXT: { + cfg_entry_t* entry = this->class->xine->config->lookup_entry(this->class->xine->config, + "media.bluray.skip_behaviour"); + switch (entry->num_value) { + case 0: /* skip by chapter */ + bd_seek_chapter(this->bdh, bd_get_current_chapter(this->bdh) + 1); + update_stream_info(this); + break; + case 1: /* skip by title */ + if (!this->nav_mode) { + open_title(this, MIN(this->num_title_idx - 1, this->current_title_idx + 1)); + } else { + bd_play_title(this->bdh, MIN(this->num_titles, this->current_title + 1)); + } + break; + } + stream_reset(this); + break; + } + + case XINE_EVENT_INPUT_PREVIOUS: { + cfg_entry_t* entry = this->class->xine->config->lookup_entry(this->class->xine->config, + "media.bluray.skip_behaviour"); + switch (entry->num_value) { + case 0: /* skip by chapter */ + bd_seek_chapter(this->bdh, MAX(0, ((int)bd_get_current_chapter(this->bdh)) - 1)); + update_stream_info(this); + break; + case 1: /* skip by title */ + if (!this->nav_mode) { + open_title(this, MAX(0, this->current_title_idx - 1)); + } else { + bd_play_title(this->bdh, MAX(1, this->current_title - 1)); + } + break; + } + stream_reset(this); + break; + } + + case XINE_EVENT_INPUT_ANGLE_NEXT: { + unsigned curr_angle = bd_get_current_angle(this->bdh); + unsigned angle = MIN(8, curr_angle + 1); + lprintf("XINE_EVENT_INPUT_ANGLE_NEXT: set angle %d --> %d\n", curr_angle, angle); + bd_seamless_angle_change(this->bdh, angle); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_ANGLE_NUMBER, bd_get_current_angle(this->bdh)); + break; + } + + case XINE_EVENT_INPUT_ANGLE_PREVIOUS: { + unsigned curr_angle = bd_get_current_angle(this->bdh); + unsigned angle = curr_angle ? curr_angle - 1 : 0; + lprintf("XINE_EVENT_INPUT_ANGLE_PREVIOUS: set angle %d --> %d\n", curr_angle, angle); + bd_seamless_angle_change(this->bdh, angle); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_ANGLE_NUMBER, bd_get_current_angle(this->bdh)); + break; + } + } + + xine_event_free(event); + } +} + +/* + * xine plugin interface + */ + +static uint32_t bluray_plugin_get_capabilities (input_plugin_t *this_gen) +{ + return INPUT_CAP_SEEKABLE | + INPUT_CAP_BLOCK | + INPUT_CAP_AUDIOLANG | + INPUT_CAP_SPULANG | + INPUT_CAP_CHAPTERS; +} + +#define CHECK_READ_INTERRUPT \ + do { \ + if (this->demux_action_req) { \ + this->demux_action_req = 0; \ + errno = EAGAIN; \ + return -1; \ + } \ + if (_x_action_pending(this->stream)) { \ + errno = EINTR; \ + return -1; \ + } \ + } while (0) + +static off_t bluray_plugin_read (input_plugin_t *this_gen, char *buf, off_t len) +{ + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + off_t result; + + if (!this || !this->bdh || len < 0 || this->error) + return -1; + + handle_events(this); + CHECK_READ_INTERRUPT; + + if (this->nav_mode) { + do { + BD_EVENT ev; + result = bd_read_ext (this->bdh, (unsigned char *)buf, len, &ev); + handle_libbluray_event(this, ev); + CHECK_READ_INTERRUPT; + + if (result == 0) { + handle_events(this); + CHECK_READ_INTERRUPT; +#if 0 + if (ev.event == BD_EVENT_NONE) { + if (_x_action_pending(this->stream)) { + break; + } + } +#endif + } + } while (!this->error && result == 0); + + } else { + result = bd_read (this->bdh, (unsigned char *)buf, len); + handle_libbluray_events(this); + } + + if (result < 0) { + LOGMSG("bd_read() failed: %s (%d of %d)\n", strerror(errno), (int)result, (int)len); + } + + if (result > 0) { + this->stream_flushed = 0; + } + + return result; +} + +static buf_element_t *bluray_plugin_read_block (input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t todo) +{ + buf_element_t *buf = fifo->buffer_pool_alloc (fifo); + + if (todo > (off_t)buf->max_size) + todo = buf->max_size; + + if (todo > ALIGNED_UNIT_SIZE) + todo = ALIGNED_UNIT_SIZE; + + if (todo > 0) { + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + + buf->size = bluray_plugin_read(this_gen, (char*)buf->mem, todo); + buf->type = BUF_DEMUX_BLOCK; + + if (buf->size > 0) { + buf->extra_info->total_time = this->title_info->duration / 90000; + return buf; + } + } + + buf->free_buffer (buf); + return NULL; +} + +static off_t bluray_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) +{ + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + + if (!this || !this->bdh) + return -1; + if (this->still_end_time) + return offset; + + /* convert relative seeks to absolute */ + + if (origin == SEEK_CUR) { + offset = bd_tell(this->bdh) + offset; + } + else if (origin == SEEK_END) { + if (offset < (off_t)bd_get_title_size(this->bdh)) + offset = bd_get_title_size(this->bdh) - offset; + else + offset = 0; + } + + lprintf("bluray_plugin_seek() seeking to %lld\n", (long long)offset); + + return bd_seek (this->bdh, offset); +} + +static off_t bluray_plugin_seek_time (input_plugin_t *this_gen, int time_offset, int origin) +{ + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + + if (!this || !this->bdh) + return -1; + + if (this->still_end_time) + return bd_tell(this->bdh); + + /* convert relative seeks to absolute */ + + if (origin == SEEK_CUR) { + time_offset += this_gen->get_current_time(this_gen); + } + else if (origin == SEEK_END) { + + pthread_mutex_lock(&this->title_info_mutex); + + if (!this->title_info) { + pthread_mutex_unlock(&this->title_info_mutex); + return -1; + } + + int duration = this->title_info->duration / 90; + if (time_offset < duration) + time_offset = duration - time_offset; + else + time_offset = 0; + + pthread_mutex_unlock(&this->title_info_mutex); + } + + lprintf("bluray_plugin_seek_time() seeking to %d.%03ds\n", time_offset / 1000, time_offset % 1000); + + return bd_seek_time(this->bdh, time_offset * INT64_C(90)); +} + +static off_t bluray_plugin_get_current_pos (input_plugin_t *this_gen) +{ + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + + return this->bdh ? bd_tell(this->bdh) : 0; +} + +static int bluray_plugin_get_current_time (input_plugin_t *this_gen) +{ + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + + return this->bdh ? (int)(bd_tell_time(this->bdh) / UINT64_C(90)) : -1; +} + +static off_t bluray_plugin_get_length (input_plugin_t *this_gen) +{ + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + + return this->bdh ? (off_t)bd_get_title_size(this->bdh) : (off_t)-1; +} + +static uint32_t bluray_plugin_get_blocksize (input_plugin_t *this_gen) +{ + return ALIGNED_UNIT_SIZE; +} + +static const char* bluray_plugin_get_mrl (input_plugin_t *this_gen) +{ + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + + return this->mrl; +} + +static int get_audio_lang (bluray_input_plugin_t *this, int *data) +{ + /* + * audio track language: + * - channel number can be mpeg-ts PID (0x1100 ... 0x11ff) + */ + + unsigned int current_clip = this->current_clip; /* can change any time */ + + if (this->title_info && current_clip < this->title_info->clip_count) { + int channel = *data; + BLURAY_CLIP_INFO *clip = &this->title_info->clips[current_clip]; + + if (channel >= 0 && channel < clip->audio_stream_count) { + memcpy(data, clip->audio_streams[channel].lang, 4); + return INPUT_OPTIONAL_SUCCESS; + } + + /* search by pid */ + int i; + for (i = 0; i < clip->audio_stream_count; i++) { + if (channel == clip->audio_streams[i].pid) { + memcpy(data, clip->audio_streams[i].lang, 4); + return INPUT_OPTIONAL_SUCCESS; + } + } + } + + return INPUT_OPTIONAL_UNSUPPORTED; +} + +static int get_spu_lang (bluray_input_plugin_t *this, int *data) +{ + /* + * SPU track language: + * - channel number can be mpeg-ts PID (0x1200 ... 0x12ff) + */ + + unsigned int current_clip = this->current_clip; /* can change any time */ + + if (this->title_info && current_clip < this->title_info->clip_count) { + int channel = *data; + BLURAY_CLIP_INFO *clip = &this->title_info->clips[current_clip]; + + if (channel >= 0 && channel < clip->pg_stream_count) { + memcpy(data, clip->pg_streams[channel].lang, 4); + return INPUT_OPTIONAL_SUCCESS; + } + + /* search by pid */ + int i; + for (i = 0; i < clip->pg_stream_count; i++) { + if (channel == clip->pg_streams[i].pid) { + memcpy(data, clip->pg_streams[i].lang, 4); + return INPUT_OPTIONAL_SUCCESS; + } + } + } + + return INPUT_OPTIONAL_UNSUPPORTED; +} + +static int get_optional_data_impl (bluray_input_plugin_t *this, void *data, int data_type) +{ + switch (data_type) { + + case INPUT_OPTIONAL_DATA_DEMUXER: + if (data) + *(const char **)data = "mpeg-ts"; + return INPUT_OPTIONAL_SUCCESS; + + case INPUT_OPTIONAL_DATA_AUDIOLANG: + return get_audio_lang(this, data); + + case INPUT_OPTIONAL_DATA_SPULANG: + return get_spu_lang(this, data); + + default: + return INPUT_OPTIONAL_UNSUPPORTED; + } + + return INPUT_OPTIONAL_UNSUPPORTED; +} + +static int bluray_plugin_get_optional_data (input_plugin_t *this_gen, void *data, int data_type) +{ + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + int r = INPUT_OPTIONAL_UNSUPPORTED; + + if (this && this->stream && data) { + pthread_mutex_lock(&this->title_info_mutex); + r = get_optional_data_impl(this, data, data_type); + pthread_mutex_unlock(&this->title_info_mutex); + } + + return r; +} + +static void bluray_plugin_dispose (input_plugin_t *this_gen) +{ + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + + if (this->bdh) + bd_register_overlay_proc(this->bdh, NULL, NULL); + + close_overlay(this, -1); + + if (this->event_queue) + xine_event_dispose_queue(this->event_queue); + + pthread_mutex_lock(&this->title_info_mutex); + if (this->title_info) + bd_free_title_info(this->title_info); + this->title_info = NULL; + pthread_mutex_unlock(&this->title_info_mutex); + + pthread_mutex_destroy(&this->title_info_mutex); + + if (this->bdh) + bd_close(this->bdh); + + free (this->mrl); + free (this->disc_root); + free (this->disc_name); + + free (this); +} + +static int parse_mrl(const char *mrl_in, char **path, int *title, int *chapter) +{ + int skip = 0; + + if (!strncasecmp(mrl_in, "bluray:", 7)) + skip = 7; + else if (!strncasecmp(mrl_in, "bd:", 3)) + skip = 3; + else + return -1; + + char *mrl = strdup(mrl_in + skip); + + /* title[.chapter] given ? parse and drop it */ + if (mrl[strlen(mrl)-1] != '/') { + char *end = strrchr(mrl, '/'); + if (end && end[1]) { + if (sscanf(end, "/%d.%d", title, chapter) < 1) + *title = -1; + else + *end = 0; + } + } + lprintf(" -> title %d, chapter %d, mrl \'%s\'\n", *title, *chapter, mrl); + + if ((mrl[0] == 0) || !strcmp(mrl, "/") || !strcmp(mrl, "//") || !strcmp(mrl, "///")) { + + /* default device */ + *path = NULL; + + } else if (*mrl == '/') { + + /* strip extra slashes */ + char *start = mrl; + while (start[0] == '/' && start[1] == '/') + start++; + + *path = strdup(start); + + _x_mrl_unescape(*path); + + lprintf("non-defaut mount point \'%s\'\n", *path); + + } else { + lprintf("invalid mrl \'%s\'\n", mrl_in); + free(mrl); + return 0; + } + + free(mrl); + + return 1; +} + +static int get_disc_info(bluray_input_plugin_t *this) +{ + const BLURAY_DISC_INFO *disc_info; + + disc_info = bd_get_disc_info(this->bdh); + + if (!disc_info) { + LOGMSG("bd_get_disc_info() failed\n"); + return -1; + } + + if (!disc_info->bluray_detected) { + LOGMSG("bd_get_disc_info(): BluRay not detected\n"); + this->nav_mode = 0; + return 0; + } + + if (disc_info->aacs_detected && !disc_info->aacs_handled) { + if (!disc_info->libaacs_detected) + _x_message (this->stream, XINE_MSG_ENCRYPTED_SOURCE, + "Media stream scrambled/encrypted with AACS", + "libaacs not installed", NULL); + else + _x_message (this->stream, XINE_MSG_ENCRYPTED_SOURCE, + "Media stream scrambled/encrypted with AACS", NULL); + return -1; + } + + if (disc_info->bdplus_detected && !disc_info->bdplus_handled) { + if (!disc_info->libbdplus_detected) + _x_message (this->stream, XINE_MSG_ENCRYPTED_SOURCE, + "Media scrambled/encrypted with BD+", + "libbdplus not installed.", NULL); + else + _x_message (this->stream, XINE_MSG_ENCRYPTED_SOURCE, + "Media stream scrambled/encrypted with BD+", NULL); + return -1; + } + + if (this->nav_mode && !disc_info->first_play_supported) { + _x_message (this->stream, XINE_MSG_GENERAL_WARNING, + "Can't play disc using menus", + "First Play title not supported", NULL); + this->nav_mode = 0; + } + + if (this->nav_mode && disc_info->num_unsupported_titles > 0) { + _x_message (this->stream, XINE_MSG_GENERAL_WARNING, + "Unsupported titles found", + "Some titles can't be played in navigation mode", NULL); + } + + this->num_titles = disc_info->num_hdmv_titles + disc_info->num_bdj_titles; + this->disc_info = disc_info; + + return 1; +} + +static char *get_disc_name(const char *path) +{ + const char *name_start; + char *file_name = NULL; + int len; + + name_start = path + strlen(path) - 1; + /* skip trailing '/' */ + while (name_start > path && name_start[0] == '/') + name_start--; + /* find prev '/' */ + while (name_start > path && name_start[-1] != '/') + name_start--; + + file_name = strdup(name_start); + len = strlen(file_name); + + /* trim trailing '/' */ + while (len > 0 && file_name[len - 1] == '/') + file_name[--len] = 0; + + /* trim trailing ".iso" */ + if (len > 3 && !strcasecmp(file_name + len - 4, ".iso")) + file_name[len - 4] = 0; + + /* '_' --> ' ' */ + for (len = 0; file_name[len]; ++len) + if (file_name[len] == '_') + file_name[len] = ' '; + + lprintf("disc name: %s\n", file_name); + return file_name; +} + +static int is_iso_image(const char *mrl) +{ + if (mrl) { + const char *pos = strrchr(mrl, '.'); + return pos && !strcasecmp(pos + 1, "iso"); + } + return 0; +} + +static int bluray_plugin_open (input_plugin_t *this_gen) +{ + bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen; + int title = -1; + int chapter = 0; + + lprintf("bluray_plugin_open '%s'\n",this->mrl); + + /* validate and parse mrl */ + if (!parse_mrl(this->mrl, &this->disc_root, &title, &chapter)) + return -1; + + if (!strncasecmp(this->mrl, "bd:", 3)) + this->nav_mode = 1; + + if (!this->disc_root) + this->disc_root = strdup(this->class->mountpoint); + + /* mount .iso image */ + if (is_iso_image(this->disc_root)) { + _x_message (this->stream, XINE_MSG_GENERAL_WARNING, + "Can't play BluRay .iso image", + "", NULL); + return -1; + } + + /* open libbluray */ + + if (! (this->bdh = bd_open (this->disc_root, NULL))) { + LOGMSG("bd_open(\'%s\') failed: %s\n", this->disc_root, strerror(errno)); + return -1; + } + lprintf("bd_open(\'%s\') OK\n", this->disc_root); + + if (get_disc_info(this) < 0) { + return -1; + } + + /* load title list */ + + this->num_title_idx = bd_get_titles(this->bdh, TITLES_RELEVANT, MIN_TITLE_LENGTH); + LOGMSG("%d titles\n", this->num_title_idx); + + if (this->num_title_idx < 1) + return -1; + + /* select title */ + + /* if title was not in mrl, guess the main title */ + if (title < 0) { + uint64_t duration = 0; + int i, playlist = 99999; + for (i = 0; i < this->num_title_idx; i++) { + BLURAY_TITLE_INFO *info = bd_get_title_info(this->bdh, i, 0); + if (info->duration > duration) { + title = i; + duration = info->duration; + playlist = info->playlist; + } + bd_free_title_info(info); + } + lprintf("main title: %d (%05d.mpls)\n", title, playlist); + } + + /* update player settings */ + + bd_set_player_setting (this->bdh, BLURAY_PLAYER_SETTING_REGION_CODE, this->class->region); + bd_set_player_setting (this->bdh, BLURAY_PLAYER_SETTING_PARENTAL, this->class->parental); + bd_set_player_setting_str(this->bdh, BLURAY_PLAYER_SETTING_AUDIO_LANG, this->class->language); + bd_set_player_setting_str(this->bdh, BLURAY_PLAYER_SETTING_PG_LANG, this->class->language); + bd_set_player_setting_str(this->bdh, BLURAY_PLAYER_SETTING_MENU_LANG, this->class->language); + bd_set_player_setting_str(this->bdh, BLURAY_PLAYER_SETTING_COUNTRY_CODE, this->class->country); + + /* init event queue */ + bd_get_event(this->bdh, NULL); + + /* get disc name */ + + this->meta_dl = bd_get_meta(this->bdh); + + if (this->meta_dl && this->meta_dl->di_name && strlen(this->meta_dl->di_name) > 1) { + this->disc_name = strdup(this->meta_dl->di_name); + } + else if (strcmp(this->disc_root, this->class->mountpoint)) { + this->disc_name = get_disc_name(this->disc_root); + } + + /* register overlay (graphics) handler */ + + bd_register_overlay_proc(this->bdh, this, overlay_proc); + + /* open */ + this->current_title = -1; + this->current_title_idx = -1; + + if (this->nav_mode) { + if (bd_play(this->bdh) <= 0) { + LOGMSG("bd_play() failed\n"); + return -1; + } + + } else { + if (open_title(this, title) <= 0 && + open_title(this, 0) <= 0) + return -1; + } + + /* jump to chapter */ + + if (chapter > 0) { + chapter = MAX(0, MIN((int)this->title_info->chapter_count, chapter) - 1); + bd_seek_chapter(this->bdh, chapter); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_CHAPTER_NUMBER, chapter + 1); + } + + return 1; +} + +static input_plugin_t *bluray_class_get_instance (input_class_t *cls_gen, xine_stream_t *stream, + const char *mrl) +{ + bluray_input_plugin_t *this; + + lprintf("bluray_class_get_instance\n"); + + if (strncasecmp(mrl, "bluray:", 7) && strncasecmp(mrl, "bd:", 3)) + return NULL; + + this = (bluray_input_plugin_t *) calloc(1, sizeof (bluray_input_plugin_t)); + + this->stream = stream; + this->class = (bluray_input_class_t*)cls_gen; + this->mrl = strdup(mrl); + + this->input_plugin.open = bluray_plugin_open; + this->input_plugin.get_capabilities = bluray_plugin_get_capabilities; + this->input_plugin.read = bluray_plugin_read; + this->input_plugin.read_block = bluray_plugin_read_block; + this->input_plugin.seek = bluray_plugin_seek; + this->input_plugin.seek_time = bluray_plugin_seek_time; + this->input_plugin.get_current_pos = bluray_plugin_get_current_pos; + this->input_plugin.get_current_time = bluray_plugin_get_current_time; + this->input_plugin.get_length = bluray_plugin_get_length; + this->input_plugin.get_blocksize = bluray_plugin_get_blocksize; + this->input_plugin.get_mrl = bluray_plugin_get_mrl; + this->input_plugin.get_optional_data = bluray_plugin_get_optional_data; + this->input_plugin.dispose = bluray_plugin_dispose; + this->input_plugin.input_class = cls_gen; + + this->event_queue = xine_event_new_queue (this->stream); + + pthread_mutex_init(&this->title_info_mutex, NULL); + + this->pg_stream = -1; + + return &this->input_plugin; +} + +/* + * plugin class + */ + +static void mountpoint_change_cb(void *data, xine_cfg_entry_t *cfg) +{ + bluray_input_class_t *class = (bluray_input_class_t *) data; + + class->mountpoint = cfg->str_value; +} + +static void device_change_cb(void *data, xine_cfg_entry_t *cfg) +{ + bluray_input_class_t *class = (bluray_input_class_t *) data; + + class->device = cfg->str_value; +} + +static void language_change_cb(void *data, xine_cfg_entry_t *cfg) +{ + bluray_input_class_t *class = (bluray_input_class_t *) data; + + class->language = cfg->str_value; +} + +static void country_change_cb(void *data, xine_cfg_entry_t *cfg) +{ + bluray_input_class_t *class = (bluray_input_class_t *) data; + + class->country = cfg->str_value; +} + +static void region_change_cb(void *data, xine_cfg_entry_t *cfg) +{ + bluray_input_class_t *class = (bluray_input_class_t *) data; + + class->region = cfg->num_value; +} + +static void parental_change_cb(void *data, xine_cfg_entry_t *cfg) +{ + bluray_input_class_t *class = (bluray_input_class_t *) data; + + class->parental = cfg->num_value; +} + +static void free_xine_playlist(bluray_input_class_t *this) +{ + if (this->xine_playlist) { + int i; + for (i = 0; i < this->xine_playlist_size; i++) { + MRL_ZERO(this->xine_playlist[i]); + free(this->xine_playlist[i]); + } + free(this->xine_playlist); + this->xine_playlist = NULL; + } + + this->xine_playlist_size = 0; +} + +static const char *bluray_class_get_description (input_class_t *this_gen) +{ + return _("BluRay input plugin"); +} + +static const char *bluray_class_get_identifier (input_class_t *this_gen) +{ + return "bluray"; +} + +static char **bluray_class_get_autoplay_list (input_class_t *this_gen, int *num_files) +{ + static char *autoplay_list[] = { "bluray:/", NULL }; + + *num_files = 1; + + return autoplay_list; +} + +static xine_mrl_t **bluray_class_get_dir(input_class_t *this_gen, const char *filename, int *nFiles) +{ + bluray_input_class_t *this = (bluray_input_class_t*) this_gen; + char *path = NULL; + int title = -1, chapter = -1, i, num_pl; + BLURAY *bdh; + + lprintf("bluray_class_get_dir(%s)\n", filename); + + free_xine_playlist(this); + + if (filename) + parse_mrl(filename, &path, &title, &chapter); + + bdh = bd_open(path ? path : this->mountpoint, NULL); + + if (bdh) { + num_pl = bd_get_titles(bdh, TITLES_RELEVANT, MIN_TITLE_LENGTH); + + if (num_pl > 0) { + + this->xine_playlist_size = num_pl; + this->xine_playlist = calloc(this->xine_playlist_size + 1, sizeof(xine_mrl_t*)); + + for (i = 0; i < num_pl; i++) { + this->xine_playlist[i] = calloc(1, sizeof(xine_mrl_t)); + + this->xine_playlist[i]->origin = _x_asprintf("bluray:/%s", path ? path : ""); + this->xine_playlist[i]->mrl = _x_asprintf("bluray:/%s/%d", path ? path : "", i); + this->xine_playlist[i]->type = mrl_dvd; + } + } + + bd_close(bdh); + } + + free(path); + + if (nFiles) + *nFiles = this->xine_playlist_size; + + return this->xine_playlist; +} + +static int bluray_class_eject_media (input_class_t *this_gen) +{ + bluray_input_class_t *this = (bluray_input_class_t*) this_gen; + + return media_eject_media (this->xine, this->device); +} + +static void bluray_class_dispose (input_class_t *this_gen) +{ + bluray_input_class_t *this = (bluray_input_class_t *) this_gen; + config_values_t *config = this->xine->config; + + free_xine_playlist(this); + + config->unregister_callback(config, "media.bluray.mountpoint"); + config->unregister_callback(config, "media.bluray.device"); + config->unregister_callback(config, "media.bluray.region"); + config->unregister_callback(config, "media.bluray.language"); + config->unregister_callback(config, "media.bluray.country"); + config->unregister_callback(config, "media.bluray.parental"); + + free (this); +} + +static void *bluray_init_plugin (xine_t *xine, void *data) +{ + static const char * const skip_modes[] = {"skip chapter", "skip title", NULL}; + + config_values_t *config = xine->config; + bluray_input_class_t *this = (bluray_input_class_t *) calloc(1, sizeof (bluray_input_class_t)); + + this->xine = xine; + + this->input_class.get_instance = bluray_class_get_instance; + this->input_class.get_identifier = bluray_class_get_identifier; + this->input_class.get_description = bluray_class_get_description; + this->input_class.get_dir = bluray_class_get_dir; + this->input_class.get_autoplay_list = bluray_class_get_autoplay_list; + this->input_class.dispose = bluray_class_dispose; + this->input_class.eject_media = bluray_class_eject_media; + + this->mountpoint = + config->register_filename(config, "media.bluray.mountpoint", + "/mnt/bluray", XINE_CONFIG_STRING_IS_DIRECTORY_NAME, + _("BluRay mount point"), + _("Default mount location for BluRay discs."), + 0, mountpoint_change_cb, (void *) this); + this->device = + config->register_filename(config, "media.bluray.device", + "/dev/dvd", XINE_CONFIG_STRING_IS_DIRECTORY_NAME, + _("device used for BluRay playback"), + _("The path to the device " + "which you intend to use for playing BluRy discs."), + 0, device_change_cb, (void *) this); + + /* Player settings */ + this->language = + config->register_string(config, "media.bluray.language", + "eng", + _("default language for BluRay playback"), + _("xine tries to use this language as a default for BluRay playback. " + "As far as the BluRay supports it, menus and audio tracks will be presented " + "in this language.\nThe value must be a three character" + "ISO639-2 language code."), + 0, language_change_cb, this); + this->country = + config->register_string(config, "media.bluray.country", + "en", + _("BluRay player country code"), + _("The value must be a two character ISO3166-1 country code."), + 0, country_change_cb, this); + this->region = + config->register_num(config, "media.bluray.region", + 7, + _("BluRay player region code (1=A, 2=B, 4=C)"), + _("This only needs to be changed if your BluRay jumps to a screen " + "complaining about a wrong region code. It has nothing to do with " + "the region code set in BluRay drives, this is purely software."), + 0, region_change_cb, this); + this->parental = + config->register_num(config, "media.bluray.parental", + 99, + _("parental control age limit (1-99)"), + _("Prevents playback of BluRay titles where parental " + "control age limit is higher than this limit"), + 0, parental_change_cb, this); + + /* */ + config->register_enum(config, "media.bluray.skip_behaviour", 0, + skip_modes, + _("unit for the skip action"), + _("You can configure the behaviour when issuing a skip command (using the skip " + "buttons for example)."), + 20, NULL, NULL); + + return this; +} + +static const char *bd_class_get_description (input_class_t *this_gen) +{ + return _("BluRay input plugin (using menus)"); +} + +static const char *bd_class_get_identifier (input_class_t *this_gen) +{ + return "bd"; +} + +static char **bd_class_get_autoplay_list (input_class_t *this_gen, int *num_files) +{ + static char *autoplay_list[] = { "bd:/", NULL }; + + *num_files = 1; + + return autoplay_list; +} + +static void *bd_init_plugin (xine_t *xine, void *data) +{ + bluray_input_class_t *this = bluray_init_plugin(xine, data); + + if (this) { + this->input_class.get_identifier = bd_class_get_identifier; + this->input_class.get_description = bd_class_get_description; + this->input_class.get_dir = NULL; + this->input_class.get_autoplay_list = bd_class_get_autoplay_list; + } + + return this; +} + +/* + * exported plugin catalog entry + */ + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_INPUT | PLUGIN_MUST_PRELOAD, INPUT_PLUGIN_IFACE_VERSION, "BLURAY", XINE_VERSION_CODE, NULL, bluray_init_plugin }, + { PLUGIN_INPUT | PLUGIN_MUST_PRELOAD, INPUT_PLUGIN_IFACE_VERSION, "BD", XINE_VERSION_CODE, NULL, bd_init_plugin }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; diff --git a/src/input/input_net.c b/src/input/input_net.c index 5c927a6d5..82ab28c1e 100644 --- a/src/input/input_net.c +++ b/src/input/input_net.c @@ -113,7 +113,10 @@ typedef struct { static int host_connect_attempt_ipv4(struct in_addr ia, int port, xine_t *xine) { int s; - struct sockaddr_in sin; + union { + struct sockaddr_in in; + struct sockaddr sa; + } sa; s = xine_socket_cloexec(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (s==-1) { @@ -122,14 +125,14 @@ static int host_connect_attempt_ipv4(struct in_addr ia, int port, xine_t *xine) return -1; } - sin.sin_family = AF_INET; - sin.sin_addr = ia; - sin.sin_port = htons(port); + sa.in.sin_family = AF_INET; + sa.in.sin_addr = ia; + sa.in.sin_port = htons(port); #ifndef WIN32 - if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 && errno != EINPROGRESS) + if (connect(s, &sa.sa, sizeof(sa.in))==-1 && errno != EINPROGRESS) #else - if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 && WSAGetLastError() != WSAEINPROGRESS) + if (connect(s, &sa.sa, sizeof(sa.in))==-1 && WSAGetLastError() != WSAEINPROGRESS) #endif { xine_log(xine, XINE_LOG_MSG, diff --git a/src/input/net_buf_ctrl.c b/src/input/net_buf_ctrl.c index d187b5c2b..ecea09dbf 100644 --- a/src/input/net_buf_ctrl.c +++ b/src/input/net_buf_ctrl.c @@ -162,8 +162,8 @@ static void dvbspeed_init (nbc_t *this) { #endif } if (xine_config_lookup_entry (xine, "engine.buffers.video_num_buffers", - &entry) && (entry.num_value < 1800)) { - config->update_num (config, "engine.buffers.video_num_buffers", 1800); + &entry) && (entry.num_value < 800)) { + config->update_num (config, "engine.buffers.video_num_buffers", 800); #ifdef LOG_DVBSPEED printf ("net_buf_ctrl: enlarged video fifo to 1800 buffers\n"); #endif diff --git a/src/libspudvb/xine_spudvb_decoder.c b/src/libspudvb/xine_spudvb_decoder.c index 339a6ad80..8d581a23e 100644 --- a/src/libspudvb/xine_spudvb_decoder.c +++ b/src/libspudvb/xine_spudvb_decoder.c @@ -79,6 +79,8 @@ typedef struct { typedef struct dvb_spu_class_s { spu_decoder_class_t class; xine_t *xine; + + int ignore_pts; } dvb_spu_class_t; typedef struct dvb_spu_decoder_s { @@ -97,9 +99,8 @@ typedef struct dvb_spu_decoder_s { char *pes_pkt_wrptr; unsigned int pes_pkt_size; - uint64_t pts; - uint64_t vpts; - uint64_t end_vpts; + int64_t vpts; + int64_t end_vpts; pthread_t dvbsub_timer_thread; struct timespec dvbsub_hide_timeout; @@ -695,7 +696,7 @@ static void draw_subtitles (dvb_spu_decoder_t * this) pthread_mutex_lock(&this->dvbsub_osd_mutex); #ifdef LOG - printf("SPUDVB: this->vpts=%llu\n",this->vpts); + printf("SPUDVB: this->vpts=%"PRId64"\n", this->vpts); #endif for ( r=0; r<MAX_REGIONS; r++ ) { #ifdef LOG @@ -752,27 +753,29 @@ static void spudec_decode_data (spu_decoder_t * this_gen, buf_element_t * buf) } return; } - else { - if (buf->decoder_info[2]) { - memset (this->pes_pkt, 0xff, 64*1024); - this->pes_pkt_wrptr = this->pes_pkt; - this->pes_pkt_size = buf->decoder_info[2]; - this->pts = buf->pts; - xine_fast_memcpy (this->pes_pkt, buf->content, buf->size); + /* accumulate data */ + if (buf->decoder_info[2]) { + memset (this->pes_pkt, 0xff, 64*1024); + this->pes_pkt_wrptr = this->pes_pkt; + this->pes_pkt_size = buf->decoder_info[2]; + + xine_fast_memcpy (this->pes_pkt, buf->content, buf->size); + this->pes_pkt_wrptr += buf->size; + + this->vpts = 0; + } + else { + if (this->pes_pkt && (this->pes_pkt_wrptr != this->pes_pkt)) { + xine_fast_memcpy (this->pes_pkt_wrptr, buf->content, buf->size); this->pes_pkt_wrptr += buf->size; } - else { - if (this->pes_pkt && (this->pes_pkt_wrptr != this->pes_pkt)) { - xine_fast_memcpy (this->pes_pkt_wrptr, buf->content, buf->size); - this->pes_pkt_wrptr += buf->size; - } - } } + /* don't ask metronom for a vpts but rather do the calculation * because buf->pts could be too far in future and metronom won't accept * further backwards pts (see metronom_got_spu_packet) */ - if (buf->pts) { + if (!this->class->ignore_pts && buf->pts > 0) { metronom_t *const metronom = this->stream->metronom; const int64_t vpts_offset = metronom->get_option( metronom, METRONOM_VPTS_OFFSET ); const int64_t spu_offset = metronom->get_option( metronom, METRONOM_SPU_OFFSET ); @@ -781,7 +784,7 @@ static void spudec_decode_data (spu_decoder_t * this_gen, buf_element_t * buf) const int64_t curvpts = clock->get_current_time( clock ); /* if buf->pts is unreliable, show page asap (better than nothing) */ #ifdef LOG - printf("SPUDVB: spu_vpts=%lld - current_vpts=%lld\n", vpts, curvpts); + printf("SPUDVB: spu_vpts=%"PRId64" - current_vpts=%"PRId64"\n", vpts, curvpts); #endif if ( vpts<=curvpts || (vpts-curvpts)>(5*90000) ) this->vpts = 0; @@ -790,7 +793,7 @@ static void spudec_decode_data (spu_decoder_t * this_gen, buf_element_t * buf) } /* completely ignore pts since it makes a lot of problems with various providers */ - this->vpts = 0; + /* this->vpts = 0; */ /* process the pes section */ @@ -925,8 +928,12 @@ static spu_decoder_t *dvb_spu_class_open_plugin (spu_decoder_class_t * class_gen return (spu_decoder_t *) this; } -static void dvb_spu_class_dispose (spu_decoder_class_t * this) +static void dvb_spu_class_dispose (spu_decoder_class_t * this_gen) { + dvb_spu_class_t *this = (dvb_spu_class_t *) this_gen; + + this->xine->config->unregister_callback(this->xine->config, "subtitles.dvb.ignore_pts"); + free (this); } @@ -940,6 +947,13 @@ static char *dvb_spu_class_get_description (spu_decoder_class_t * this) return "DVB subtitle decoder plugin"; } +static void spu_dvb_ignore_pts_change(void *this_gen, xine_cfg_entry_t *value) +{ + dvb_spu_class_t *this = (dvb_spu_class_t *) this_gen; + + this->ignore_pts = value->num_value; +} + static void *init_spu_decoder_plugin (xine_t * xine, void *data) { dvb_spu_class_t *this = calloc(1, sizeof (dvb_spu_class_t)); @@ -951,6 +965,12 @@ static void *init_spu_decoder_plugin (xine_t * xine, void *data) this->xine = xine; + this->ignore_pts = xine->config->register_bool(xine->config, + "subtitles.dvb.ignore_pts", 0, + _("Ignore DVB subtitle timing"), + _("Do not use PTS timestamps for DVB subtitle timing"), + 1, spu_dvb_ignore_pts_change, this); + return &this->class; } diff --git a/src/libspuhdmv/xine_hdmv_decoder.c b/src/libspuhdmv/xine_hdmv_decoder.c index d0a82da09..849346ecd 100644 --- a/src/libspuhdmv/xine_hdmv_decoder.c +++ b/src/libspuhdmv/xine_hdmv_decoder.c @@ -267,7 +267,7 @@ static void segbuf_parse_segment_header(segment_buffer_t *buf) if ( buf->segment_type < 0x14 || ( buf->segment_type > 0x18 && buf->segment_type != 0x80)) { - XINE_HDMV_ERROR("unknown segment type, resetting\n"); + XINE_HDMV_ERROR("unknown segment type 0x%02x, resetting\n", buf->segment_type); segbuf_reset(buf); } } else { diff --git a/src/xine-engine/buffer.h b/src/xine-engine/buffer.h index 8f5d1152e..2c13919b8 100644 --- a/src/xine-engine/buffer.h +++ b/src/xine-engine/buffer.h @@ -687,7 +687,7 @@ void _x_bmiheader_le2me( xine_bmiheader *bih ) XINE_PROTECTED; /* convert xine_waveformatex struct from little endian */ void _x_waveformatex_le2me( xine_waveformatex *wavex ) XINE_PROTECTED; -static __inline int _x_is_fourcc(void *ptr, void *tag) { +static __inline int _x_is_fourcc(const void *ptr, const void *tag) { return memcmp(ptr, tag, 4) == 0; } diff --git a/src/xine-utils/memcpy.c b/src/xine-utils/memcpy.c index 9056749e2..df514b682 100644 --- a/src/xine-utils/memcpy.c +++ b/src/xine-utils/memcpy.c @@ -408,7 +408,18 @@ static struct { { NULL, NULL, 0, 0 } }; -#if (defined(ARCH_X86) || defined(ARCH_X86_64)) && defined(HAVE_SYS_TIMES_H) +#ifdef HAVE_POSIX_TIMERS +/* Prefer clock_gettime() where available. */ +static int64_t _x_gettime(void) +{ + struct timespec tm; + return (clock_gettime (CLOCK_THREAD_CPUTIME_ID, &tm) == -1) + ? times (NULL) + : (int64_t)tm.tv_sec * 1e9 + tm.tv_nsec; +} +# define rdtsc(x) _x_gettime() + +#elif (defined(ARCH_X86) || defined(ARCH_X86_64)) && defined(HAVE_SYS_TIMES_H) static int64_t rdtsc(int config_flags) { int64_t x; @@ -511,6 +522,12 @@ void xine_probe_fast_memcpy(xine_t *xine) memset(buf1,0,BUFSIZE); memset(buf2,0,BUFSIZE); + /* some initial activity to ensure that we're not running slowly :-) */ + for(j=0;j<50;j++) { + memcpy_method[1].function(buf2,buf1,BUFSIZE); + memcpy_method[1].function(buf1,buf2,BUFSIZE); + } + for(i=1; memcpy_method[i].name; i++) { if( (config_flags & memcpy_method[i].cpu_require) != |