summaryrefslogtreecommitdiff
path: root/src/demuxers/demux_mpeg_block.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/demuxers/demux_mpeg_block.c')
-rw-r--r--src/demuxers/demux_mpeg_block.c519
1 files changed, 519 insertions, 0 deletions
diff --git a/src/demuxers/demux_mpeg_block.c b/src/demuxers/demux_mpeg_block.c
new file mode 100644
index 000000000..531bf44aa
--- /dev/null
+++ b/src/demuxers/demux_mpeg_block.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2000 the xine project
+ *
+ * This file is part of xine, a unix 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: demux_mpeg_block.c,v 1.1 2001/04/18 22:33:58 f1rmb Exp $
+ *
+ * demultiplexer for mpeg 1/2 program streams
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+
+#include "xine.h"
+#include "monitor.h"
+#include "demux.h"
+#include "utils.h"
+
+static uint32_t xine_debug;
+
+typedef struct demux_mpeg_block_s {
+ demux_plugin_t demux_plugin;
+
+ fifo_buffer_t *audio_fifo;
+ fifo_buffer_t *video_fifo;
+ fifo_buffer_t *spu_fifo;
+
+ input_plugin_t *input;
+
+ pthread_t thread;
+
+ int status;
+
+ int blocksize;
+} demux_mpeg_block_t ;
+
+
+static void demux_mpeg_block_parse_pack (demux_mpeg_block_t *this) {
+
+ buf_element_t *buf;
+ unsigned char *p;
+ int bMpeg1=0;
+ uint32_t nHeaderLen;
+ uint32_t nPTS;
+ uint32_t nDTS;
+ uint32_t nPacketLen;
+ uint32_t nStreamID;
+
+
+ buf = this->input->read_block (this->video_fifo, this->blocksize);
+
+ if (buf==NULL) {
+ this->status = DEMUX_FINISHED;
+ return ;
+ }
+
+ p = buf->content; /* len = this->mnBlocksize; */
+
+ if (p[3] == 0xBA) { /* program stream pack header */
+
+ int nStuffingBytes;
+
+ xprintf (VERBOSE|DEMUX, "program stream pack header\n");
+
+ bMpeg1 = (p[4] & 0x40) == 0;
+
+ if (bMpeg1) {
+
+ p += 12;
+
+ } else { /* mpeg2 */
+
+ nStuffingBytes = p[0xD] & 0x07;
+
+ xprintf (VERBOSE|DEMUX, "%d stuffing bytes\n",nStuffingBytes);
+
+ p += 14 + nStuffingBytes;
+ }
+ }
+
+
+ if (p[3] == 0xbb) { /* program stream system header */
+
+ int nHeaderLen;
+
+ xprintf (VERBOSE|DEMUX, "program stream system header\n");
+
+ nHeaderLen = (p[4] << 8) | p[5];
+
+ p += 6 + nHeaderLen;
+ }
+
+ /* we should now have a PES packet here */
+
+ if (p[0] || p[1] || (p[2] != 1)) {
+ fprintf (stderr, "demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]);
+ buf->free_buffer (buf);
+ return ;
+ }
+
+ nPacketLen = p[4] << 8 | p[5];
+ nStreamID = p[3];
+
+ xprintf (VERBOSE|DEMUX, "packet id = %02x len = %d\n",nStreamID, nPacketLen);
+
+ if (bMpeg1) {
+
+ if (nStreamID == 0xBF) {
+ buf->free_buffer (buf);
+ return ;
+ }
+
+ p += 6; /* nPacketLen -= 6; */
+
+ while ((p[0] & 0x80) == 0x80) {
+ p++;
+ nPacketLen--;
+ /* printf ("stuffing\n");*/
+ }
+
+ if ((p[0] & 0xc0) == 0x40) {
+ /* STD_buffer_scale, STD_buffer_size */
+ p += 2;
+ nPacketLen -=2;
+ }
+
+ nPTS = 0;
+ nDTS = 0;
+ if ((p[0] & 0xf0) == 0x20) {
+ nPTS = (p[ 0] & 0x0E) << 29 ;
+ nPTS |= p[ 1] << 22 ;
+ nPTS |= (p[ 2] & 0xFE) << 14 ;
+ nPTS |= p[ 3] << 7 ;
+ nPTS |= (p[ 4] & 0xFE) >> 1 ;
+ p += 5;
+ nPacketLen -=5;
+ } else if ((p[0] & 0xf0) == 0x30) {
+ nPTS = (p[ 0] & 0x0E) << 29 ;
+ nPTS |= p[ 1] << 22 ;
+ nPTS |= (p[ 2] & 0xFE) << 14 ;
+ nPTS |= p[ 3] << 7 ;
+ nPTS |= (p[ 4] & 0xFE) >> 1 ;
+ nDTS = (p[ 5] & 0x0E) << 29 ;
+ nDTS |= p[ 6] << 22 ;
+ nDTS |= (p[ 7] & 0xFE) << 14 ;
+ nDTS |= p[ 8] << 7 ;
+ nDTS |= (p[ 9] & 0xFE) >> 1 ;
+ p += 10;
+ nPacketLen -= 10;
+ } else {
+ p++;
+ nPacketLen --;
+ }
+
+ } else { /* mpeg 2 */
+
+ if (p[7] & 0x80) { /* PTS avail */
+
+ nPTS = (p[ 9] & 0x0E) << 29 ;
+ nPTS |= p[10] << 22 ;
+ nPTS |= (p[11] & 0xFE) << 14 ;
+ nPTS |= p[12] << 7 ;
+ nPTS |= (p[13] & 0xFE) >> 1 ;
+
+ } else
+ nPTS = 0;
+
+ if (p[7] & 0x40) { /* PTS avail */
+
+ nDTS = (p[14] & 0x0E) << 29 ;
+ nDTS |= p[15] << 22 ;
+ nDTS |= (p[16] & 0xFE) << 14 ;
+ nDTS |= p[17] << 7 ;
+ nDTS |= (p[18] & 0xFE) >> 1 ;
+
+ } else
+ nDTS = 0;
+
+
+ nHeaderLen = p[8];
+
+ p += nHeaderLen + 9;
+ nPacketLen -= nHeaderLen + 3;
+ }
+
+ xprintf (VERBOSE|DEMUX, "stream_id=%x len=%d pts=%d dts=%d\n", nStreamID, nPacketLen, nPTS, nDTS);
+
+ if (nStreamID == 0xbd) {
+
+ int nTrack, nSPUID;
+
+ nTrack = p[0] & 0x0F; /* hack : ac3 track */
+
+ if((p[0] & 0xE0) == 0x20) {
+ nSPUID = (p[0] & 0x1f);
+
+ xprintf(VERBOSE|DEMUX, "SPU PES packet, id 0x%03x\n",p[0] & 0x1f);
+
+ buf->content = p+1;
+ buf->size = nPacketLen-1;
+ buf->type = BUF_SPU_PACKAGE + nSPUID;
+ buf->PTS = nPTS;
+ buf->DTS = nDTS ;
+ buf->input_pos = this->input->seek (0, SEEK_CUR);
+
+ this->spu_fifo->put (this->spu_fifo, buf);
+
+ return;
+ }
+
+ if ((p[0]&0xF0) == 0x80) {
+
+ xprintf (VERBOSE|DEMUX|AC3, "ac3 PES packet, track %02x\n",nTrack);
+ /* printf ( "ac3 PES packet, track %02x\n",nTrack); */
+
+ buf->content = p+4;
+ buf->size = nPacketLen-4;
+ buf->type = BUF_AUDIO_AC3 + nTrack;
+ buf->PTS = nPTS;
+ buf->DTS = nDTS ;
+ buf->input_pos = this->input->seek (0, SEEK_CUR);
+
+ this->audio_fifo->put (this->audio_fifo, buf);
+
+ return ;
+ } else if ((p[0]&0xf0) == 0xa0) {
+
+ int pcm_offset;
+
+ xprintf (VERBOSE|DEMUX,"LPCMacket, len : %d %02x\n",nPacketLen-4, p[0]);
+
+ for( pcm_offset=0; ++pcm_offset < nPacketLen-1 ; ){
+ if ( p[pcm_offset] == 0x01 && p[pcm_offset+1] == 0x80 ) { /* START */
+ pcm_offset += 2;
+ break;
+ }
+ }
+
+ buf->content = p+pcm_offset;
+ buf->size = nPacketLen-pcm_offset;
+ buf->type = BUF_AUDIO_LPCM + nTrack;
+ buf->PTS = nPTS;
+ buf->DTS = nDTS ;
+ buf->input_pos = this->input->seek (0, SEEK_CUR);
+
+ this->audio_fifo->put (this->audio_fifo, buf);
+
+ return ;
+ }
+
+ } else if ((nStreamID >= 0xbc) && ((nStreamID & 0xf0) == 0xe0)) {
+
+ xprintf (VERBOSE|DEMUX, "video %d\n", nStreamID);
+
+ buf->content = p;
+ buf->size = nPacketLen;
+ buf->type = BUF_VIDEO_MPEG;
+ buf->PTS = nPTS;
+ buf->DTS = nDTS;
+ buf->input_pos = this->input->seek (0, SEEK_CUR);
+
+ this->video_fifo->put (this->video_fifo, buf);
+
+ return ;
+
+ } else if ((nStreamID & 0xe0) == 0xc0) {
+ int nTrack;
+
+ nTrack = nStreamID & 0x1f;
+
+ xprintf (VERBOSE|DEMUX|MPEG, "mpg audio #%d", nTrack);
+
+ buf->content = p;
+ buf->size = nPacketLen;
+ buf->type = BUF_AUDIO_MPEG + nTrack;
+ buf->PTS = nPTS;
+ buf->DTS = nDTS;
+ buf->input_pos = this->input->seek (0, SEEK_CUR);
+
+ this->audio_fifo->put (this->audio_fifo, buf);
+
+ return ;
+
+ } else {
+ xprintf (VERBOSE | DEMUX, "unknown packet, id = %x\n",nStreamID);
+ }
+
+ buf->free_buffer (buf);
+
+ return ;
+
+}
+
+static void *demux_mpeg_block_loop (void *this_gen) {
+
+ buf_element_t *buf;
+ demux_mpeg_block_t *this = (demux_mpeg_block_t *) this_gen;
+
+ do {
+
+ demux_mpeg_block_parse_pack(this);
+
+ } while (this->status == DEMUX_OK) ;
+
+ xprintf (VERBOSE|DEMUX, "demux loop finished (status: %d)\n",
+ this->mnStatus);
+
+ this->status = DEMUX_FINISHED;
+
+ buf = this->video_fifo->buffer_pool_alloc ();
+ buf->type = BUF_CONTROL_END;
+ this->video_fifo->put (this->video_fifo, buf);
+
+ buf = this->audio_fifo->buffer_pool_alloc ();
+ buf->type = BUF_CONTROL_END;
+ this->audio_fifo->put (this->audio_fifo, buf);
+
+ return NULL;
+}
+
+static void demux_mpeg_block_stop (demux_plugin_t *this_gen) {
+
+ demux_mpeg_block_t *this = (demux_mpeg_block_t *) this_gen;
+ void *p;
+
+ this->status = DEMUX_FINISHED;
+
+ pthread_join (this->thread, &p);
+}
+
+static int demux_mpeg_block_get_status (demux_plugin_t *this_gen) {
+ demux_mpeg_block_t *this = (demux_mpeg_block_t *) this_gen;
+
+ return this->status;
+}
+
+static void demux_mpeg_block_start (demux_plugin_t *this_gen,
+ fifo_buffer_t *video_fifo,
+ fifo_buffer_t *audio_fifo,
+ fifo_buffer_t *spu_fifo,
+ off_t pos)
+{
+
+ demux_mpeg_block_t *this = (demux_mpeg_block_t *) this_gen;
+ buf_element_t *buf;
+
+ this->video_fifo = video_fifo;
+ this->audio_fifo = audio_fifo;
+ this->spu_fifo = spu_fifo;
+
+ this->status = DEMUX_OK;
+
+ pos /= (off_t) this->blocksize;
+ pos *= (off_t) this->blocksize;
+
+ if((this->input->get_capabilities() & INPUT_CAP_SEEKABLE) != 0) {
+ xprintf (VERBOSE|DEMUX, "=>seek to %Ld\n",pos);
+ this->input->seek (pos, SEEK_SET);
+ }
+
+ /*
+ * send start buffer
+ */
+
+ buf = this->video_fifo->buffer_pool_alloc ();
+ buf->type = BUF_CONTROL_START;
+ this->video_fifo->put (this->video_fifo, buf);
+ buf = this->audio_fifo->buffer_pool_alloc ();
+ buf->type = BUF_CONTROL_START;
+ this->audio_fifo->put (this->audio_fifo, buf);
+
+ /*
+ * now start demuxing
+ */
+
+ pthread_create (&this->thread, NULL, demux_mpeg_block_loop, this) ;
+}
+
+static int demux_mpeg_block_open(demux_plugin_t *this_gen,
+ input_plugin_t *input, int stage) {
+
+ demux_mpeg_block_t *this = (demux_mpeg_block_t *) this_gen;
+
+ this->input = input;
+
+ switch(stage) {
+
+ case STAGE_BY_CONTENT: {
+ uint8_t buf[4096];
+
+ if((input->get_capabilities() & INPUT_CAP_SEEKABLE) != 0) {
+ input->seek(0, SEEK_SET);
+
+ this->blocksize = input->get_blocksize();
+
+ if (!this->blocksize)
+ return DEMUX_CANNOT_HANDLE;
+
+ if (input->read(buf, this->blocksize)) {
+
+ if(buf[0] || buf[1] || (buf[2] != 0x01))
+ return DEMUX_CANNOT_HANDLE;
+
+ switch(buf[3]) {
+
+ case 0xba:
+ if((buf[4] & 0xc0) == 0x40)
+ return DEMUX_CAN_HANDLE;
+
+ break;
+
+ case 0xe0:
+ if((buf[6] & 0xc0) == 0x80)
+ return DEMUX_CAN_HANDLE;
+
+ break;
+
+ }
+ }
+ }
+ return DEMUX_CANNOT_HANDLE;
+ }
+ break;
+
+ case STAGE_BY_EXTENSION: {
+ char *media;
+ char *ending;
+ char *MRL;
+
+ MRL = input->get_mrl ();
+
+ media = strstr(MRL, "://");
+ if(media) {
+ if(!strncmp(MRL, "dvd", 3)
+ || !strncmp(MRL, "fifo", 4)
+ || (((!strncmp(MRL, "stdin", 5) || !strncmp(MRL, "fifo", 4))
+ && (!strncmp((media+3), "mpeg2", 5) )))
+ ) {
+ this->blocksize = 2048;
+ return DEMUX_CAN_HANDLE;
+ }
+ if(!strncmp(MRL, "vcd", 3)) {
+ this->blocksize = 2324;
+ return DEMUX_CAN_HANDLE;
+ }
+ }
+
+ /*
+ * check ending
+ */
+
+ ending = strrchr(MRL, '.');
+
+ xprintf(VERBOSE|DEMUX, "demux_mpeg_block_can_handle: ending %s of %s\n",
+ ending ? ending :"(none)", MRL);
+
+ if(!ending)
+ return DEMUX_CANNOT_HANDLE;
+
+ if(!strcasecmp(ending, ".vob")) {
+ this->blocksize = 2048;
+ return DEMUX_CAN_HANDLE;
+ }
+ }
+ break;
+
+ default:
+ return DEMUX_CANNOT_HANDLE;
+ break;
+ }
+
+ return DEMUX_CANNOT_HANDLE;
+}
+
+static char *demux_mpeg_block_get_id(demux_plugin_t *this) {
+ return "MPEG_BLOCK";
+}
+
+static void demux_mpeg_block_close (demux_plugin_t *this) {
+ /* nothing */
+}
+
+demux_plugin_t *init_demux_mpeg_block(config_values_t *cfg, uint32_t xd) {
+
+ demux_mpeg_block_t *this = xmalloc (sizeof (demux_mpeg_block_t));
+
+ xine_debug = xd;
+
+ this->demux_plugin.open = demux_mpeg_block_open;
+ this->demux_plugin.start = demux_mpeg_block_start;
+ this->demux_plugin.stop = demux_mpeg_block_stop;
+ this->demux_plugin.close = demux_mpeg_block_close;
+ this->demux_plugin.get_status = demux_mpeg_block_get_status;
+ this->demux_plugin.get_identifier = demux_mpeg_block_get_id;
+
+ return (demux_plugin_t *) this;
+}