diff options
Diffstat (limited to 'src/dxr3/dxr3_mpeg_encoders.c')
-rw-r--r-- | src/dxr3/dxr3_mpeg_encoders.c | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/src/dxr3/dxr3_mpeg_encoders.c b/src/dxr3/dxr3_mpeg_encoders.c new file mode 100644 index 000000000..4dfc4cccb --- /dev/null +++ b/src/dxr3/dxr3_mpeg_encoders.c @@ -0,0 +1,543 @@ +/* + * 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: dxr3_mpeg_encoders.c,v 1.1 2002/05/24 22:09:44 miguelfreitas Exp $ + */ + +/* mpeg encoders for the dxr3 video out plugin. + * supports the libfame and librte mpeg encoder libraries. + */ + +#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 <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <math.h> + +#include "xineutils.h" +#include "video_out_dxr3.h" + +#define LOG_ENC 1 + +/* 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 +/* initialization function */ +int dxr3_rte_init(dxr3_driver_t *drv); + +/* functions required by encoder api */ +static int rte_on_update_format(dxr3_driver_t *drv, dxr3_frame_t *frame); +static int rte_on_display_frame(dxr3_driver_t *drv, dxr3_frame_t *frame); +static int rte_on_close(dxr3_driver_t *drv); + +/* helper function */ +static void mp1e_callback(rte_context *context, void *data, ssize_t size, + void *user_data); + +/* encoder structure */ +typedef struct rte_data_s { + encoder_data_t encoder_data; + rte_context *context; /* handle for encoding */ + int width, height; + void *rte_ptr; /* buffer maintened by librte */ + double rte_bitrate; /* mpeg out bitrate, default 2.3e6 bits/s */ +} rte_data_t; +#endif + +#ifdef HAVE_LIBFAME +/* initialization function */ +int dxr3_fame_init(dxr3_driver_t *drv); + +/* functions required by encoder api */ +static int fame_on_update_format(dxr3_driver_t *drv, dxr3_frame_t *frame); +static int fame_on_display_frame(dxr3_driver_t *drv, dxr3_frame_t *frame); +static int fame_on_close(dxr3_driver_t *drv); + +/* encoder structure */ +typedef struct { + encoder_data_t encoder_data; + fame_context_t *context; /* needed for fame calls */ + fame_parameters_t fp; + fame_yuv_t yuv; + char *buffer; /* temporary buffer for mpeg data */ + /* temporary buffer for YUY2->YV12 conversion */ + uint8_t *out[3]; /* aligned buffer for YV12 data */ + uint8_t *buf; /* unaligned YV12 buffer */ +} fame_data_t; + +/* helper function */ +static int fame_prepare_frame(fame_data_t *this, dxr3_driver_t *drv, + dxr3_frame_t *frame); +#endif + + +#ifdef HAVE_LIBRTE +int dxr3_rte_init(dxr3_driver_t *drv) +{ + rte_data_t* this; + + if (!rte_init()) { + printf("dxr3_mpeg_encoder: failed to init librte\n"); + return 0; + } + + this = malloc(sizeof(rte_data_t)); + if (!this) return 0; + memset(this, 0, sizeof(rte_data_t)); + + this->encoder_data.type = ENC_RTE; + this->encoder_data.on_update_format = rte_on_update_format; + this->encoder_data.on_frame_copy = NULL; + this->encoder_data.on_display_frame = rte_on_display_frame; + this->encoder_data.on_close = rte_on_close; + this->context = 0; + + drv->enc = &this->encoder_data; + return 1; +} + +static int rte_on_update_format(dxr3_driver_t *drv, dxr3_frame_t *frame) +{ + rte_data_t *this = (rte_data_t *)drv->enc; + rte_context *context; + rte_codec *codec; + double fps; + + if (this->context) { /* already running */ +#if LOG_ENC + printf("dxr3_mpeg_encoder: closing current encoding context.\n"); +#endif + rte_stop(this->context); + rte_context_destroy(this->context); + this->context = 0; + } + + this->width = drv->video_width; + this->height = drv->video_oheight; + + /* create new rte context */ + this->context = rte_context_new(this->width, this->height, "mp1e", drv); + if (!this->context) { + printf("dxr3_mpeg_encoder: failed to get rte context.\n"); + return 0; + } + context = this->context; /* shortcut */ +#if LOG_ENC + rte_set_verbosity(context, 2); +#endif + + /* get mpeg codec handle */ + codec = rte_codec_set(context, RTE_STREAM_VIDEO, 0, "mpeg1_video"); + if (!codec) { + printf("dxr3_mpeg_encoder: could not create codec.\n"); + rte_context_destroy(context); + this->context = 0; + return 0; + } + + this->rte_bitrate = drv->config->register_range(drv->config, + "dxr3.rte_bitrate", 10000, 1000, 20000, + "Dxr3enc: rte mpeg output bitrate (kbit/s)", + "The bitrate the mpeg encoder library librte should use for dxr3's encoding mode", + 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 + */ + fps = 90000.0 / frame->vo_frame.duration; + if (!rte_option_set(codec, "virtual_frame_rate", fps)) + printf("dxr3_mpeg_encoder: WARNING: rte_option_set failed; virtual_frame_rate = %g.\n", fps); + if (!rte_option_set(codec, "coded_frame_rate", fps)) + printf("dxr3_mpeg_encoder: WARNING: rte_option_set failed; coded_frame_rate = %g.\n", fps); + if (!rte_option_set(codec, "bit_rate", (int)this->rte_bitrate)) + printf("dxr3_mpeg_encoder: WARNING: rte_option_set failed; bit_rate = %d.\n", (int)this->rte_bitrate); + if (!rte_option_set(codec, "gop_sequence", "I")) + printf("dxr3_mpeg_encoder: 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_mpeg_encoder: WARNING: rte_option_set failed; motion_compensation = 0.\n"); + + rte_set_input(context, RTE_VIDEO, RTE_PUSH, FALSE, NULL, NULL, NULL); + rte_set_output(context, mp1e_callback, NULL, NULL); + + if (!rte_init_context(context)) { + printf("dxr3_mpeg_encoder: cannot init the context: %s\n", context->error); + rte_context_destroy(context); + this->context = 0; + return 0; + } + /* do the sync'ing and start encoding */ + if (!rte_start_encoding(context)) { + printf("dxr3_mpeg_encoder: cannot start encoding: %s\n", context->error); + rte_context_destroy(context); + this->context = 0; + return 0; + } + this->rte_ptr = rte_push_video_data(context, NULL, 0); + if (!this->rte_ptr) { + printf("dxr3_mpeg_encoder: failed to get encoder buffer pointer.\n"); + return 0; + } + + /* set the dxr3 playmode */ + if (drv->enhanced_mode) { + em8300_register_t regs; + regs.microcode_register = 1; + regs.reg = 0; + regs.val = MVCOMMAND_SYNC; + ioctl(drv->fd_control, EM8300_IOCTL_WRITEREG, ®s); + } + + return 1; +} + +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; + + if ((this->width != frame->width) || (this->height != frame->oheight)) + /* maybe we were reinitialized and get an old frame. */ + return 1; + + size = frame->width * frame->oheight; + if (frame->vo_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_ptr = rte_push_video_data(this->context, this->rte_ptr, + frame->vo_frame.vpts / 90000.0); + + frame->vo_frame.displayed(&frame->vo_frame); + return 1; +} + +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_destroy(this->context); + } + free(this); + drv->enc = 0; + return 1; +} + + +static void mp1e_callback(rte_context *context, void *data, ssize_t size, void *user_data) +{ + dxr3_driver_t *drv = (dxr3_driver_t *)user_data; + char tmpstr[128]; + ssize_t written; + + if (drv->fd_video == CLOSED_FOR_ENCODER) { + snprintf(tmpstr, sizeof(tmpstr), "%s_mv%s", drv->devname, drv->devnum); + drv->fd_video = open(tmpstr, O_WRONLY | O_NONBLOCK); + } + if (drv->fd_video < 0) return; + written = write(drv->fd_video, data, size); + if (written < 0) { + printf("dxr3_mpeg_encoder: video device write failed (%s)\n", + strerror(errno)); + return; + } + if (written != size) + printf("dxr3_mpeg_encoder: Could only write %d of %d mpeg bytes.\n", + written, size); +} +#endif + + +#ifdef HAVE_LIBFAME +int dxr3_fame_init(dxr3_driver_t *drv) +{ + fame_data_t *this; + + this = malloc(sizeof(fame_data_t)); + if (!this) return 0; + memset(this, 0, sizeof(fame_data_t)); + + this->encoder_data.type = ENC_FAME; + this->encoder_data.on_update_format = fame_on_update_format; + this->encoder_data.on_frame_copy = NULL; + this->encoder_data.on_display_frame = fame_on_display_frame; + this->encoder_data.on_close = fame_on_close; + this->context = 0; + + drv->enc = &this->encoder_data; + return 1; +} + +static int fame_on_update_format(dxr3_driver_t *drv, dxr3_frame_t *frame) +{ + fame_data_t *this = (fame_data_t *)drv->enc; + fame_parameters_t init_fp = FAME_PARAMETERS_INITIALIZER; + double fps; + + if (this->buf) free(this->buf); + this->buf = 0; + this->out[0] = this->out[1] = this->out[2] = 0; + + /* if YUY2 and dimensions changed, we need to re-allocate the + * internal YV12 buffer */ + if (drv->format == IMGFMT_YUY2) { + int image_size = drv->video_width * drv->video_oheight; + + this->out[0] = xine_xmalloc_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); +#if LOG_ENC + printf("dxr3_mpeg_encoder: Using YUY2->YV12 conversion\n"); +#endif + } + + if (this->context) { +#if LOG_ENC + printf("dxr3_mpeg_encoder: closing current encoding context.\n"); +#endif + fame_close(this->context); + this->context = 0; + } + + this->context = fame_open(); + if (!this->context) { + printf("dxr3_mpeg_encoder: Couldn't start the FAME library\n"); + return 0; + } + + if (!this->buffer) + this->buffer = (unsigned char *)malloc(DEFAULT_BUFFER_SIZE); + if (!this->buffer) { + printf("dxr3_mpeg_encoder: Couldn't allocate temp buffer for mpeg data\n"); + return 0; + } + + this->fp = init_fp; + this->fp.quality = drv->config->register_range(drv->config, + "dxr3.fame_quality", 90, 10, 100, + "Dxr3enc: fame mpeg encoding quality", + "The encoding quality of the libfame mpeg encoder library.", + NULL,NULL); +#if LOG_ENC + /* 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_mpeg_encoder: quality %d -> quant scale = %d\n", this->fp.quality, + 1 + (30 * (100 - this->fp.quality) + 50) / 100); +#endif + this->fp.width = drv->video_width; + this->fp.height = drv->video_oheight; + this->fp.profile = "mpeg1"; + this->fp.coding = "I"; +#if LOG_ENC + this->fp.verbose = 1; +#else + this->fp.verbose = 0; +#endif + + /* start guessing the framerate */ + fps = 90000.0 / frame->vo_frame.duration; + if (fabs(fps - 25) < 0.01) { /* PAL */ +#if LOG_ENC + printf("dxr3_mpeg_encoder: setting mpeg output framerate to PAL (25 Hz)\n"); +#endif + this->fp.frame_rate_num = 25; + this->fp.frame_rate_den = 1; + } + else if (fabs(fps - 24) < 0.01) { /* FILM */ +#if LOG_ENC + printf("dxr3_mpeg_encoder: setting mpeg output framerate to FILM (24 Hz)\n"); +#endif + this->fp.frame_rate_num = 24; + this->fp.frame_rate_den = 1; + } + else if (fabs(fps - 23.976) < 0.01) { /* NTSC-FILM */ +#if LOG_ENC + printf("dxr3_mpeg_encoder: setting mpeg output framerate to NTSC-FILM (23.976 Hz)\n"); +#endif + this->fp.frame_rate_num = 24000; + this->fp.frame_rate_den = 1001; + } + else if (fabs(fps - 29.97) < 0.01) { /* NTSC */ +#if LOG_ENC + printf("dxr3_mpeg_encoder: setting mpeg output framerate to NTSC (29.97 Hz)\n"); +#endif + 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; +#if LOG_ENC + printf("dxr3_mpeg_encoder: trying to set mpeg output framerate to %d Hz\n", + this->fp.frame_rate_num); +#endif + } + + fame_init (this->context, &this->fp, this->buffer, DEFAULT_BUFFER_SIZE); + + /* set the dxr3 playmode */ + if (drv->enhanced_mode) { + em8300_register_t regs; + regs.microcode_register = 1; + regs.reg = 0; + regs.val = MVCOMMAND_SYNC; + ioctl(drv->fd_control, EM8300_IOCTL_WRITEREG, ®s); + } + + return 1; +} + +static int fame_on_display_frame(dxr3_driver_t *drv, dxr3_frame_t *frame) +{ + fame_data_t *this = (fame_data_t *)drv->enc; + char tmpstr[128]; + ssize_t written; + int size; + + if ((frame->width != this->fp.width) || (frame->oheight != this->fp.height)) + /* probably an old frame for a previous context. ignore it */ + return 1; + + fame_prepare_frame(this, drv, frame); + size = fame_encode_frame(this->context, &this->yuv, NULL); + frame->vo_frame.displayed(&frame->vo_frame); + + if (drv->fd_video == CLOSED_FOR_ENCODER) { + snprintf (tmpstr, sizeof(tmpstr), "%s_mv%s", drv->devname, drv->devnum); + drv->fd_video = open(tmpstr, O_WRONLY | O_NONBLOCK); + } + if (drv->fd_video < 0) return 0; + written = write(drv->fd_video, this->buffer, size); + if (written < 0) { + printf("dxr3_mpeg_encoder: video device write failed (%s)\n", + strerror(errno)); + return 0; + } + if (written != size) + printf("dxr3_mpeg_encoder: Could only write %d of %d mpeg bytes.\n", + written, size); + return 1; +} + +static int fame_on_close(dxr3_driver_t *drv) +{ + fame_data_t *this = (fame_data_t *)drv->enc; + + if (this->context) + fame_close(this->context); + free(this); + drv->enc = 0; + return 1; +} + + +static int fame_prepare_frame(fame_data_t *this, dxr3_driver_t *drv, dxr3_frame_t *frame) +{ + int i, j, w2; + uint8_t *y, *u, *v, *yuy2; + + if (frame->vo_frame.bad_frame) 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_mpeg_encoder: Internal YV12 buffer not created.\n"); + return 0; + } + y = this->out[0] + frame->width * drv->top_bar; + u = this->out[1] + frame->width/2 * (drv->top_bar / 2); + v = this->out[2] + frame->width/2 * (drv->top_bar / 2); + yuy2 = frame->vo_frame.base[0]; + w2 = frame->width / 2; + for (i = 0; i < frame->iheight; 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]; + } + + this->yuv.y = y; + this->yuv.u = u; + this->yuv.v = v; + return 1; +} +#endif |