diff options
Diffstat (limited to 'src/dxr3/dxr3_decode_video.c')
-rw-r--r-- | src/dxr3/dxr3_decode_video.c | 1245 |
1 files changed, 493 insertions, 752 deletions
diff --git a/src/dxr3/dxr3_decode_video.c b/src/dxr3/dxr3_decode_video.c index 9e2fbcf1a..cb6bbfdb7 100644 --- a/src/dxr3/dxr3_decode_video.c +++ b/src/dxr3/dxr3_decode_video.c @@ -17,52 +17,17 @@ * 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_decode_video.c,v 1.1 2002/05/02 14:33:30 jcdutton Exp $ - * - * dxr3 video and spu decoder plugin. Accepts the video and spu data - * from XINE and sends it directly to the corresponding dxr3 devices. - * Takes precedence over the libmpeg2 and libspudec due to a higher - * priority. - * also incorporates an scr plugin for metronom - * - * update 7/1/2002 by jcdutton: - * Updated to work better with the changes done to dvdnav. - * Subtitles display properly now. - * TODO: Process NAV packets so that the first - * menu button appears, and also so that - * menu buttons disappear when one starts playing the movie. - * Processing NAV packets will also make "The White Rabbit" - * work on DXR3 as I currently works on XV. - * - * update 25/11/01 by Harm: - * Major retooling; so much so that I've decided to cvs-tag the dxr3 sources - * as DXR3_095 before commiting. - * - major retooling of dxr3_decode_data; Mike Lampard's comments indicate - * that dxr3_decode_data is called once every 12 frames or so. This seems - * no longer true; we're in fact called on average more than once per frame. - * This gives us some interesting possibilities to lead metronom up the garden - * path (and administer it a healthy beating behind the toolshed ;-). - * Read the comments for details, but the short version is that we take a - * look at the scr clock to guestimate when we should call get_frame/draw/free. - * - renamed update_aspect to parse_mpeg_header. - * - replaced printing to stderr by stdout, following xine practice and - * to make it easier to write messages to a log. - * - replaced 6667 flag with proper define in dxr3_video_out.h - * - * The upshot of all this is that sync is a lot better now; I get good - * a/v sync within a few seconds of playing after start/seek. I also - * get a lot of "throwing away frame..." messages, especially just - * after start/seek, but those are relatively harmless. (I guess we - * call img->draw a tad too often). - * - * update 21/12/01 by Harm - * many revisions, but I've been too lazy to document them here. - * read the cvs log, that's what it's for anyways. + * $Id: dxr3_decode_video.c,v 1.2 2002/05/06 11:26:37 jcdutton Exp $ + */ + +/* dxr3 video decoder plugin. + * Accepts the video data from xine and sends it directly to the + * corresponding dxr3 device. Takes precedence over the libmpeg2 + * due to a higher priority. */ #include <sys/types.h> -#include <sys/stat.h> #include <sys/ioctl.h> #include <stdio.h> #include <stdlib.h> @@ -70,777 +35,553 @@ #include <unistd.h> #include <fcntl.h> #include <errno.h> -#include <signal.h> -#include <linux/soundcard.h> -#include <linux/em8300.h> -#include "video_out.h" #include "xine_internal.h" #include "buffer.h" -#include "xine-engine/bswap.h" -#include "nav_types.h" -#include "nav_read.h" +#include "dxr3_scr.h" +#include "dxr3.h" -/* for DXR3_VO_UPDATE_FLAG */ -#include "dxr3_video_out.h" - -#define LOOKUP_DEV "dxr3.devicename" -#define DEFAULT_DEV "/dev/em8300" -static char devname[128]; -static char devnum[3]; - -/* lots of poohaa about pts things */ +#define LOG_VID 1 #define LOG_PTS 0 -#define LOG_SPU 0 - -#define MV_COMMAND 0 /* the number of frames to pass after an out-of-sync situation before locking the stream again */ #define RESYNC_WINDOW_SIZE 50 +/* we adjust vpts_offset in metronom, when skip_count reaches this value */ +#define SKIP_TOLERANCE 200 -typedef struct dxr3_decoder_s { - video_decoder_t video_decoder; - vo_instance_t *video_out; - config_values_t *config; - metronom_t *metronom; - - int fd_control; - int fd_video; - int64_t last_pts; - scr_plugin_t *scr; - int scr_prio; - int width; - int height; - int aspect; - int frame_rate_code; - int repeat_first_field; - /* try to sync pts every frame. will be disabled if non-progessive - video is detected via repeat first field */ - int sync_every_frame; - /* if disabled by repeat first field, retry after 500 frames */ - int sync_retry; - int enhanced_mode; - int resync_window; - int have_header_info; - int in_buffer_fill; - pthread_t decoder_thread; /* reference to self */ -} dxr3_decoder_t; - -static int dxr3_presence_test( xine_t* xine) -{ - int info; - if (xine && xine->video_driver ) { - info = xine->video_driver->get_property( xine->video_driver, VO_PROP_VO_TYPE); - printf("dxr3_presence_test:info=%d\n",info); - if (info != VO_TYPE_DXR3) { - return 0; - } - } - return 1; -} - - -/* *** dxr3_mvcommand *** - Changes the dxr3 playmode. Possible playmodes (currently) are - 0 - Stop - 2 - Pause - 3 - Start playback - 4 - Play intra frames only (for FFWD/FBackward) - 6 - Alternate playmode - not much is known about this mode - other than it buffers frames, possibly re-organising them - on-the-fly to match scr vs pts values - 0x11 - Flush the onboard buffer??????? - 0x10 - as above??? -*/ -static int dxr3_mvcommand(int fd_control, int command) { - em8300_register_t regs; - regs.microcode_register=1; /* Yes, this is a MC Reg */ - regs.reg = MV_COMMAND; - regs.val=command; - - return (ioctl(fd_control, EM8300_IOCTL_WRITEREG, ®s)); -} - -typedef struct dxr3scr_s { - scr_plugin_t scr; - int fd_control; - int priority; - int64_t offset; /* difference between real scr and internal dxr3 clock */ - uint32_t last_pts; /* last known value of internal dxr3 clock to detect wrap around */ - pthread_mutex_t mutex; -} dxr3scr_t; - -static int dxr3scr_get_priority (scr_plugin_t *scr) { - dxr3scr_t *self = (dxr3scr_t*) scr; - return self->priority; -} - - -/* *** dxr3scr_set_speed *** - sets the speed and playmode of the dxr3. if FFWD is requested - the function changes the speed of the onboard clock, and sets - the playmode to SCAN (mv_command 4). -*/ -int scanning_mode=0; -static int dxr3scr_set_speed (scr_plugin_t *scr, int speed) { - dxr3scr_t *self = (dxr3scr_t*) scr; - uint32_t em_speed; - int playmode; - - switch(speed){ - case SPEED_PAUSE: - em_speed = 0; - playmode=MVCOMMAND_PAUSE; - break; - case SPEED_SLOW_4: - em_speed = 0x900/4; - playmode=MVCOMMAND_START; - break; - case SPEED_SLOW_2: - em_speed = 0x900/2; - playmode=MVCOMMAND_START; - break; - case SPEED_NORMAL: - em_speed = 0x900; - playmode=MVCOMMAND_SYNC; - break; - case SPEED_FAST_2: - em_speed = 0x900*2; - playmode=MVCOMMAND_START; - break; - case SPEED_FAST_4: - em_speed = 0x900*4; - playmode=MVCOMMAND_START; - break; - default: - em_speed = 0; - playmode = MVCOMMAND_PAUSE; - } - - if (dxr3_mvcommand(self->fd_control, playmode)) - printf("dxr3scr: failed to playmode (%s)\n", strerror(errno)); - - if(em_speed>0x900) - scanning_mode=1; - else - scanning_mode=0; - - if (ioctl(self->fd_control, EM8300_IOCTL_SCR_SETSPEED, &em_speed)) - printf("dxr3scr: failed to set speed (%s)\n", strerror(errno)); - - return speed; -} - - -/* *** dxr3scr_adjust *** - Adjusts the scr value of the card to match that given. - This function is only called if the dxr3 scr plugin is - _NOT_ master... - Harm: wish that were so. It's called by audio_out - (those adjusting master clock x->y messages) -*/ -static void dxr3scr_adjust (scr_plugin_t *scr, int64_t vpts) { - dxr3scr_t *self = (dxr3scr_t*) scr; - uint32_t cpts32; - int32_t offset32; - - pthread_mutex_lock(&self->mutex); - if (ioctl(self->fd_control, EM8300_IOCTL_SCR_GET, &cpts32)) - printf("dxr3scr: adjust get failed (%s)\n", strerror(errno)); - self->last_pts = cpts32; - self->offset = vpts - ((int64_t)cpts32 << 1); - offset32 = self->offset / 4; - /* kernel driver ignores diffs < 7200, so abs(offset32) must be > 7200 / 4 */ - if (offset32 < -7200/4 || offset32 > 7200/4) { - uint32_t vpts32 = vpts >> 1; - if (ioctl(self->fd_control, EM8300_IOCTL_SCR_SET, &vpts32)) - printf("dxr3scr: adjust set failed (%s)\n", strerror(errno)); - self->last_pts = vpts32; - self->offset = vpts - ((int64_t)vpts32 << 1); - } - pthread_mutex_unlock(&self->mutex); -} +/* offset for mpeg header parsing */ +#define HEADER_OFFSET 0 -/* *** dxr3scr_start *** - sets the dxr3 onboard system reference clock to match that handed to - it in start_vpts. also sets the speed of the clock to 0x900 - which - is normal speed. -*/ -static void dxr3scr_start (scr_plugin_t *scr, int64_t start_vpts) { - dxr3scr_t *self = (dxr3scr_t*) scr; - uint32_t vpts32 = start_vpts >> 1; - pthread_mutex_lock(&self->mutex); - self->last_pts = vpts32; - self->offset = start_vpts - ((int64_t)vpts32 << 1); - if (ioctl(self->fd_control, EM8300_IOCTL_SCR_SET, &vpts32)) - printf("dxr3scr: start failed (%s)\n", strerror(errno)); - /* mis-use vpts32 */ - vpts32 = 0x900; - ioctl(self->fd_control, EM8300_IOCTL_SCR_SETSPEED, &vpts32); - pthread_mutex_unlock(&self->mutex); -} +/* plugin initialization function */ +video_decoder_t *init_video_decoder_plugin(int iface_version, xine_t *xine); +/* functions required by xine api */ +static char *dxr3_get_id(void); +static int dxr3_can_handle(video_decoder_t *this_gen, int buf_type); +static void dxr3_init(video_decoder_t *this_gen, vo_instance_t *video_out); +static void dxr3_decode_data(video_decoder_t *this_gen, buf_element_t *buf); +static void dxr3_flush(video_decoder_t *this_gen); +static void dxr3_reset(video_decoder_t *this_gen); +static void dxr3_close(video_decoder_t *this_gen); +static void dxr3_dispose(video_decoder_t *this_gen); -/* *** dxr3_get_current *** - returns the current SCR value as indicated by the hardware clock - on the dxr3 - apparently only called when the dxr3_scr plugin is - master.. -*/ -static int64_t dxr3scr_get_current (scr_plugin_t *scr) { - dxr3scr_t *self = (dxr3scr_t*) scr; - uint32_t pts; - int64_t current; +/* plugin structure */ +typedef struct dxr3_decoder_s { + video_decoder_t video_decoder; + vo_instance_t *video_out; + dxr3_scr_t *scr; + xine_t *xine; + + char devname[128]; + char devnum[3]; + int fd_control; + int fd_video; /* to access the dxr3 devices */ + + int have_header_info; + int width; + int height; + int aspect; + int frame_rate_code; + int repeat_first_field; /* mpeg stream header data */ + + int sync_every_frame; + int sync_retry; + int enhanced_mode; + int resync_window; + int skip_count; /* syncing parameters */ +#if LOG_PTS + int64_t last_pts; +#endif +} dxr3_decoder_t; - pthread_mutex_lock(&self->mutex); - if (ioctl(self->fd_control, EM8300_IOCTL_SCR_GET, &pts)) - printf("dxr3scr: get current failed (%s)\n", strerror(errno)); - /* FIXME: The pts value read from the card sometimes drops to zero for - unknown reasons. We catch this here, but we should better find out, why. */ - if (pts == 0) pts = self->last_pts; - if (pts < self->last_pts) /* wrap around detected, compensate with offset */ - self->offset += (int64_t)1 << 33; - self->last_pts = pts; - current = ((int64_t)pts << 1) + self->offset; - pthread_mutex_unlock(&self->mutex); +/* helper functions */ +static int dxr3_present(xine_t *xine); +static int dxr3_mvcommand(int fd_control, int command); +static void parse_mpeg_header(dxr3_decoder_t *this, uint8_t *buffer); +static int get_duration(int framecode, int repeat_first_field); +static void dxr3_update_priority(void *this_gen, cfg_entry_t *entry); +static void dxr3_update_sync_mode(void *this_gen, cfg_entry_t *entry); +static void dxr3_update_enhanced_mode(void *this_gen, cfg_entry_t *entry); - return current; -} - /* *** dxr3scr_exit *** - clean up for exit - */ -static void dxr3scr_exit (scr_plugin_t *scr) { - dxr3scr_t *self = (dxr3scr_t*) scr; - pthread_mutex_destroy(&self->mutex); - free (self); +video_decoder_t *init_video_decoder_plugin(int iface_version, xine_t *xine) +{ + dxr3_decoder_t *this; + config_values_t *cfg; + char *tmpstr; + int dashpos; + + if (iface_version != 8) { + printf( "dxr3_decode_video: plugin doesn't support plugin API version %d.\n" + "dxr3_decode_video: this means there's a version mismatch between xine and this\n" + "dxr3_decode_video: decoder plugin. Installing current plugins should help.\n", + iface_version); + return NULL; + } + + if (!dxr3_present(xine)) return NULL; + + this = (dxr3_decoder_t *)malloc(sizeof (dxr3_decoder_t)); + + cfg = xine->config; + tmpstr = cfg->register_string(cfg, CONF_LOOKUP, CONF_DEFAULT, CONF_NAME, CONF_HELP, NULL, NULL); + strncpy(this->devname, tmpstr, 128); + this->devname[127] = '\0'; + dashpos = strlen(this->devname) - 2; /* the dash in the new device naming scheme would be here */ + if (this->devname[dashpos] == '-') { + /* use new device naming scheme with trailing number */ + strncpy(this->devnum, &this->devname[dashpos], 3); + this->devname[dashpos] = '\0'; + } else { + /* use old device naming scheme without trailing number */ + /* FIXME: remove this when everyone uses em8300 >=0.12.0 */ + this->devnum[0] = '\0'; + } + + this->video_decoder.interface_version = iface_version; + this->video_decoder.get_identifier = dxr3_get_id; + this->video_decoder.can_handle = dxr3_can_handle; + this->video_decoder.init = dxr3_init; + this->video_decoder.decode_data = dxr3_decode_data; + this->video_decoder.flush = dxr3_flush; + this->video_decoder.reset = dxr3_reset; + this->video_decoder.close = dxr3_close; + this->video_decoder.dispose = dxr3_dispose; + this->video_decoder.priority = cfg->register_num(cfg, + "dxr3.decoder_priority", 10, "Dxr3: video decoder priority", + "Decoder priorities greater 5 enable hardware decoding, 0 disables it.", + dxr3_update_priority, this); + + this->scr = NULL; + this->xine = xine; + + this->have_header_info = 0; + this->repeat_first_field = 0; + + this->sync_every_frame = cfg->register_bool(cfg, + "dxr3.sync_every_frame", 0, "Try to sync video every frame", + "This is relevant for progressive video only (most PAL films).", + dxr3_update_sync_mode, this); + this->sync_retry = 0; + this->enhanced_mode = cfg->register_bool(cfg, + "dxr3.alt_play_mode", 1, "Use alternate Play mode", + "Enabling this option will utilise a smoother play mode.", + dxr3_update_enhanced_mode, this); + + return &this->video_decoder; } -/* *** dxr3scr_init *** - initialise the SCR plugin -*/ -static scr_plugin_t* dxr3scr_init (dxr3_decoder_t *dxr3) { - dxr3scr_t *self; - char tmpstr[128]; - - self = malloc(sizeof(*self)); - memset(self, 0, sizeof(*self)); - - self->scr.interface_version = 2; - self->scr.get_priority = dxr3scr_get_priority; - self->scr.set_speed = dxr3scr_set_speed; - self->scr.adjust = dxr3scr_adjust; - self->scr.start = dxr3scr_start; - self->scr.get_current = dxr3scr_get_current; - self->scr.exit = dxr3scr_exit; - - self->offset = 0; - self->last_pts = 0; - pthread_mutex_init(&self->mutex, NULL); - - snprintf (tmpstr, sizeof(tmpstr), "%s%s", devname, devnum); - if ((self->fd_control = open (tmpstr, O_WRONLY)) < 0) { - printf("dxr3scr: Failed to open control device %s (%s)\n", - tmpstr, strerror(errno)); - return NULL; - } - - self->priority = dxr3->scr_prio; - - printf("dxr3scr_init: init complete\n"); - return &self->scr; +static char *dxr3_get_id(void) +{ + return "dxr3-mpeg2"; } -static int dxr3_can_handle (video_decoder_t *this_gen, int buf_type) +static int dxr3_can_handle(video_decoder_t *this_gen, int buf_type) { - buf_type &= 0xFFFF0000; - return (buf_type == BUF_VIDEO_MPEG); + buf_type &= 0xFFFF0000; + return (buf_type == BUF_VIDEO_MPEG); } -static void dxr3_init (video_decoder_t *this_gen, vo_instance_t *video_out) +static void dxr3_init(video_decoder_t *this_gen, vo_instance_t *video_out) { - dxr3_decoder_t *this = (dxr3_decoder_t *) this_gen; - metronom_t *metronom = this->metronom; - char tmpstr[128]; - int64_t cur_offset; - - snprintf (tmpstr, sizeof(tmpstr), "%s%s", devname, devnum); - printf("dxr3: Entering video init, devname=%s.\n",tmpstr); - - this->fd_video = -1; /* open later */ - - if ((this->fd_control = open (tmpstr, O_WRONLY)) < 0) { - printf("dxr3: Failed to open control device %s (%s)\n", - tmpstr, strerror(errno)); - return; - } - - video_out->open(video_out); - this->video_out = video_out; - - this->last_pts = metronom->get_current_time(metronom); - this->decoder_thread = pthread_self(); - - this->resync_window = 0; - - cur_offset = metronom->get_option(metronom, METRONOM_AV_OFFSET); - metronom->set_option(metronom, METRONOM_AV_OFFSET, cur_offset - 21600); - /* a note on the 21600: this seems to be a necessary offset, - maybe the dxr3 needs some internal time, however it improves sync a lot */ + dxr3_decoder_t *this = (dxr3_decoder_t *)this_gen; + metronom_t *metronom = this->xine->metronom; + char tmpstr[128]; + int64_t cur_offset; + + snprintf(tmpstr, sizeof(tmpstr), "%s%s", this->devname, this->devnum); +#if LOG_VID + printf("dxr3_decode_video: Entering video init, devname=%s.\n",tmpstr); +#endif + + /* open later, because dxr3_video_out might have it open until we request a frame */ + this->fd_video = -1; + + if ((this->fd_control = open(tmpstr, O_WRONLY)) < 0) { + printf("dxr3_decode_video: Failed to open control device %s (%s)\n", + tmpstr, strerror(errno)); + return; + } + + video_out->open(video_out); + this->video_out = video_out; + + this->resync_window = 0; + this->skip_count = 0; + + cur_offset = metronom->get_option(metronom, METRONOM_AV_OFFSET); + metronom->set_option(metronom, METRONOM_AV_OFFSET, cur_offset - 21600); + /* a note on the 21600: this seems to be a necessary offset, + maybe the dxr3 needs some internal time, however it improves sync a lot */ + +#if LOG_PTS + this->last_vpts = metronom->get_current_time(metronom); +#endif } - -/* *** parse_mpeg_header *** - Does a partial parse of the mpeg buffer, extracting information such as - frame width & height, aspect ratio, and framerate, and sends it to the - video_out plugin via get_frame -*/ -#define HEADER_OFFSET 0 -static void parse_mpeg_header(dxr3_decoder_t *this, uint8_t * buffer) +static void dxr3_decode_data(video_decoder_t *this_gen, buf_element_t *buf) { - /* framerate code... needed for metronom */ - this->frame_rate_code = 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; - - this->have_header_info = 1; + dxr3_decoder_t *this = (dxr3_decoder_t *)this_gen; + ssize_t written; + int64_t vpts; + int i, duration, skip; + vo_frame_t *img; + uint8_t *buffer, byte; + uint32_t shift; + + vpts = 0; + + /* parse frames in the buffer handed in, evaluate headers, + * send frames to video_out and handle some syncing + */ + 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 */ + shift = 0xffffff00; + if (byte == 0xb3) { + /* sequence data */ + parse_mpeg_header(this, buffer); + continue; + } + if (byte == 0xb5) { + /* extension data */ + if ((buffer[0] & 0xf0) == 0x80) + this->repeat_first_field = (buffer[3] >> 1) & 1; + /* check if we can keep syncing */ + if (this->repeat_first_field && this->sync_retry) /* reset counter */ + this->sync_retry = 500; + if (this->repeat_first_field && this->sync_every_frame) { +#if LOG_VID + printf("dxr3: non-progressive video detected. " + "disabling sync_every_frame.\n"); +#endif + this->sync_every_frame = 0; + this->sync_retry = 500; /* see you later */ + } + continue; + } + if (byte != 0x00) /* Don't care what it is. It's not a new frame */ + continue; + /* we have a code for a new frame */ + if (!this->have_header_info) /* this->width et al may still be undefined */ + continue; + if (buf->decoder_flags & BUF_FLAG_PREVIEW) + continue; + + duration = get_duration(this->frame_rate_code, this->repeat_first_field); + /* pretend like we have decoded a frame */ + img = this->video_out->get_frame (this->video_out, + this->width, this->height, this->aspect, + IMGFMT_MPEG, VO_BOTH_FIELDS); + img->pts = buf->pts; + img->bad_frame = 0; + img->duration = duration; + + skip = img->draw(img); + if (skip <= 0) { /* don't skip */ + vpts = img->vpts; /* copy so we can free img */ + + if (this->skip_count) this->skip_count--; + + if (this->resync_window == 0 && this->scr && this->enhanced_mode && + !this->scr->scanning) { + /* we are in sync, so we can lock the stream now */ +#if LOG_VID + printf("dxr3_decode_video: in sync, stream locked\n"); +#endif + dxr3_mvcommand(this->fd_control, MVCOMMAND_SYNC); + this->resync_window = -RESYNC_WINDOW_SIZE; + } + if (this->resync_window != 0 && this->resync_window > -RESYNC_WINDOW_SIZE) + this->resync_window--; + } + else { /* metronom says skip, so don't set vpts */ +#if LOG_VID + printf("dxr3_decode_video: %d frames to skip\n", skip); +#endif + vpts = 0; + + this->skip_count += skip; + if (this->skip_count > SKIP_TOLERANCE) { + /* we have had enough skipping messages now, let's react */ + this->xine->metronom->set_option(this->xine->metronom, + METRONOM_ADJ_VPTS_OFFSET, skip * (int64_t)img->duration / 2); + this->skip_count = 0; + this->resync_window = 0; + } + + if (this->scr && this->scr->scanning) this->resync_window = 0; + if (this->resync_window == 0 && this->scr && this->enhanced_mode && + !this->scr->scanning) { + /* switch off sync mode in the card to allow resyncing */ +#if LOG_VID + printf("dxr3_decode_video: out of sync, allowing stream resync\n"); +#endif + dxr3_mvcommand(this->fd_control, MVCOMMAND_START); + this->resync_window = RESYNC_WINDOW_SIZE; + } + if (this->resync_window != 0 && this->resync_window < RESYNC_WINDOW_SIZE) + this->resync_window++; + } + img->free(img); +#if LOG_PTS + this->last_vpts += img->duration; /* predict vpts */ +#endif + /* if sync_every_frame was disabled, decrease the counter + * for a retry + * (it might be due to crappy studio logos and stuff + * so we should give the main movie a chance) + */ + if (this->sync_retry) { + if (!--this->sync_retry) { +#if LOG_VID + printf("dxr3_decode_video: retrying sync_every_frame"); +#endif + this->sync_every_frame = 1; + } + } + } + if (buf->decoder_flags & BUF_FLAG_PREVIEW) return; + + /* 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%s", this->devname, this->devnum); + if ((this->fd_video = open(tmpstr, O_WRONLY | O_NONBLOCK)) < 0) { + printf("dxr3_decode_video: Failed to open video device %s (%s)\n", + tmpstr, strerror(errno)); + return; + } + } + + /* We may want to issue a SETPTS, so make sure the scr plugin + * is running and registered. Unfortuantely wa cannot do this + * earlier, because the dxr3's internal scr gets confused + * when started with a closed video device. Maybe this is a + * driver bug and gets fixed somewhen. FIXME: We might then + * want to move this code to dxr3_init. + */ + if (!this->scr) { + int64_t time; + + time = this->xine->metronom->get_current_time( + this->xine->metronom); + + this->scr = dxr3_scr_init(this->xine); + this->scr->scr_plugin.start(&this->scr->scr_plugin, time); + this->xine->metronom->register_scr( + this->xine->metronom, &this->scr->scr_plugin); + if (this->xine->metronom->scr_master == &this->scr->scr_plugin) +#if LOG_VID + printf("dxr3_decode_video: dxr3_scr plugin is master\n"); +#endif + else +#if LOG_VID + printf("dxr3_decode_video: dxr3scr plugin is NOT master\n"); +#endif + } + + /* update the pts timestamp in the card, which tags the data we write to it */ + if (vpts) { + int64_t delay; + + delay = vpts - this->xine->metronom->get_current_time( + this->xine->metronom); +#if LOG_PTS + printf("dxr3_decode_video: SETPTS got %lld expected = %lld (delta %lld) delay = %lld\n", + vpts, this->last_vpts, vpts - this->last_vpts, delay); + this->last_vpts = vpts; +#endif + /* SETPTS only if less then one second in the future and + * either buffer has pts or sync_every_frame is set */ + if ((delay > 0) && (delay < 90000) && + (this->sync_every_frame || buf->pts)) { + uint32_t vpts32 = vpts; + /* update the dxr3's current pts value */ + if (ioctl(this->fd_video, EM8300_IOCTL_VIDEO_SETPTS, &vpts32)) + printf("dxr3_decode_video: set video pts failed (%s)\n", + strerror(errno)); + } + if (delay >= 90000) /* frame more than 1 sec ahead */ + printf("dxr3_decode_video: WARNING: vpts %lld is %.02f seconds ahead of time!\n", + vpts, delay/90000.0); + if (delay < 0) + printf("dxr3_decode_video: WARNING: overdue frame.\n"); + } +#if LOG_PTS + else if (buf->pts) { + printf("dxr3_decode_video: skip buf->pts = %lld (no vpts) last_vpts = %lld\n", + buf->pts, this->last_vpts); + } +#endif + + /* now write the content to the dxr3 mpeg device and, in a dramatic + * break with open source tradition, check the return value + */ + written = write(this->fd_video, buf->content, buf->size); + if (written < 0) { + if (errno == EAGAIN) { + printf("dxr3_decode_video: write to device would block. flushing\n"); + dxr3_flush(this_gen); + } else { + printf("dxr3_decode_video: video device write failed (%s)\n", + strerror(errno)); + } + return; + } + if (written != buf->size) + printf("dxr3_decode_video: Could only write %d of %d video bytes.\n", + written, buf->size); } -static int get_duration(int framecode, int repeat_first_field) +static void dxr3_flush(video_decoder_t *this_gen) { - int duration; - switch (framecode){ - case 1: /* 23.976 */ - duration=3913; - break; - case 2: /* 24.000 */ - duration=3750; - break; - case 3: /* 25.000 */ - duration=repeat_first_field ? 5400 : 3600; - /*duration=3600;*/ - break; - case 4: /* 29.970 */ - duration=repeat_first_field ? 4505 : 3003; - /*duration=3003;*/ - break; - case 5: /* 30.000 */ - duration=3000; - break; - case 6: /* 50.000 */ - duration=1800; - break; - case 7: /* 59.940 */ - duration=1525; - break; - case 8: /* 60.000 */ - duration=1509; - break; - default: - printf("dxr3: warning: unknown frame rate code %d: using PAL\n", framecode); - duration=3600; /* PAL 25fps */ - break; - } - return duration; + dxr3_decoder_t *this = (dxr3_decoder_t *)this_gen; + + if (this->fd_video >= 0) fsync(this->fd_video); } static void dxr3_reset(video_decoder_t *this_gen) { -/* dxr3_decoder_t *this = (dxr3_decoder_t *) this_gen; */ } -/* *** dxr3_flush *** - flush the dxr3's onboard buffers - but I'm not sure that this is - doing that - more testing is required. -*/ -static void dxr3_flush (video_decoder_t *this_gen) +static void dxr3_close(video_decoder_t *this_gen) { - dxr3_decoder_t *this = (dxr3_decoder_t *) this_gen; - /* Don't flush, causes still images to disappear. We don't seem - * to need it anyway... */ - printf("dxr3_decoder: flush requested, disabled for the moment.\n"); - /* dxr3_mvcommand(this->fd_control, 0x11); - this->have_header_info = 0;*/ - if (this->fd_video >= 0) - fsync(this->fd_video); + dxr3_decoder_t *this = (dxr3_decoder_t *)this_gen; + metronom_t *metronom = this->xine->metronom; + int64_t cur_offset; + + if (this->scr) { + metronom->unregister_scr(metronom, &this->scr->scr_plugin); + this->scr->scr_plugin.exit(&this->scr->scr_plugin); + this->scr = NULL; + } + + /* revert av_offset change done in dxr3_init */ + cur_offset = metronom->get_option(metronom, METRONOM_AV_OFFSET); + metronom->set_option(metronom, METRONOM_AV_OFFSET, cur_offset + 21600); + + if (this->fd_video >= 0) close(this->fd_video); + this->fd_video = -1; + + this->video_out->close(this->video_out); + + this->have_header_info = 0; } - -static void dxr3_decode_data (video_decoder_t *this_gen, buf_element_t *buf) +static void dxr3_dispose(video_decoder_t *this_gen) { - dxr3_decoder_t *this = (dxr3_decoder_t *) this_gen; - ssize_t written; - int64_t vpts; - int i, duration, skip; - vo_frame_t *img; - uint8_t *buffer, byte; - uint32_t shift; - - if (buf->decoder_flags & BUF_FLAG_PREVIEW) return; - - vpts = 0; - - /* 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 == 0xb5) { - /* extension data */ - if ((buffer[0] & 0xf0) == 0x80) { - /* picture coding extension */ - this->repeat_first_field = (buffer[3] >> 1) & 1; - } - /* check if we can keep syncing */ - if (this->repeat_first_field && this->sync_every_frame) { - /* metronom can't handle variable duration */ - printf("dxr3: non-progressive video detected. " - "disabling sync_every_frame.\n"); - this->sync_every_frame = 0; - this->sync_retry = 500; /* see you later */ - } - if (this->repeat_first_field && this->sync_retry) { - /* reset counter */ - this->sync_retry = 500; - } - continue; - } - if (byte != 0x00) { - /* Don't care what it is. It's not a new frame */ - continue; - } - /* we have a code for a new frame */ - if (! this->have_header_info) { - /* this->width et al may still be undefined */ - continue; - } - duration = get_duration(this->frame_rate_code, - this->repeat_first_field); - /* pretend like we have decoded a frame */ - img = this->video_out->get_frame (this->video_out, - this->width, - this->height, - this->aspect, - IMGFMT_MPEG, - VO_BOTH_FIELDS); - img->pts=buf->pts; - img->bad_frame = 0; - img->duration = duration; - /* draw calls metronom->got_video_frame with img pts and scr - and stores the return value in img->vpts - Calling draw with buf->pts==0 is okay; metronome will - extrapolate a value. */ - skip = img->draw(img); - if (skip <= 0) { /* don't skip */ - vpts = img->vpts; /* copy so we can free img */ - - if (this->resync_window == 0 && this->scr && this->enhanced_mode && !scanning_mode) { - /* we are in sync, so we can lock the stream now */ - printf("dxr3: in sync, stream locked\n"); - dxr3_mvcommand(this->fd_control, MVCOMMAND_SYNC); - this->resync_window = -RESYNC_WINDOW_SIZE; - } - if (this->resync_window != 0 && this->resync_window > -RESYNC_WINDOW_SIZE) - this->resync_window--; - } - else { /* metronom says skip, so don't set vpts */ - printf("dxr3: skip = %d\n", skip); - vpts = 0; - - if (scanning_mode) this->resync_window = 0; - if (this->resync_window == 0 && this->scr && this->enhanced_mode && !scanning_mode) { - /* switch off sync mode in the card to allow resyncing */ - printf("dxr3: out of sync, allowing stream resync\n"); - dxr3_mvcommand(this->fd_control, MVCOMMAND_START); - this->resync_window = RESYNC_WINDOW_SIZE; - } - if (this->resync_window != 0 && this->resync_window < RESYNC_WINDOW_SIZE) - this->resync_window++; - } - img->free(img); - this->last_pts += duration; /* predict vpts */ - - /* if sync_every_frame was disabled, decrease the counter - * for a retry - * (it might be due to crappy studio logos and stuff - * so we should give the main movie a chance) */ - if (this->sync_retry) { - this->sync_retry--; - if (!this->sync_retry) { - printf("dxr3: retrying sync_every_frame"); - this->sync_every_frame = 1; - } - } - } - - - /* 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%s", devname, devnum); - if ((this->fd_video = open (tmpstr, O_WRONLY | O_NONBLOCK)) < 0) { - printf("dxr3: Failed to open video device %s (%s)\n", - tmpstr, strerror(errno)); - return; - } - } + free(this_gen); +} - /* We may want to issue a SETPTS, so make sure the scr plugin - is running and registered. Unfortuantely wa cannot do this - earlier, because the dxr3's internal scr gets confused - when started with a closed video device. Maybe this is a - driver bug and gets fixed somewhen. FIXME: We might then - want to move this code to dxr3_init. */ - if (!this->scr) { - int64_t time; - - time = this->metronom->get_current_time( - this->metronom); - - this->scr = dxr3scr_init(this); - this->scr->start(this->scr, time); - this->metronom->register_scr( - this->metronom, this->scr); - if (this->metronom->scr_master == this->scr) { - printf("dxr3: dxr3scr plugin is master\n"); - } else { - printf("dxr3: dxr3scr plugin is NOT master\n"); - } - } - /* From time to time, update the pts value - * FIXME: the exact conditions here are a bit uncertain... */ - if (vpts) - { - int64_t delay; - - delay = vpts - this->metronom->get_current_time( - this->metronom); -#if LOG_PTS - printf("dxr3: SETPTS got %lld expected = %lld (delta %lld) delay = %lld\n", - vpts, this->last_pts, vpts-this->last_pts, delay); -#endif - this->last_pts = vpts; - - /* SETPTS only if less then one second in the future and - * either buffer has pts or sync_every_frame is set */ - if ((delay > 0) && (delay < 90000) && - (this->sync_every_frame || buf->pts)) { - uint32_t vpts32 = vpts; - /* update the dxr3's current pts value */ - if (ioctl(this->fd_video, EM8300_IOCTL_VIDEO_SETPTS, &vpts32)) - printf("dxr3: set video pts failed (%s)\n", - strerror(errno)); - } - if (delay >= 90000) { - /* frame more than 1 sec ahead */ - printf("dxr3: WARNING: vpts %lld is %.02f seconds ahead of time!\n", - vpts, delay/90000.0); - } - if (delay < 0) { - printf("dxr3: WARNING: overdue frame.\n"); - } - } -#if LOG_PTS - else if (buf->pts) { - printf("dxr3: skip buf->pts = %lld (no vpts) last_vpts = %lld\n", - buf->pts, this->last_pts); - } +static int dxr3_present(xine_t *xine) +{ + int info; + + if (xine && xine->video_driver) { + info = xine->video_driver->get_property(xine->video_driver, VO_PROP_VO_TYPE); +#ifdef LOG_VID + printf("dxr3_decode_video: dxr3 presence test: info = %d\n", info); #endif - - /* now write the content to the dxr3 mpeg device and, in a dramatic - break with open source tradition, check the return value */ - written = write(this->fd_video, buf->content, buf->size); - if (written < 0) { - if (errno == EAGAIN) { - printf("dxr3: write to device would block. flushing\n"); - dxr3_flush(this_gen); - } - else { - printf("dxr3: video device write failed (%s)\n", - strerror(errno)); - } - return; - } - if (written != buf->size) - printf("dxr3: Could only write %d of %d video bytes.\n", - written, buf->size); - + if (info != VO_TYPE_DXR3) + return 0; + } + return 1; } -static void dxr3_close (video_decoder_t *this_gen) +static int dxr3_mvcommand(int fd_control, int command) { - dxr3_decoder_t *this = (dxr3_decoder_t *) this_gen; - metronom_t *metronom = this->metronom; - int64_t cur_offset; - - if (this->scr) { - metronom->unregister_scr(metronom, this->scr); - this->scr->exit(this->scr); - this->scr = 0; - } - - /* revert av_offset change done in dxr3_init */ - cur_offset = metronom->get_option(metronom, METRONOM_AV_OFFSET); - metronom->set_option(metronom, METRONOM_AV_OFFSET, cur_offset + 21600); - - if (this->fd_video >= 0) - close(this->fd_video); - this->fd_video = -1; - - this->video_out->close(this->video_out); - this->have_header_info = 0; + em8300_register_t regs; + + regs.microcode_register = 1; + regs.reg = 0; + regs.val = command; + + return ioctl(fd_control, EM8300_IOCTL_WRITEREG, ®s); } -static char *dxr3_get_id(void) { - return "dxr3-mpeg2"; -} - -static void dxr3_update_enhanced_mode(void *this_gen, cfg_entry_t *entry) +static void parse_mpeg_header(dxr3_decoder_t *this, uint8_t * buffer) { - ((dxr3_decoder_t*)this_gen)->enhanced_mode = entry->num_value; - printf("dxr3: mpeg playback: set enhanced mode to %s\n", - (entry->num_value ? "on" : "off")); + this->frame_rate_code = buffer[HEADER_OFFSET+3] & 15; + 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; + + this->have_header_info = 1; } -static void dxr3_update_sync_mode(void *this_gen, cfg_entry_t *entry) +static int get_duration(int framecode, int repeat_first_field) { - ((dxr3_decoder_t*)this_gen)->sync_every_frame = entry->num_value; - printf("dxr3: set sync_every_frame to %s\n", - (entry->num_value ? "on" : "off")); + int duration; + + switch (framecode) { + case 1: /* 23.976 */ + duration = 3913; + break; + case 2: /* 24.000 */ + duration = 3750; + break; + case 3: /* 25.000 */ + duration = repeat_first_field ? 5400 : 3600; + break; + case 4: /* 29.970 */ + duration = repeat_first_field ? 4505 : 3003; + break; + case 5: /* 30.000 */ + duration = 3000; + break; + case 6: /* 50.000 */ + duration = 1800; + break; + case 7: /* 59.940 */ + duration = 1525; + break; + case 8: /* 60.000 */ + duration = 1509; + break; + default: + printf("dxr3_decode_video: warning: unknown frame rate code %d: using PAL\n", framecode); + duration = 3600; /* PAL 25fps */ + break; + } + + return duration; } -static void dxr3_flush_decoder(void *this_gen, cfg_entry_t *entry) +static void dxr3_update_priority(void *this_gen, cfg_entry_t *entry) { - /* dxr3_decoder_t *this = (dxr3_decoder_t*)this_gen; */ - printf("dxr3: flush requested\n"); -/* - pthread_kill(this->decoder_thread, SIGINT); - if (this->fd_video >= 0) { - close(this->fd_video); - this->fd_video = -1; - } -*/ - dxr3_flush(this_gen); - /* reset to false, so it'll look like a button in the gui :-) */ - entry->num_value = 0; + ((dxr3_decoder_t *)this_gen)->video_decoder.priority = entry->num_value; + printf("dxr3_decode_video: setting decoder priority to %d\n", + entry->num_value); } -static void dxr3_dispose (video_decoder_t *this_gen) { - free (this_gen); +static void dxr3_update_sync_mode(void *this_gen, cfg_entry_t *entry) +{ + ((dxr3_decoder_t *)this_gen)->sync_every_frame = entry->num_value; + printf("dxr3_decode_video: setting sync_every_frame to %s\n", + (entry->num_value ? "on" : "off")); } -video_decoder_t *init_video_decoder_plugin (int iface_version, - xine_t *xine) +static void dxr3_update_enhanced_mode(void *this_gen, cfg_entry_t *entry) { - dxr3_decoder_t *this ; - config_values_t *cfg; - char *tmpstr; - int dashpos; - int result; - - if (iface_version != 8) { - printf( "dxr3_decode_video: plugin doesn't support plugin API version %d.\n" - "dxr3_decode_video: this means there's a version mismatch between xine and this\n" - "dxr3_decode_video: decoder plugin. Installing current plugins should help.\n", - iface_version); - return NULL; - } - - cfg = xine->config; - tmpstr = cfg->register_string (cfg, LOOKUP_DEV, DEFAULT_DEV, "Dxr3: Device Name",NULL,NULL,NULL); - strncpy(devname, tmpstr, 128); - devname[127] = '\0'; - dashpos = strlen(devname) - 2; /* the dash in the new device naming scheme would be here */ - if (devname[dashpos] == '-') { - /* use new device naming scheme with trailing number */ - strncpy(devnum, &devname[dashpos], 3); - devname[dashpos] = '\0'; - } else { - /* use old device naming scheme without trailing number */ - /* FIXME: remove this when everyone uses em8300 >=0.12.0 */ - devnum[0] = '\0'; - } - - result = dxr3_presence_test ( xine ); - if (!result) return NULL; - - this = (dxr3_decoder_t *) malloc (sizeof (dxr3_decoder_t)); - - this->video_decoder.interface_version = iface_version; - this->video_decoder.can_handle = dxr3_can_handle; - this->video_decoder.init = dxr3_init; - this->video_decoder.decode_data = dxr3_decode_data; - this->video_decoder.close = dxr3_close; - this->video_decoder.get_identifier = dxr3_get_id; - this->video_decoder.flush = dxr3_flush; - this->video_decoder.reset = dxr3_reset; - this->video_decoder.dispose = dxr3_dispose; - this->video_decoder.priority = cfg->register_num(cfg, - "dxr3.decoder_priority", 10, "Dxr3: video decoder priority", NULL, NULL, NULL); - - this->config = cfg; - this->metronom = xine->metronom; - this->frame_rate_code = 0; - this->repeat_first_field = 0; - this->sync_every_frame = 1; - - this->scr = NULL; - this->scr_prio = cfg->register_num(cfg, "dxr3.scr_priority", 10, "Dxr3: SCR plugin priority",NULL,NULL,NULL); - - this->sync_every_frame = cfg->register_bool(cfg, - "dxr3.sync_every_frame", - 0, - "Try to sync video every frame", - "This is relevant for progressive video only (most PAL films)", - dxr3_update_sync_mode, this); - - this->sync_retry = 0; - - this->enhanced_mode = cfg->register_bool(cfg, - "dxr3.alt_play_mode", - 1, - "Use alternate Play mode", - "Enabling this option will utilise a slightly different play mode", - dxr3_update_enhanced_mode, this); - - /* a boolean that's really a button; request a decoder flush */ - cfg->register_bool(cfg, "dxr3.flush", 0, "Flush decoder now", - "Flushing the decoder might unfreeze playback or restore sync", - dxr3_flush_decoder, this); - - 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; + ((dxr3_decoder_t *)this_gen)->enhanced_mode = entry->num_value; + printf("dxr3_decode_video: setting enhanced mode to %s\n", + (entry->num_value ? "on" : "off")); } - |