[MPlayer-dev-eng] Re: [PATCH] Queue VOB subtitle packets to eliminate dropouts
Tobias Diedrich
td at sim.uni-hannover.de
Sun Feb 16 05:03:37 CET 2003
Tomasz Farkas wrote:
> I have seen problems where DVD subtitles don't display
> at the right time and sometimes they don't appear at
> all. The problem stems from the fact that subtitle
> command packets are being applied as soon as they are
> read and assembled from the input stream.
/me wrote:
> I have recently bought the Korean DVD "My Sassy Girl" and was surprised
> that the subtitles would not show up in mplayer. Further research showed
> that they are being processed, but the alpha values are always 0 because
> the packet commands look like this:
This is basically the same problem, but was not solved by your patch.
I rewrote most of it so that now commands are parsed immediately and packets
containing the relevant changes (start_pts, end_pts, alpha, palette...)
are queued. The rendering code now uses the data from the queued packets,
so that it works for both cases now.
Comments?
OK to apply?
--
Tobias PGP: 0x9AC7E0BC
This mail is made of 100% recycled bits
Now playing: Kato Tokiko: Studio Ghibli Songs - Sakuranbo no Minoru Koro (KnB)
-------------- next part --------------
Index: spudec.c
===================================================================
RCS file: /cvsroot/mplayer/main/spudec.c,v
retrieving revision 1.39
diff -u -r1.39 spudec.c
--- spudec.c 16 Feb 2003 01:23:10 -0000 1.39
+++ spudec.c 16 Feb 2003 04:06:22 -0000
@@ -42,22 +42,29 @@
typedef struct packet_t packet_t;
struct packet_t {
- unsigned char *data;
- unsigned int process_pts; /* When to process the packet */
- size_t reserve; /* size of the memory pointed to by packet */
- unsigned int offset; /* end of the currently assembled fragment */
- unsigned int size; /* size of the packet once all fragments are assembled */
- unsigned int fragment_pts; /* PTS of the last fragment for this packet */
- unsigned int control_start; /* index of start of control data */
+ unsigned char *packet;
+ unsigned int palette[4];
+ unsigned int alpha[4];
+ unsigned int control_start; /* index of start of control data */
+ unsigned int current_nibble[2]; /* next data nibble (4 bits) to be
+ processed (for RLE decoding) for
+ even and odd lines */
+ int deinterlace_oddness; /* 0 or 1, index into current_nibble */
+ unsigned int height, start_row;
+ unsigned int start_pts, end_pts;
packet_t *next;
};
typedef struct {
- packet_t *packets; /* Linked list of packets sorted by process_pts */
- packet_t *last_packet; /* Last packet in linked list */
-
+ packet_t *queue_head;
+ packet_t *queue_tail;
unsigned int global_palette[16];
unsigned int orig_frame_width, orig_frame_height;
+ unsigned char* packet;
+ size_t packet_reserve; /* size of the memory pointed to by packet */
+ unsigned int packet_offset; /* end of the currently assembled fragment */
+ unsigned int packet_size; /* size of the packet once all fragments are assembled */
+ unsigned int packet_pts; /* PTS for this packet */
unsigned int palette[4];
unsigned int alpha[4];
unsigned int cuspal[4];
@@ -67,10 +74,6 @@
unsigned int start_col, end_col;
unsigned int start_row, end_row;
unsigned int width, height, stride;
- unsigned int current_nibble[2]; /* next data nibble (4 bits) to be
- processed (for RLE decoding) for
- even and odd lines */
- int deinterlace_oddness; /* 0 or 1, index into current_nibble */
size_t image_size; /* Size of the image buffer */
unsigned char *image; /* Grayscale value */
unsigned char *aimage; /* Alpha value */
@@ -86,46 +89,31 @@
int spu_changed;
} spudec_handle_t;
-/* Add packet to end of list */
-static void spudec_append_packet (spudec_handle_t *this, packet_t *packet)
+static void spudec_queue_packet(spudec_handle_t *this, packet_t *packet)
{
- packet->next = NULL;
- if (this->last_packet == NULL)
- this->packets = packet;
+ if (this->queue_head == NULL)
+ this->queue_head = packet;
else
- this->last_packet->next = packet;
-
- this->last_packet = packet;
+ this->queue_tail->next = packet;
+ this->queue_tail = packet;
}
-/* Add a new packet to end of the list */
-static void spudec_append_new_packet (spudec_handle_t *this)
+static packet_t *spudec_dequeue_packet(spudec_handle_t *this)
{
- packet_t *new_packet = calloc (1, sizeof (packet_t));
+ packet_t *retval = this->queue_head;
- /* Do not process packet yet, so set process time way into the future */
- new_packet->process_pts = -1L;
+ this->queue_head = retval->next;
+ if (this->queue_head == NULL)
+ this->queue_tail = NULL;
- spudec_append_packet (this, new_packet);
+ return retval;
}
-/* Remove top-most packet and free the memory it used */
-static void spudec_pop_packet (spudec_handle_t *this)
+static void spudec_free_packet(packet_t *packet)
{
- packet_t *temp;
-
- if (this->packets != NULL)
- {
- temp = this->packets;
- this->packets = temp->next;
- if (temp->data != NULL)
- free (temp->data);
- free (temp);
-
- /* Null last packet pointer if there are no packets in the queue */
- if (this->packets == NULL)
- this->last_packet = NULL;
- }
+ if (packet->packet != NULL)
+ free(packet->packet);
+ free(packet);
}
static inline unsigned int get_be16(const unsigned char *p)
@@ -138,22 +126,22 @@
return (get_be16(p) << 8) + p[2];
}
-static void next_line(spudec_handle_t *this)
+static void next_line(packet_t *packet)
{
- if (this->current_nibble[this->deinterlace_oddness] % 2)
- this->current_nibble[this->deinterlace_oddness]++;
- this->deinterlace_oddness = (this->deinterlace_oddness + 1) % 2;
+ if (packet->current_nibble[packet->deinterlace_oddness] % 2)
+ packet->current_nibble[packet->deinterlace_oddness]++;
+ packet->deinterlace_oddness = (packet->deinterlace_oddness + 1) % 2;
}
-static inline unsigned char get_nibble(spudec_handle_t *this, packet_t *packet)
+static inline unsigned char get_nibble(packet_t *packet)
{
unsigned char nib;
- unsigned int *nibblep = this->current_nibble + this->deinterlace_oddness;
+ unsigned int *nibblep = packet->current_nibble + packet->deinterlace_oddness;
if (*nibblep / 2 >= packet->control_start) {
mp_msg(MSGT_SPUDEC,MSGL_WARN, "SPUdec: ERROR: get_nibble past end of packet\n");
return 0;
}
- nib = packet->data[*nibblep / 2];
+ nib = packet->packet[*nibblep / 2];
if (*nibblep % 2)
nib &= 0xf;
else
@@ -223,11 +211,12 @@
unsigned int cmap[4], alpha[4];
unsigned int i, x, y;
- this->deinterlace_oddness = 0;
this->scaled_frame_width = 0;
this->scaled_frame_height = 0;
+ this->height = packet->height;
+ this->start_row = packet->start_row;
for (i = 0; i < 4; ++i) {
- alpha[i] = mkalpha(this->alpha[i]);
+ alpha[i] = mkalpha(packet->alpha[i]);
if (alpha[i] == 0)
cmap[i] = 0;
else if (this->custom){
@@ -236,7 +225,7 @@
cmap[i] = 256 - alpha[i];
}
else {
- cmap[i] = ((this->global_palette[this->palette[i]] >> 16) & 0xff);
+ cmap[i] = ((this->global_palette[packet->palette[i]] >> 16) & 0xff);
if (cmap[i] + alpha[i] > 255)
cmap[i] = 256 - alpha[i];
}
@@ -264,21 +253,21 @@
memset(this->image + y * this->stride + this->width, 0, this->stride - this->width);
}
- i = this->current_nibble[1];
+ i = packet->current_nibble[1];
x = 0;
y = 0;
- while (this->current_nibble[0] < i
- && this->current_nibble[1] / 2 < packet->control_start
+ while (packet->current_nibble[0] < i
+ && packet->current_nibble[1] / 2 < packet->control_start
&& y < this->height) {
unsigned int len, color;
unsigned int rle = 0;
- rle = get_nibble(this, packet);
+ rle = get_nibble(packet);
if (rle < 0x04) {
- rle = (rle << 4) | get_nibble(this, packet);
+ rle = (rle << 4) | get_nibble(packet);
if (rle < 0x10) {
- rle = (rle << 4) | get_nibble(this, packet);
+ rle = (rle << 4) | get_nibble(packet);
if (rle < 0x040) {
- rle = (rle << 4) | get_nibble(this, packet);
+ rle = (rle << 4) | get_nibble(packet);
if (rle < 0x0004)
rle |= ((this->width - x) << 2);
}
@@ -293,7 +282,7 @@
memset(this->aimage + y * this->stride + x, alpha[color], len);
x += len;
if (x >= this->width) {
- next_line(this);
+ next_line(packet);
x = 0;
++y;
}
@@ -309,14 +298,14 @@
I tested it with four streams and even got something readable. Half of the
times I got black characters with white around and half the reverse.
*/
-static void compute_palette(spudec_handle_t *this)
+static void compute_palette(spudec_handle_t *this, packet_t *packet)
{
int used[16],i,cused,start,step,color;
memset(used, 0, sizeof(used));
for (i=0; i<4; i++)
- if (this->alpha[i]) /* !Transparent? */
- used[this->palette[i]] = 1;
+ if (packet->alpha[i]) /* !Transparent? */
+ used[packet->palette[i]] = 1;
for (cused=0, i=0; i<16; i++)
if (used[i]) cused++;
if (!cused) return;
@@ -329,8 +318,8 @@
}
memset(used, 0, sizeof(used));
for (i=0; i<4; i++) {
- color = this->palette[i];
- if (this->alpha[i] && !used[color]) { /* not assigned? */
+ color = packet->palette[i];
+ if (packet->alpha[i] && !used[color]) { /* not assigned? */
used[color] = 1;
this->global_palette[color] = start<<16;
start += step;
@@ -338,24 +327,28 @@
}
}
-static void spudec_process_control(spudec_handle_t *this, packet_t *packet)
+static void spudec_process_control(spudec_handle_t *this, unsigned int pts100)
{
int a,b; /* Temporary vars */
unsigned int date, type;
unsigned int off;
unsigned int start_off = 0;
unsigned int next_off;
- unsigned int pts100 = packet->process_pts;
+ unsigned int start_pts;
+ unsigned int end_pts;
+ unsigned int current_nibble[2];
+ unsigned int control_start;
+ unsigned int display = 0;
- packet->control_start = get_be16(packet->data + 2);
- next_off = packet->control_start;
+ control_start = get_be16(this->packet + 2);
+ next_off = control_start;
while (start_off != next_off) {
start_off = next_off;
- date = get_be16(packet->data + start_off) * 1024;
- next_off = get_be16(packet->data + start_off + 2);
+ date = get_be16(this->packet + start_off) * 1024;
+ next_off = get_be16(this->packet + start_off + 2);
mp_msg(MSGT_SPUDEC,MSGL_DBG2, "date=%d\n", date);
off = start_off + 4;
- for (type = packet->data[off++]; type != 0xff; type = packet->data[off++]) {
+ for (type = this->packet[off++]; type != 0xff; type = this->packet[off++]) {
mp_msg(MSGT_SPUDEC,MSGL_DBG2, "cmd=%d ",type);
switch(type) {
case 0x00:
@@ -368,42 +361,39 @@
case 0x01:
/* Start display */
mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Start display!\n");
- this->start_pts = pts100 + date;
- this->end_pts = UINT_MAX;
+ start_pts = pts100 + date;
+ end_pts = UINT_MAX;
+ display = 1;
break;
case 0x02:
/* Stop display */
mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Stop display!\n");
- this->end_pts = pts100 + date;
+ end_pts = pts100 + date;
break;
case 0x03:
/* Palette */
- this->palette[0] = packet->data[off] >> 4;
- this->palette[1] = packet->data[off] & 0xf;
- this->palette[2] = packet->data[off + 1] >> 4;
- this->palette[3] = packet->data[off + 1] & 0xf;
+ this->palette[0] = this->packet[off] >> 4;
+ this->palette[1] = this->packet[off] & 0xf;
+ this->palette[2] = this->packet[off + 1] >> 4;
+ this->palette[3] = this->packet[off + 1] & 0xf;
mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Palette %d, %d, %d, %d\n",
this->palette[0], this->palette[1], this->palette[2], this->palette[3]);
off+=2;
break;
case 0x04:
/* Alpha */
- this->alpha[0] = packet->data[off] >> 4;
- this->alpha[1] = packet->data[off] & 0xf;
- this->alpha[2] = packet->data[off + 1] >> 4;
- this->alpha[3] = packet->data[off + 1] & 0xf;
- if (this->auto_palette) {
- compute_palette(this);
- this->auto_palette = 0;
- }
+ this->alpha[0] = this->packet[off] >> 4;
+ this->alpha[1] = this->packet[off] & 0xf;
+ this->alpha[2] = this->packet[off + 1] >> 4;
+ this->alpha[3] = this->packet[off + 1] & 0xf;
mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Alpha %d, %d, %d, %d\n",
this->alpha[0], this->alpha[1], this->alpha[2], this->alpha[3]);
off+=2;
break;
case 0x05:
/* Co-ords */
- a = get_be24(packet->data + off);
- b = get_be24(packet->data + off + 3);
+ a = get_be24(this->packet + off);
+ b = get_be24(this->packet + off + 3);
this->start_col = a >> 12;
this->end_col = a & 0xfff;
this->width = (this->end_col < this->start_col) ? 0 : this->end_col - this->start_col + 1;
@@ -418,10 +408,10 @@
break;
case 0x06:
/* Graphic lines */
- this->current_nibble[0] = 2 * get_be16(packet->data + off);
- this->current_nibble[1] = 2 * get_be16(packet->data + off + 2);
+ current_nibble[0] = 2 * get_be16(this->packet + off);
+ current_nibble[1] = 2 * get_be16(this->packet + off + 2);
mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Graphic offset 1: %d offset 2: %d\n",
- this->current_nibble[0] / 2, this->current_nibble[1] / 2);
+ current_nibble[0] / 2, current_nibble[1] / 2);
off+=4;
break;
case 0xff:
@@ -436,24 +426,41 @@
}
}
next_control:
- ;
+ if (display) {
+ packet_t *packet = calloc(1, sizeof(packet_t));
+ int i;
+ packet->start_pts = start_pts;
+ if (end_pts == UINT_MAX && start_off != next_off) {
+ start_pts = pts100 + get_be16(this->packet + next_off) * 1024;
+ packet->end_pts = start_pts - 1;
+ } else packet->end_pts = end_pts;
+ packet->current_nibble[0] = current_nibble[0];
+ packet->current_nibble[1] = current_nibble[1];
+ packet->height = this->height;
+ packet->start_row = this->start_row;
+ packet->control_start = control_start;
+ for (i=0; i<4; i++) {
+ packet->alpha[i] = this->alpha[i];
+ packet->palette[i] = this->palette[i];
+ }
+ packet->packet = malloc(this->packet_size);
+ memcpy(packet->packet, this->packet, this->packet_size);
+ spudec_queue_packet(this, packet);
+ }
}
}
-static void spudec_decode(spudec_handle_t *this, packet_t *queued_packet)
+static void spudec_decode(spudec_handle_t *this, unsigned int pts100)
{
if(this->hw_spu) {
static vo_mpegpes_t packet = { NULL, 0, 0x20, 0 };
static vo_mpegpes_t *pkg=&packet;
- packet.data = queued_packet->data;
- packet.size = queued_packet->size;
- packet.timestamp = queued_packet->process_pts;
+ packet.data = this->packet;
+ packet.size = this->packet_size;
+ packet.timestamp = pts100;
this->hw_spu->draw_frame((uint8_t**)&pkg);
- } else {
- spudec_process_control(this, queued_packet);
- spudec_process_data(this, queued_packet);
- }
- this->spu_changed = 1;
+ } else
+ spudec_process_control(this, pts100);
}
int spudec_changed(void * this)
@@ -462,88 +469,80 @@
return (spu->spu_changed || spu->now_pts > spu->end_pts);
}
-void spudec_assemble(void *this, unsigned char *packet_bytes, unsigned int len, unsigned int pts100)
+void spudec_assemble(void *this, unsigned char *packet, unsigned int len, unsigned int pts100)
{
spudec_handle_t *spu = (spudec_handle_t*)this;
- packet_t *last_packet;
-
- /* Create a new packet if one doesn't exist in the queue */
- if (spu->last_packet == NULL)
- spudec_append_new_packet (spu);
-
- last_packet = spu->last_packet;
-
// spudec_heartbeat(this, pts100);
if (len < 2) {
mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: packet too short\n");
return;
}
- if ((last_packet->fragment_pts + 10000) < pts100) {
+ if ((spu->packet_pts + 10000) < pts100) {
// [cb] too long since last fragment: force new packet
- last_packet->offset = 0;
+ spu->packet_offset = 0;
}
- last_packet->fragment_pts = pts100;
- if (last_packet->offset == 0) {
- unsigned int len2 = get_be16(packet_bytes);
+ spu->packet_pts = pts100;
+ if (spu->packet_offset == 0) {
+ unsigned int len2 = get_be16(packet);
// Start new fragment
- if (last_packet->reserve < len2) {
- if (last_packet->data != NULL)
- free(last_packet->data);
- last_packet->data = malloc(len2);
- last_packet->reserve = last_packet->data != NULL ? len2 : 0;
+ if (spu->packet_reserve < len2) {
+ if (spu->packet != NULL)
+ free(spu->packet);
+ spu->packet = malloc(len2);
+ spu->packet_reserve = spu->packet != NULL ? len2 : 0;
}
- if (last_packet->data != NULL) {
- last_packet->size = len2;
+ if (spu->packet != NULL) {
+ spu->packet_size = len2;
if (len > len2) {
mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid frag len / len2: %d / %d \n", len, len2);
return;
}
- memcpy(last_packet->data, packet_bytes, len);
- last_packet->offset = len;
+ memcpy(spu->packet, packet, len);
+ spu->packet_offset = len;
+ spu->packet_pts = pts100;
}
} else {
// Continue current fragment
- if (last_packet->size < last_packet->offset + len){
+ if (spu->packet_size < spu->packet_offset + len){
mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid fragment\n");
- last_packet->size = last_packet->offset = 0;
+ spu->packet_size = spu->packet_offset = 0;
return;
} else {
- memcpy(last_packet->data + last_packet->offset, packet_bytes, len);
- last_packet->offset += len;
+ memcpy(spu->packet + spu->packet_offset, packet, len);
+ spu->packet_offset += len;
}
}
#if 1
// check if we have a complete packet (unfortunatelly packet_size is bad
// for some disks)
// [cb] packet_size is padded to be even -> may be one byte too long
- if ((last_packet->offset == last_packet->size) ||
- ((last_packet->offset + 1) == last_packet->size)){
+ if ((spu->packet_offset == spu->packet_size) ||
+ ((spu->packet_offset + 1) == spu->packet_size)){
unsigned int x=0,y;
- while(x+4<=last_packet->offset) {
- y=get_be16(last_packet->data+x+2); // next control pointer
- mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUtest: x=%d y=%d off=%d size=%d\n",x,y,last_packet->offset,last_packet->size);
+ while(x+4<=spu->packet_offset){
+ y=get_be16(spu->packet+x+2); // next control pointer
+ mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUtest: x=%d y=%d off=%d size=%d\n",x,y,spu->packet_offset,spu->packet_size);
if(x>=4 && x==y){ // if it points to self - we're done!
// we got it!
- mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUgot: off=%d size=%d \n",last_packet->offset,last_packet->size);
- break;
+ mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUgot: off=%d size=%d \n",spu->packet_offset,spu->packet_size);
+ spudec_decode(spu, pts100);
+ spu->packet_offset = 0;
+ break;
}
- if(y<=x || y>=last_packet->size){ // invalid?
+ if(y<=x || y>=spu->packet_size){ // invalid?
mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUtest: broken packet!!!!! y=%d < x=%d\n",y,x);
- last_packet->size = last_packet->offset = 0;
- return;
+ spu->packet_size = spu->packet_offset = 0;
+ break;
}
x=y;
}
-
- /* Packet is done. Schedule time to process it and start a new one. */
- last_packet->process_pts = last_packet->fragment_pts;
- spudec_append_new_packet (spu);
+ // [cb] packet is done; start new packet
+ spu->packet_offset = 0;
}
#else
if (spu->packet_offset == spu->packet_size) {
- /* Packet is done. Schedule time to process it and start a new one. */
- last_packet->process_pts = last_packet->fragment_pts;
- spudec_append_new_packet (spu);
+ spudec_decode(spu, pts100);
+ spu->packet_offset = 0;
}
#endif
}
@@ -552,21 +551,24 @@
{
spudec_handle_t *spu = (spudec_handle_t*)this;
spu->now_pts = 0;
- while (spu->packets != NULL)
- spudec_pop_packet (spu);
+ spu->packet_size = spu->packet_offset = 0;
}
void spudec_heartbeat(void *this, unsigned int pts100)
-{
+{
spudec_handle_t *spu = (spudec_handle_t*) this;
spu->now_pts = pts100;
- /* Process queued instructions for the current beat */
- while (spu->packets != NULL && pts100 >= spu->packets->process_pts)
- {
- spudec_decode (spu, spu->packets);
- spudec_pop_packet (spu);
- }
+ while (spu->queue_head != NULL && pts100 >= spu->queue_head->start_pts) {
+ packet_t *packet = spudec_dequeue_packet(spu);
+ spu->start_pts = packet->start_pts;
+ spu->end_pts = packet->end_pts;
+ if (spu->auto_palette)
+ compute_palette(spu, packet);
+ spudec_process_data(spu, packet);
+ spudec_free_packet(packet);
+ spu->spu_changed = 1;
+ }
}
int spudec_visible(void *this){
@@ -1078,7 +1080,7 @@
spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t));
if (this){
//(fprintf(stderr,"VobSub Custom Palette: %d,%d,%d,%d", this->cuspal[0], this->cuspal[1], this->cuspal[2],this->cuspal[3]);
- this->packets = NULL;
+ this->packet = NULL;
this->image = NULL;
this->scaled_image = NULL;
/* XXX Although the video frame is some size, the SPU frame is
@@ -1112,8 +1114,10 @@
{
spudec_handle_t *spu = (spudec_handle_t*)this;
if (spu) {
- while (spu->packets != NULL)
- spudec_pop_packet (this);
+ while (spu->queue_head)
+ spudec_free_packet(spudec_dequeue_packet(spu));
+ if (spu->packet)
+ free(spu->packet);
if (spu->scaled_image)
free(spu->scaled_image);
if (spu->image)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
URL: <http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/attachments/20030216/d53693ca/attachment.pgp>
More information about the MPlayer-dev-eng
mailing list