/* 2004.02.01 first released source code for IOMP */ /* * Copyright (C) 2000-2003 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.2 2003/11/25 04:25:46 georgedon 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 # include #endif #ifdef HAVE_LIBFAME # include #endif #include #include #include #include #include #include #include #include #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_unneeded(dxr3_driver_t *drv); 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_unneeded(dxr3_driver_t *drv); 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 /* initialization function */ int dxr3_lavc_init(dxr3_driver_t *drv, plugin_node_t *node); /* close function from encoder api */ static int lavc_on_close(dxr3_driver_t *drv); #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_unneeded = rte_on_unneeded; 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; } if ((frame->vo_frame.pitches[0] % 16 != 0) || (frame->oheight % 16 != 0)) { printf("dxr3_mpeg_encoder: rte only handles video dimensions which are multiples of 16\n"); return 0; } this->width = frame->vo_frame.pitches[0]; this->height = frame->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->class->xine->config->register_range(drv->class->xine->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"), 10, 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, (frame->vo_frame.format == XINE_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->vo_frame.pitches[0]) && (this->height == frame->oheight)) { /* This frame belongs to current context. */ size = frame->vo_frame.pitches[0] * frame->oheight; if (frame->vo_frame.format == XINE_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.free(&frame->vo_frame); return 1; } static int rte_on_unneeded(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); this->context = 0; } return 1; } static int rte_on_close(dxr3_driver_t *drv) { rte_on_unneeded(drv); free(drv->enc); 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; 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_unneeded = fame_on_unneeded; 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 (frame->vo_frame.format == XINE_IMGFMT_YUY2) { int image_size = frame->vo_frame.width * frame->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->class->xine->config->register_range(drv->class->xine->config, "dxr3.fame_quality", 90, 10, 100, _("Dxr3enc: fame mpeg encoding quality"), _("The encoding quality of the libfame mpeg encoder library."), 10, 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 = frame->vo_frame.width; this->fp.height = frame->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->vo_frame.width != this->fp.width) || (frame->oheight != this->fp.height)) { /* probably an old frame for a previous context. ignore it */ frame->vo_frame.free(&frame->vo_frame); return 1; } fame_prepare_frame(this, drv, frame); #ifdef HAVE_NEW_LIBFAME fame_start_frame(this->context, &this->yuv, NULL); size = fame_encode_slice(this->context); fame_end_frame(this->context, NULL); #else size = fame_encode_frame(this->context, &this->yuv, NULL); #endif frame->vo_frame.free(&frame->vo_frame); 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_unneeded(dxr3_driver_t *drv) { fame_data_t *this = (fame_data_t *)drv->enc; if (this->context) { fame_close(this->context); this->context = 0; } return 1; } static int fame_on_close(dxr3_driver_t *drv) { fame_on_unneeded(drv); free(drv->enc); 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 == XINE_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->vo_frame.width * drv->top_bar; u = this->out[1] + (frame->vo_frame.width / 2) * (drv->top_bar / 2); v = this->out[2] + (frame->vo_frame.width / 2) * (drv->top_bar / 2); yuy2 = frame->vo_frame.base[0]; w2 = frame->vo_frame.width / 2; for (i = 0; i < frame->vo_frame.height; 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 int dxr3_lavc_init(dxr3_driver_t *drv, plugin_node_t *node) { void *ffmpeg; int (*init)(dxr3_driver_t *); int result; ffmpeg = dlopen(node->filename, RTLD_LAZY); if (!ffmpeg) return 0; init = dlsym(ffmpeg, "dxr3_encoder_init"); if (!init) return 0; result = init(drv); /* the close function is implemented here, because it will call dlclose() * and that should not be done be the library we are closing... */ drv->enc->on_close = lavc_on_close; drv->enc->handle = ffmpeg; return result; } static int lavc_on_close(dxr3_driver_t *drv) { drv->enc->on_unneeded(drv); dlclose(drv->enc->handle); free(drv->enc); drv->enc = NULL; return 1; }