summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dxr3/mpeg_encoders.c453
1 files changed, 453 insertions, 0 deletions
diff --git a/src/dxr3/mpeg_encoders.c b/src/dxr3/mpeg_encoders.c
new file mode 100644
index 000000000..f36d3a3b9
--- /dev/null
+++ b/src/dxr3/mpeg_encoders.c
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2000-2001 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: mpeg_encoders.c,v 1.1 2001/12/16 19:06:49 hrm Exp $
+ *
+ * mpeg encoders for the dxr3 video out plugin.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_LIBRTE
+# define _GNU_SOURCE
+# include <unistd.h>
+# include <rte.h>
+#endif
+#ifdef HAVE_LIBFAME
+# include <fame.h>
+#endif
+
+#include "dxr3_video_out.h"
+
+#define MV_COMMAND 0
+/* buffer size for encoded mpeg1 stream; will hold one intra frame
+ * at 640x480 typical sizes are <50 kB. 512 kB should be plenty */
+#define DEFAULT_BUFFER_SIZE 512*1024
+
+#ifdef HAVE_LIBRTE
+typedef struct {
+ encoder_data_t encoder_data;
+ rte_context* context; /* handle for encoding */
+ void* rte_ptr; /* buffer maintened by librte */
+ double rte_time; /* frame time (s) */
+ double rte_time_step; /* time per frame (s) */
+ double rte_bitrate; /* mpeg out bitrate, default 2.3e6 bits/s */
+} rte_data_t;
+
+
+static void mp1e_callback(rte_context *context, void *data, ssize_t size,
+ void* user_data)
+{
+ dxr3_driver_t *this = (dxr3_driver_t*)user_data;
+ em8300_register_t regs;
+ char tmpstr[128];
+
+ if (this->enhanced_mode)
+ {
+ regs.microcode_register=1; /* Yes, this is a MC Reg */
+ regs.reg = MV_COMMAND;
+ regs.val=6; /* Mike's mystery number :-) */
+ ioctl(this->fd_control, EM8300_IOCTL_WRITEREG, &regs);
+ }
+ if (this->fd_video < 0) {
+ snprintf (tmpstr, sizeof(tmpstr), "%s_mv", this->devname);
+ this->fd_video = open(tmpstr, O_WRONLY);
+ }
+ if (write(this->fd_video, data, size) < 0)
+ perror("dxr3: writing to video device");
+}
+
+static int rte_on_update_format(dxr3_driver_t *drv)
+{
+ rte_data_t *this = (rte_data_t*)drv->enc;
+ rte_context* context;
+ rte_codec *codec;
+ int width, height;
+ char tmpstr[128];
+
+ width = drv->video_width;
+ height = drv->video_height;
+
+ if (this->context != 0) /* already done */
+ return 0;
+
+ this->context = rte_context_new (width, height, "mp1e", drv);
+ if (! this->context) {
+ printf("failed to get rte context.\n");
+ return 1;
+ }
+ context = this->context; /* shortcut */
+ rte_set_verbosity(context, 2);
+ /* get mpeg codec handle */
+ codec = rte_codec_set(context, RTE_STREAM_VIDEO, 0, "mpeg1_video");
+ if (! codec) {
+ printf("dxr3: could not create codec.\n");
+ rte_context_destroy(context);
+ context = 0;
+ return 1;
+ }
+ this->rte_bitrate=drv->config->register_range(drv->config,"dxr3.rte_bitrate",10000, 1000,20000, "Dxr3enc: rte mpeg output bitrate (kbit/s)",NULL,NULL,NULL);
+ this->rte_bitrate *= 1000; /* config in kbit/s, rte wants bit/s */
+ /* FIXME: this needs to be replaced with a codec option call.
+ * However, there seems to be none for the colour format!
+ * So we'll use the deprecated set_video_parameters instead.
+ * Alternative is to manually set context->video_format (RTE_YU... )
+ * and context->video_bytes (= width * height * bytes/pixel) */
+ rte_set_video_parameters(context,
+ (drv->format == IMGFMT_YV12 ? RTE_YUV420 : RTE_YUYV),
+ context->width, context->height,
+ context->video_rate, context->output_video_bits,
+ context->gop_sequence);
+ /* Now set a whole bunch of codec options */
+ /* If I understand correctly, virtual_frame_rate is the frame rate
+ * of the source (can be anything), while coded_frame_rate must be
+ * one of the mpeg1 alloweds */
+ if (!rte_option_set(codec, "virtual_frame_rate", drv->fps))
+ printf("dxr3: WARNING: rte_option_set failed; virtual_frame_rate=%g.\n",drv->fps);
+ if (!rte_option_set(codec, "coded_frame_rate", drv->fps))
+ printf("dxr3: WARNING: rte_option_set failed; coded_frame_rate=%g.\n",drv->fps);
+ if (!rte_option_set(codec, "bit_rate", (int)this->rte_bitrate))
+ printf("dxr3: WARNING: rte_option_set failed; bit_rate = %d.\n",
+ (int)this->rte_bitrate);
+ if (!rte_option_set(codec, "gop_sequence", "I"))
+ printf("dxr3: WARNING: rte_option_set failed; gop_sequence = \"I\".\n");
+ /* just to be sure, disable motion comp (not needed in I frames) */
+ if (!rte_option_set(codec, "motion_compensation", 0))
+ printf("dxr3: WARNING: rte_option_set failed; motion_compensation = 0.\n");
+ rte_set_input(context, RTE_VIDEO, RTE_PUSH, FALSE, NULL, NULL, NULL);
+ snprintf (tmpstr, sizeof(tmpstr), "%s_mv", drv->devname);
+ rte_set_output(context, mp1e_callback, NULL, NULL);
+ if (!rte_init_context(context)) {
+ printf("dxr3: cannot init the context: %s\n",
+ context->error);
+ rte_context_delete(context);
+ context = 0;
+ return 1;
+ }
+ /* do the sync'ing and start encoding */
+ if (!rte_start_encoding(context)) {
+ printf("dxr3: cannot start encoding: %s\n",
+ context->error);
+ rte_context_delete(context);
+ context = 0;
+ return 1;
+ }
+ this->rte_ptr = rte_push_video_data(context, NULL, 0);
+ if (! this->rte_ptr) {
+ printf("dxr3: failed to get encoder buffer pointer.\n");
+ return 1;
+ }
+ this->rte_time = 0.0;
+ this->rte_time_step = 1.0/drv->fps;
+
+ return 0;
+}
+
+static int rte_on_display_frame( dxr3_driver_t* drv, dxr3_frame_t* frame )
+{
+ int size;
+ rte_data_t* this = (rte_data_t*)drv->enc;
+ size = frame->width * frame->oheight;
+ if (frame->format == IMGFMT_YV12)
+ xine_fast_memcpy(this->rte_ptr, frame->real_base[0], size*3/2);
+ else
+ xine_fast_memcpy(this->rte_ptr, frame->real_base[0], size*2);
+ this->rte_time += this->rte_time_step;
+ this->rte_ptr = rte_push_video_data(this->context, this->rte_ptr,
+ this->rte_time);
+ frame->vo_frame.displayed(&frame->vo_frame);
+ return 0;
+}
+
+static int rte_on_close( dxr3_driver_t *drv )
+{
+ rte_data_t *this = (rte_data_t*)drv->enc;
+ if (this->context) {
+ rte_stop(this->context);
+ rte_context_delete(this->context);
+ this->context = 0;
+ }
+ free(this);
+ drv->enc = 0;
+ return 0;
+}
+
+int dxr3_rte_init( dxr3_driver_t *drv )
+{
+ rte_data_t* this;
+ if (! rte_init() ) {
+ printf("dxr3: failed to init librte\n");
+ return 1;
+ }
+ this = malloc(sizeof(rte_data_t));
+ if (!this)
+ return 1;
+ memset(this, 0, sizeof(rte_data_t));
+ this->encoder_data.type = ENC_RTE;
+ this->context = 0;
+ this->encoder_data.on_update_format = rte_on_update_format;
+ this->encoder_data.on_frame_copy = 0;
+ this->encoder_data.on_display_frame = rte_on_display_frame;
+ this->encoder_data.on_close = rte_on_close;
+ drv->enc = (encoder_data_t*)this;
+ return 0;
+}
+
+#endif
+
+#ifdef HAVE_LIBFAME
+typedef struct {
+ encoder_data_t encoder_data;
+ fame_context_t *fc; /* needed for fame calls */
+ fame_parameters_t fp;
+ fame_yuv_t yuv;
+ /* temporary buffer for mpeg data */
+ char *buffer;
+ /* temporary buffer for YUY2->YV12 conversion */
+ uint8_t *out[3]; /* aligned buffer for YV12 data */
+ uint8_t *buf; /* unaligned YV12 buffer */
+} fame_data_t;
+
+
+static fame_parameters_t dummy_init_fp = FAME_PARAMETERS_INITIALIZER;
+
+static int fame_on_update_format(dxr3_driver_t *drv)
+{
+ fame_data_t *this = (fame_data_t*)drv->enc;
+ double fps;
+
+ /* if YUY2 and dimensions changed, we need to re-allocate the
+ * internal YV12 buffer */
+ if (this->buf) free(this->buf);
+ this->buf = 0;
+ this->out[0] = this->out[1] = this->out[2] = 0;
+ if (drv->format == IMGFMT_YUY2) {
+ int image_size = drv->video_width * drv->video_height;
+
+ this->out[0] = malloc_aligned(16, image_size * 3/2,
+ (void*)&this->buf);
+ this->out[1] = this->out[0] + image_size;
+ this->out[2] = this->out[1] + image_size/4;
+
+ /* fill with black (yuv 16,128,128) */
+ memset(this->out[0], 16, image_size);
+ memset(this->out[1], 128, image_size/4);
+ memset(this->out[2], 128, image_size/4);
+
+ printf("dxr3: Using YUY2->YV12 conversion\n");
+ }
+
+ if (!this->fc)
+ this->fc = fame_open();
+ if (!this->fc) {
+ printf("Couldn't start the FAME library\n");
+ return 1;
+ }
+
+ if (!this->buffer)
+ this->buffer = (unsigned char *) malloc (DEFAULT_BUFFER_SIZE);
+ if (!this->buffer) {
+ printf("Couldn't allocate temp buffer for mpeg data\n");
+ return 1;
+ }
+
+ this->fp = dummy_init_fp;
+ this->fp.quality=drv->config->register_range(drv->config,"dxr3.fame_quality",90, 10,100, "Dxr3enc: fame mpeg encoding quality",NULL,NULL,NULL);
+ /* the really interesting bit is the quantizer scale. The formula
+ * below is copied from libfame's sources (could be changed in the
+ * future) */
+ printf("dxr3: quality %d -> quant scale = %d\n", this->fp.quality,
+ 1 + (30*(100-this->fp.quality)+50)/100);
+ this->fp.width = drv->video_width;
+ this->fp.height = drv->video_height;
+ this->fp.profile = "mpeg1";
+ this->fp.coding = "I";
+ this->fp.verbose = 1; /* we don't need any more info.. thanks :) */
+
+ /* start guessing the framerate */
+ fps = drv->fps;
+ if (fabs(fps - 25) < 0.01) { /* PAL */
+ printf("dxr3: setting mpeg output framerate to PAL (25 Hz)\n");
+ this->fp.frame_rate_num = 25; this->fp.frame_rate_den = 1;
+ }
+ else if (fabs(fps - 24) < 0.01) { /* FILM */
+ printf("dxr3: setting mpeg output framerate to FILM (24 Hz))\n");
+ this->fp.frame_rate_num = 24; this->fp.frame_rate_den = 1;
+ }
+ else if (fabs(fps - 23.976) < 0.01) { /* NTSC-FILM */
+ printf("dxr3: setting mpeg output framerate to NTSC-FILM (23.976 Hz))\n");
+ this->fp.frame_rate_num = 24000; this->fp.frame_rate_den = 1001;
+ }
+ else if (fabs(fps - 29.97) < 0.01) { /* NTSC */
+ printf("dxr3: setting mpeg output framerate to NTSC (29.97 Hz)\n");
+ this->fp.frame_rate_num = 30000; this->fp.frame_rate_den = 1001;
+ }
+ else { /* try 1/fps, if not legal, libfame will go to PAL */
+ this->fp.frame_rate_num = (int)(fps + 0.5); this->fp.frame_rate_den = 1;
+ printf("dxr3: trying to set mpeg output framerate to %d Hz\n",
+ this->fp.frame_rate_num);
+ }
+ fame_init (this->fc, &this->fp, this->buffer, DEFAULT_BUFFER_SIZE);
+
+ return 0;
+}
+
+static int fame_on_frame_copy(dxr3_driver_t *drv, dxr3_frame_t *frame,
+ uint8_t **src)
+{
+ int size, i, j, hoffset, w2;
+ uint8_t *y, *u, *v, *yuy2;
+ fame_data_t *this = (fame_data_t*)drv->enc;
+
+ if (frame->vo_frame.bad_frame)
+ return 0;
+
+ if (frame->copy_calls == frame->height/16) {
+ /* shouldn't happen */
+ printf("dxr3: Internal error. Too many calls to dxr3_frame_copy (%d)\n",
+ frame->copy_calls);
+ return 1;
+ }
+
+ if (frame->vo_frame.format == IMGFMT_YUY2) {
+ /* need YUY2->YV12 conversion */
+ if (! (this->out[0] && this->out[1] && this->out[2]) ) {
+ printf("dxr3: Internal error. Internal YV12 buffer not created.\n");
+ return 1;
+ }
+ /* need conversion */
+ hoffset = ((frame->oheight - frame->height)/32)*16;
+ y = this->out[0] + frame->width*(hoffset + frame->copy_calls*16);
+ u = this->out[1] + frame->width/2*(hoffset/2 + frame->copy_calls*8);
+ v = this->out[2] + frame->width/2*(hoffset/2 + frame->copy_calls*8);
+ yuy2 = src[0];
+ w2 = frame->width/2;
+ /* we get 16 lines each time */
+ for (i=0; i<16; i+=2) {
+ for (j=0; j<w2; j++) {
+ /* packed YUV 422 is: Y[i] U[i] Y[i+1] V[i] */
+ *(y++) = *(yuy2++);
+ *(u++) = *(yuy2++);
+ *(y++) = *(yuy2++);
+ *(v++) = *(yuy2++);
+ }
+ /* down sampling */
+ for (j=0; j<w2; j++) {
+ /* skip every second line for U and V */
+ *(y++) = *(yuy2++);
+ yuy2++;
+ *(y++) = *(yuy2++);
+ yuy2++;
+ }
+ }
+ /* reset for encoder */
+ y = this->out[0];
+ u = this->out[1];
+ v = this->out[2];
+ }
+ else { /* YV12 */
+ y = frame->real_base[0];
+ u = frame->real_base[1];
+ v = frame->real_base[2];
+ }
+
+ frame->copy_calls++;
+
+ /* frame complete yet? */
+ if (frame->copy_calls != frame->height/16)
+ return 0;
+ /* frame is complete: encode */
+ this->yuv.y=y;
+ this->yuv.u=u;
+ this->yuv.v=v;
+ size = fame_encode_frame(this->fc, &this->yuv, NULL);
+ if (size >= DEFAULT_BUFFER_SIZE) {
+ printf("dxr3: warning, mpeg buffer too small!\n");
+ size = DEFAULT_BUFFER_SIZE;
+ }
+ /* alloc frame does not allocate the mpeg buffer. do this now */
+ if (! frame->mpeg) {
+ frame->mpeg = (unsigned char *) malloc (DEFAULT_BUFFER_SIZE);
+ }
+ if (! frame->mpeg) {
+ printf("dxr3: error, could not allocate mpeg buffer!\n");
+ return 1;
+ }
+ /* copy mpeg data to frame */
+ xine_fast_memcpy(frame->mpeg, this->buffer, size);
+ frame->mpeg_size = size;
+ return 0;
+}
+
+
+static int fame_on_display_frame( dxr3_driver_t* drv, dxr3_frame_t* frame)
+{
+ char tmpstr[128];
+ em8300_register_t regs;
+ if (drv->enhanced_mode)
+ {
+ regs.microcode_register=1; /* Yes, this is a MC Reg */
+ regs.reg = MV_COMMAND;
+ regs.val=6; /* Mike's mystery number :-) */
+ ioctl(drv->fd_control, EM8300_IOCTL_WRITEREG, &regs);
+ }
+
+ if (drv->fd_video < 0) {
+ snprintf (tmpstr, sizeof(tmpstr), "%s_mv", drv->devname);
+ drv->fd_video = open(tmpstr, O_WRONLY);
+ }
+ if (write(drv->fd_video, frame->mpeg, frame->mpeg_size) < 0)
+ perror("dxr3: writing to video device");
+ frame->vo_frame.displayed(&frame->vo_frame);
+ return 0;
+}
+
+static int fame_on_close( dxr3_driver_t *drv )
+{
+ fame_data_t *this = (fame_data_t*)drv->enc;
+ if (this->fc) {
+ fame_close(this->fc);
+ }
+ free(this);
+ drv->enc = 0;
+ return 0;
+}
+
+int dxr3_fame_init( dxr3_driver_t *drv )
+{
+ fame_data_t* this;
+ this = malloc(sizeof(fame_data_t));
+ if (! this)
+ return 1;
+ memset(this, 0, sizeof(fame_data_t));
+ this->encoder_data.type = ENC_FAME;
+ /* fame context */
+ this->fc = 0;
+ this->encoder_data.on_update_format = fame_on_update_format;
+ this->encoder_data.on_frame_copy = fame_on_frame_copy;
+ this->encoder_data.on_display_frame = fame_on_display_frame;
+ this->encoder_data.on_close = fame_on_close;
+ drv->enc = (encoder_data_t*)this;
+ return 0;
+}
+
+#endif
+