summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dxr3/dxr3_decoder.c259
1 files changed, 124 insertions, 135 deletions
diff --git a/src/dxr3/dxr3_decoder.c b/src/dxr3/dxr3_decoder.c
index 9afd0d7b4..826c12c60 100644
--- a/src/dxr3/dxr3_decoder.c
+++ b/src/dxr3/dxr3_decoder.c
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: dxr3_decoder.c,v 1.45 2001/12/17 16:20:42 hrm Exp $
+ * $Id: dxr3_decoder.c,v 1.46 2001/12/17 22:28:33 hrm Exp $
*
* dxr3 video and spu decoder plugin. Accepts the video and spu data
* from XINE and sends it directly to the corresponding dxr3 devices.
@@ -72,6 +72,8 @@
#define DEFAULT_DEV "/dev/em8300"
static char *devname;
+#define METRONOM_HACK 0
+
#define MV_COMMAND 0
#define MV_STATUS 1
#ifndef MVCOMMAND_SCAN
@@ -87,7 +89,6 @@ typedef struct dxr3_decoder_s {
int fd_control;
int fd_video;
int last_pts;
- int last_scr;
scr_plugin_t *scr;
int scr_prio;
int width;
@@ -95,6 +96,8 @@ typedef struct dxr3_decoder_s {
int aspect;
int duration;
int enhanced_mode;
+ int have_header_info;
+ int in_buffer_fill;
} dxr3_decoder_t;
/* Function to check whether the dxr3 video out plugin is active.
@@ -356,74 +359,55 @@ static void dxr3_init (video_decoder_t *this_gen, vo_instance_t *video_out)
frame width & height, aspect ratio, and framerate, and sends it to the
video_out plugin via get_frame
*/
-#define HEADER_OFFSET 4
+#define HEADER_OFFSET 0
static void parse_mpeg_header(dxr3_decoder_t *this, uint8_t * buffer)
{
- /* only carry on if we have a legitimate mpeg header... */
- if (buffer[1]==0 && buffer[0]==0 && buffer[2]==1 && buffer[3]==0xb3) {
- int old_h = this->height;
- int old_w = this->width;
- int old_a = this->aspect;
-
- /* framerate code... needed for metronom */
- int framecode = buffer[HEADER_OFFSET+3] & 15;
-
- /* grab video resolution and aspect ratio from the stream */
- this->height = (buffer[HEADER_OFFSET+0] << 16) |
- (buffer[HEADER_OFFSET+1] << 8) |
- buffer[HEADER_OFFSET+2];
- this->width = ((this->height >> 12) + 15) & ~15;
- this->height = ((this->height & 0xfff) + 15) & ~15;
- this->aspect = buffer[HEADER_OFFSET+3] >> 4;
-
- switch (framecode){
- case 1: /* 23.976 */
- this->duration=3913;
- break;
- case 2: /* 24.000 */
- this->duration=3750;
- break;
- case 3: /* 25.000 */
- this->duration=3600;
- break;
- case 4: /* 29.970 */
- this->duration=3003;
- break;
- case 5: /* 30.000 */
- this->duration=3000;
- break;
- case 6: /* 50.000 */
- this->duration=1800;
- break;
- case 7: /* 59.940 */
- this->duration=1525;
- break;
- case 8: /* 60.000 */
- this->duration=1509;
- break;
- default:
- /* only print this warning once */
- if (this->duration != 3600) {
- printf("dxr3: warning: unknown frame rate code %d: using PAL\n", framecode);
- }
- this->duration=3600; /* PAL 25fps */
- break;
- }
-
- /* and ship the data if different ... appeasing any other vo plugins
- that are active ... */
- if (old_h!=this->height || old_w!=this->width || old_a!=this->aspect)
- {
- vo_frame_t *img;
- /* call with flags=DXR3_VO_UPDATE_FLAG, so that the
- dxr3 vo driver will update sizes and aspect ratio */
- img = this->video_out->get_frame(this->video_out,
- this->width,this->height,this->aspect,
- IMGFMT_YV12, this->duration,
- DXR3_VO_UPDATE_FLAG);
- img->free(img);
+ /* framerate code... needed for metronom */
+ int framecode = buffer[HEADER_OFFSET+3] & 15;
+
+ /* grab video resolution and aspect ratio from the stream */
+ this->height = (buffer[HEADER_OFFSET+0] << 16) |
+ (buffer[HEADER_OFFSET+1] << 8) |
+ buffer[HEADER_OFFSET+2];
+ this->width = ((this->height >> 12) + 15) & ~15;
+ this->height = ((this->height & 0xfff) + 15) & ~15;
+ this->aspect = buffer[HEADER_OFFSET+3] >> 4;
+
+ switch (framecode){
+ case 1: /* 23.976 */
+ this->duration=3913;
+ break;
+ case 2: /* 24.000 */
+ this->duration=3750;
+ break;
+ case 3: /* 25.000 */
+ this->duration=3600;
+ break;
+ case 4: /* 29.970 */
+ this->duration=3003;
+ break;
+ case 5: /* 30.000 */
+ this->duration=3000;
+ break;
+ case 6: /* 50.000 */
+ this->duration=1800;
+ break;
+ case 7: /* 59.940 */
+ this->duration=1525;
+ break;
+ case 8: /* 60.000 */
+ this->duration=1509;
+ break;
+ default:
+ /* only print this warning once */
+ if (this->duration != 3600) {
+ printf("dxr3: warning: unknown frame rate code %d: using PAL\n", framecode);
}
+ this->duration=3600; /* PAL 25fps */
+ break;
}
+
+ this->have_header_info = 1;
}
@@ -436,6 +420,9 @@ static void dxr3_flush (video_decoder_t *this_gen)
dxr3_decoder_t *this = (dxr3_decoder_t *) this_gen;
printf("dxr3_decoder: flushing\n");
dxr3_mvcommand(this->fd_control, 0x11);
+ this->have_header_info = 0;
+ if (this->fd_video >= 0)
+ fsync(this->fd_video);
}
@@ -443,48 +430,24 @@ static void dxr3_decode_data (video_decoder_t *this_gen, buf_element_t *buf)
{
dxr3_decoder_t *this = (dxr3_decoder_t *) this_gen;
ssize_t written;
- int vpts;
+ int vpts, i;
vo_frame_t *img;
-
- /* The dxr3 does not need the preview-data */
- if (buf->decoder_info[0] == 0) {
- return;
- }
-
- /* examine mpeg header, if this buffer's contents has one, and
- send an update message to the dxr3 vo driver if needed */
- parse_mpeg_header(this, buf->content);
-
- /* not sure if this is supposed to ever happen, but
- checking is cheap enough... */
- if (buf->SCR < this->last_scr) { /* wrapped ? */
- this->last_scr = buf->SCR;
- }
-
+ uint8_t *buffer, byte;
+ uint32_t shift;
- /* Now try to make metronom happy by calling get_frame,draw,free.
- There are two conditions when we need to do this:
- (1) Normal mpeg decoding; we want to make the calls for each
- frame, but the problem is that dxr3_decode_data is called more
- frequently than once per frame (not sure why). We'd have to analyse
- the mpeg data to find out whether or not a new frame should be
- announced. That defeats the point of hardware mpeg decoding
- somewhat, so we'll just look at the clock value; if the time
- elapsed since the previous image get_frame/draw/free trio is
- more than the frame's duration, we draw.
- (2) Still pictures; A buffer type of BUF_VIDEO_FILL is used
- when still frames are required (after an initial frame is sent
- for display, BUF_VIDEO_FILL grabs and re-displays the last frame)
- the dxr3 doesn't require this functionality (just do nothing and
- the last frame will stick), but for interoperability purposes
- this plugin must implement it in order to override xine's
- builtin version - so we just pretend to be outputting the same
- old frame at the correct frame rate.
- */
vpts = 0;
- if ( buf->SCR >= this->last_scr + this->duration
- || buf->type == BUF_VIDEO_FILL ) /* time to draw */
- {
+
+ /* if we're just coming from a BUF_VIDEO_FILL situation,
+ * do a flush for good riddance. (doesn't help much though) */
+ if (this->in_buffer_fill && buf->type != BUF_VIDEO_FILL)
+ dxr3_flush(this_gen);
+ this->in_buffer_fill = (buf->type == BUF_VIDEO_FILL);
+
+ /* FIXME What are we supposed to do with this? */
+ if (buf->type == BUF_VIDEO_FILL && this->have_header_info) {
+ /* printf("dxr3enc: BUF_VIDEO_FILL\n"); */
+ /* require have_header_info, otherwise width and height
+ * settings may be random */
img = this->video_out->get_frame (this->video_out,
this->width,
this->height,
@@ -492,15 +455,54 @@ static void dxr3_decode_data (video_decoder_t *this_gen, buf_element_t *buf)
IMGFMT_YV12,
this->duration,
DXR3_VO_UPDATE_FLAG);
- /* copy PTS from buffer to img, img->draw uses it.
- leaving img->SCR alone seems to work best */
- if (buf->type != BUF_VIDEO_FILL) {
- img->PTS=buf->PTS;
+ img->PTS=0;
+ img->bad_frame = 0;
+ img->draw(img);
+ vpts = img->PTS;
+ img->free(img);
+ return;
+ }
+
+ /* count the number of frame codes in this block of data
+ * this code borrowed from libmpeg2.
+ * Note: this uses the 'naive' approach of constant durations,
+ * not the real NTSC-like durations that vary dep on repeat first
+ * field flags and stuff. */
+ buffer = buf->content;
+ shift = 0xffffff00;
+ for (i=0; i<buf->size; i++) {
+ byte = *buffer++;
+ if (shift != 0x00000100) {
+ shift = (shift | byte) << 8;
+ continue;
+ }
+ /* header code of some kind found */
+ /* printf("dxr3: have header %x\n", byte); */
+ shift = 0xffffff00;
+ if (byte == 0xb3) {
+ /* sequence data, also borrowed from libmpeg2 */
+ /* will enable have_header_info */
+ parse_mpeg_header(this, buffer);
+ continue;
+ }
+ if (byte != 0x00) {
+ /* not a new frame */
+ continue;
}
- else {
- /* force zero, value in buf may be undefined */
- img->PTS = 0;
+ /* we have a code for a new frame */
+ if (! this->have_header_info) {
+ /* this->width et al may still be undefined */
+ continue;
}
+ /* pretend like we have decoded a frame */
+ img = this->video_out->get_frame (this->video_out,
+ this->width,
+ this->height,
+ this->aspect,
+ IMGFMT_YV12,
+ this->duration,
+ DXR3_VO_UPDATE_FLAG);
+ img->PTS=buf->PTS;
img->bad_frame = 0;
/* draw calls metronom->got_video_frame with img pts and scr
and stores the return value back in img->PTS
@@ -509,21 +511,13 @@ static void dxr3_decode_data (video_decoder_t *this_gen, buf_element_t *buf)
img->draw(img);
vpts = img->PTS; /* copy so we can free img */
/* store at what time we called draw last */
- this->last_scr = buf->SCR;
img->free(img);
}
- /* Every once in a while a buffer has a PTS value associated with it.
- From my testing, once around every 12-13 frames, the
- buf->PTS is non-zero, which is around every .5 seconds or so...
- If vpts is non-zero, we already called img->draw which in
- turn has called got_video_frame, so we have vpts already */
- if (buf->PTS && vpts == 0) {
- /* receive an updated pts value from metronom... */
- vpts = this->video_decoder.metronom->got_video_frame(
- this->video_decoder.metronom, buf->PTS, buf->SCR );
- }
- /* ensure video device is open */
+
+ /* ensure video device is open
+ * (we open it late because on occasion the dxr3 video out driver
+ * wants to open it) */
if (this->fd_video < 0) {
char tmpstr[128];
snprintf (tmpstr, sizeof(tmpstr), "%s_mv", devname);
@@ -534,10 +528,9 @@ static void dxr3_decode_data (video_decoder_t *this_gen, buf_element_t *buf)
}
}
- /* Now that we have a PTS value from the stream, not a metronom
- interpolated one, it's a good time to make sure the dxr3 pts
- is in sync. Checking every 0.5 seconds should be enough... */
- if (buf->PTS && this->last_pts < vpts)
+ /* From time to time, update the pts value
+ * FIXME: the exact conditions here are a bit uncertain... */
+ if (buf->PTS && vpts && this->last_pts < vpts)
{
this->last_pts = vpts;
/* update the dxr3's current pts value */
@@ -547,13 +540,6 @@ static void dxr3_decode_data (video_decoder_t *this_gen, buf_element_t *buf)
strerror(errno));
}
}
-
- /* for stills we're done now */
- if(buf->type == BUF_VIDEO_FILL) {
- return;
- }
-
-
/* if the dxr3_alt_play option is used, change the dxr3 playmode */
if(this->enhanced_mode && !scanning_mode)
dxr3_mvcommand(this->fd_control, 6);
@@ -584,6 +570,7 @@ static void dxr3_close (video_decoder_t *this_gen)
this->fd_video = -1;
this->video_out->close(this->video_out);
+ this->have_header_info = 0;
}
static char *dxr3_get_id(void) {
@@ -626,6 +613,8 @@ video_decoder_t *init_video_decoder_plugin (int iface_version,
if(this->enhanced_mode)
printf("Dxr3: Using Mode 6 for playback\n");
+ this->have_header_info = 0;
+ this->in_buffer_fill = 0;
return (video_decoder_t *) this;
}