diff options
Diffstat (limited to 'src/libreal/xine_real_video_decoder.c')
-rw-r--r-- | src/libreal/xine_real_video_decoder.c | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/src/libreal/xine_real_video_decoder.c b/src/libreal/xine_real_video_decoder.c new file mode 100644 index 000000000..ea1fc8c54 --- /dev/null +++ b/src/libreal/xine_real_video_decoder.c @@ -0,0 +1,557 @@ +/* + * Copyright (C) 2000-2004 the xine project + * + * This file is part of xine, a free 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: xine_decoder.c,v 1.93 2007/03/17 15:45:41 dgp85 Exp $ + * + * thin layer to use real binary-only codecs in xine + * + * code inspired by work from Florian Schneider for the MPlayer Project + */ + + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <dlfcn.h> + +#define LOG_MODULE "real_decoder" +#define LOG_VERBOSE +/* +#define LOG +*/ +#include "bswap.h" +#include "xine_internal.h" +#include "video_out.h" +#include "buffer.h" +#include "xineutils.h" + +#include "real_common.h" + +typedef struct { + video_decoder_class_t decoder_class; + + /* empty so far */ +} real_class_t; + +#define BUF_SIZE 65536 + +typedef struct realdec_decoder_s { + video_decoder_t video_decoder; + + real_class_t *cls; + + xine_stream_t *stream; + + void *rv_handle; + + uint32_t (*rvyuv_custom_message)(void*, void*); + uint32_t (*rvyuv_free)(void*); + uint32_t (*rvyuv_hive_message)(uint32_t, uint32_t); + uint32_t (*rvyuv_init)(void*, void*); /* initdata,context */ + uint32_t (*rvyuv_transform)(char*, char*, void*, uint32_t*,void*); + + void *context; + + uint32_t width, height; + double ratio; + double fps; + + uint8_t *chunk_buffer; + int chunk_buffer_size; + int chunk_buffer_max; + + int64_t pts; + int duration; + + uint8_t *frame_buffer; + int frame_size; + int decoder_ok; + +} realdec_decoder_t; + +/* we need exact positions */ +typedef struct { + int16_t unk1; + int16_t w; + int16_t h; + int16_t unk3; + int32_t unk2; + int32_t subformat; + int32_t unk5; + int32_t format; +} rv_init_t; + +/* + * Structures for data packets. These used to be tables of unsigned ints, but + * that does not work on 64 bit platforms (e.g. Alpha). The entries that are + * pointers get truncated. Pointers on 64 bit platforms are 8 byte longs. + * So we have to use structures so the compiler will assign the proper space + * for the pointer. + */ +typedef struct cmsg_data_s { + uint32_t data1; + uint32_t data2; + uint32_t* dimensions; +} cmsg_data_t; + +typedef struct transform_in_s { + uint32_t len; + uint32_t unknown1; + uint32_t chunks; + uint32_t* extra; + uint32_t unknown2; + uint32_t timestamp; +} transform_in_t; + +/* + * real codec loader + */ + +static int load_syms_linux (realdec_decoder_t *this, const char *codec_name, const char *const codec_alternate) { + cfg_entry_t* entry = + this->stream->xine->config->lookup_entry(this->stream->xine->config, + "decoder.external.real_codecs_path"); + + if ( (this->rv_handle = _x_real_codec_open(this->stream, entry->str_value, codec_name, codec_alternate)) == NULL ) + return 0; + + this->rvyuv_custom_message = dlsym (this->rv_handle, "RV20toYUV420CustomMessage"); + this->rvyuv_free = dlsym (this->rv_handle, "RV20toYUV420Free"); + this->rvyuv_hive_message = dlsym (this->rv_handle, "RV20toYUV420HiveMessage"); + this->rvyuv_init = dlsym (this->rv_handle, "RV20toYUV420Init"); + this->rvyuv_transform = dlsym (this->rv_handle, "RV20toYUV420Transform"); + + if (this->rvyuv_custom_message && + this->rvyuv_free && + this->rvyuv_hive_message && + this->rvyuv_init && + this->rvyuv_transform) + return 1; + + this->rvyuv_custom_message = dlsym (this->rv_handle, "RV40toYUV420CustomMessage"); + this->rvyuv_free = dlsym (this->rv_handle, "RV40toYUV420Free"); + this->rvyuv_hive_message = dlsym (this->rv_handle, "RV40toYUV420HiveMessage"); + this->rvyuv_init = dlsym (this->rv_handle, "RV40toYUV420Init"); + this->rvyuv_transform = dlsym (this->rv_handle, "RV40toYUV420Transform"); + + if (this->rvyuv_custom_message && + this->rvyuv_free && + this->rvyuv_hive_message && + this->rvyuv_init && + this->rvyuv_transform) + return 1; + + xprintf (this->stream->xine, XINE_VERBOSITY_LOG, + _("libreal: Error resolving symbols! (version incompatibility?)\n")); + return 0; +} + +static int init_codec (realdec_decoder_t *this, buf_element_t *buf) { + + /* unsigned int* extrahdr = (unsigned int*) (buf->content+28); */ + int result; + rv_init_t init_data = {11, 0, 0, 0, 0, 0, 1, 0}; /* rv30 */ + + + switch (buf->type) { + case BUF_VIDEO_RV20: + _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "Real Video 2.0"); + if (!load_syms_linux (this, "drv2.so", "drv2.so.6.0")) + return 0; + break; + case BUF_VIDEO_RV30: + _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "Real Video 3.0"); + if (!load_syms_linux (this, "drvc.so", "drv3.so.6.0")) + return 0; + break; + case BUF_VIDEO_RV40: + _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "Real Video 4.0"); + if (!load_syms_linux(this, "drvc.so", "drv3.so.6.0")) + return 0; + break; + default: + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + "libreal: error, i don't handle buf type 0x%08x\n", buf->type); + _x_abort(); + } + + init_data.w = BE_16(&buf->content[12]); + init_data.h = BE_16(&buf->content[14]); + + this->width = (init_data.w + 1) & (~1); + this->height = (init_data.h + 1) & (~1); + + if(buf->decoder_flags & BUF_FLAG_ASPECT) + this->ratio = (double)buf->decoder_info[1] / (double)buf->decoder_info[2]; + else + this->ratio = (double)this->width / (double)this->height; + + /* While the framerate is stored in the header it sometimes doesn't bear + * much resemblence to the actual frequency of frames in the file. Hence + * it's better to just let the engine estimate the frame duration for us */ +#if 0 + this->fps = (double) BE_16(&buf->content[22]) + + ((double) BE_16(&buf->content[24]) / 65536.0); + this->duration = 90000.0 / this->fps; +#endif + + lprintf("this->ratio=%f\n", this->ratio); + + lprintf ("init_data.w=%d(0x%x), init_data.h=%d(0x%x)," + "this->width=%d(0x%x), this->height=%d(0x%x)\n", + init_data.w, init_data.w, + init_data.h, init_data.h, + this->width, this->width, this->height, this->height); + + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, this->width); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, this->height); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_RATIO, this->ratio*10000); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, this->duration); + + init_data.subformat = BE_32(&buf->content[26]); + init_data.format = BE_32(&buf->content[30]); + +#ifdef LOG + printf ("libreal: init_data for rvyuv_init:\n"); + xine_hexdump ((char *) &init_data, sizeof (init_data)); + + printf ("libreal: buf->content\n"); + xine_hexdump (buf->content, buf->size); +#endif + lprintf ("init codec %dx%d... %x %x\n", + init_data.w, init_data.h, + init_data.subformat, init_data.format ); + + this->context = NULL; + + result = this->rvyuv_init (&init_data, &this->context); + + lprintf ("init result: %d\n", result); + + /* setup rv30 codec (codec sub-type and image dimensions): */ + if ((init_data.format>=0x20200002) && (buf->type != BUF_VIDEO_RV40)) { + int i, j; + uint32_t cmsg24[(buf->size - 34 + 2) * sizeof(uint32_t)]; + cmsg_data_t cmsg_data = { 0x24, 1 + ((init_data.subformat >> 16) & 7), &cmsg24[0] }; + + cmsg24[0] = this->width; + cmsg24[1] = this->height; + for(i = 2, j = 34; j < buf->size; i++, j++) + cmsg24[i] = 4 * buf->content[j]; + +#ifdef LOG + printf ("libreal: CustomMessage cmsg_data:\n"); + xine_hexdump ((uint8_t *) cmsg_data, sizeof (cmsg_data)); + printf ("libreal: cmsg24:\n"); + xine_hexdump ((uint8_t *) cmsg24, (buf->size - 34 + 2) * sizeof(uint32_t)); +#endif + + this->rvyuv_custom_message (&cmsg_data, this->context); + } + + this->stream->video_out->open(this->stream->video_out, this->stream); + + this->frame_size = this->width * this->height; + this->frame_buffer = xine_xmalloc (this->width * this->height * 3 / 2); + + this->chunk_buffer = xine_xmalloc (BUF_SIZE); + this->chunk_buffer_max = BUF_SIZE; + + return 1; +} + +static void realdec_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { + realdec_decoder_t *this = (realdec_decoder_t *) this_gen; + + lprintf ("decode_data, flags=0x%08x, len=%d, pts=%"PRId64" ...\n", + buf->decoder_flags, buf->size, buf->pts); + + if (buf->decoder_flags & BUF_FLAG_PREVIEW) { + /* real_find_sequence_header (&this->real, buf->content, buf->content + buf->size);*/ + return; + } + + if (buf->decoder_flags & BUF_FLAG_FRAMERATE) { + this->duration = buf->decoder_info[0]; + _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, + this->duration); + } + + if (buf->decoder_flags & BUF_FLAG_HEADER) { + + this->decoder_ok = init_codec (this, buf); + if( !this->decoder_ok ) + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HANDLED, 0); + + } else if (this->decoder_ok && this->context) { + + /* Each frame starts with BUF_FLAG_FRAME_START and ends with + * BUF_FLAG_FRAME_END. + * The last buffer contains the chunk offset table. + */ + + if (!(buf->decoder_flags & BUF_FLAG_SPECIAL)) { + + lprintf ("buffer (%d bytes)\n", buf->size); + + if (buf->decoder_flags & BUF_FLAG_FRAME_START) { + /* new frame starting */ + + this->chunk_buffer_size = 0; + this->pts = buf->pts; + lprintf ("new frame starting, pts=%"PRId64"\n", this->pts); + } + + if ((this->chunk_buffer_size + buf->size) > this->chunk_buffer_max) { + lprintf("increasing chunk buffer size\n"); + + this->chunk_buffer_max *= 2; + this->chunk_buffer = realloc(this->chunk_buffer, this->chunk_buffer_max); + } + + xine_fast_memcpy (this->chunk_buffer + this->chunk_buffer_size, + buf->content, + buf->size); + + this->chunk_buffer_size += buf->size; + + } else { + /* end of frame, chunk table */ + + lprintf ("special buffer (%d bytes)\n", buf->size); + + if (buf->decoder_info[1] == BUF_SPECIAL_RV_CHUNK_TABLE) { + + int result; + vo_frame_t *img; + + uint32_t transform_out[5]; + transform_in_t transform_in = { + this->chunk_buffer_size, + /* length of the packet (sub-packets appended) */ + 0, + /* unknown, seems to be unused */ + buf->decoder_info[2], + /* number of sub-packets - 1 */ + buf->decoder_info_ptr[2], + /* table of sub-packet offsets */ + 0, + /* unknown, seems to be unused */ + this->pts / 90 + /* timestamp (the integer value from the stream) */ + }; + + lprintf ("chunk table\n"); + + +#ifdef LOG + printf ("libreal: got %d chunks\n", + buf->decoder_info[2] + 1); + + printf ("libreal: decoding %d bytes:\n", this->chunk_buffer_size); + xine_hexdump (this->chunk_buffer, this->chunk_buffer_size); + + printf ("libreal: transform_in:\n"); + xine_hexdump ((uint8_t *) transform_in, 6 * 4); + + printf ("libreal: chunk_table:\n"); + xine_hexdump ((uint8_t *) buf->decoder_info_ptr[2], + 2*(buf->decoder_info[2]+1)*sizeof(uint32_t)); +#endif + + result = this->rvyuv_transform (this->chunk_buffer, + this->frame_buffer, + &transform_in, + transform_out, + this->context); + + lprintf ("transform result: %08x\n", result); + lprintf ("transform_out:\n"); + #ifdef LOG + xine_hexdump ((uint8_t *) transform_out, 5 * 4); + #endif + + /* Sometimes the stream contains video of a different size + * to that specified in the realmedia header */ + if(transform_out[0] && ((transform_out[3] != this->width) || + (transform_out[4] != this->height))) { + this->width = transform_out[3]; + this->height = transform_out[4]; + + this->frame_size = this->width * this->height; + + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, this->width); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, this->height); + } + + img = this->stream->video_out->get_frame (this->stream->video_out, + /* this->av_picture.linesize[0], */ + this->width, + this->height, + this->ratio, + XINE_IMGFMT_YV12, + VO_BOTH_FIELDS); + + img->pts = this->pts; + img->duration = this->duration; + _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, this->duration); + img->bad_frame = 0; + + yv12_to_yv12( + /* Y */ + this->frame_buffer, this->width, + img->base[0], img->pitches[0], + /* U */ + this->frame_buffer + this->frame_size, this->width/2, + img->base[1], img->pitches[1], + /* V */ + this->frame_buffer + this->frame_size * 5/4, this->width/2, + img->base[2], img->pitches[2], + /* width x height */ + this->width, this->height); + + img->draw(img, this->stream); + img->free(img); + + } else { + /* unsupported special buf */ + } + } + } + + lprintf ("decode_data...done\n"); +} + +static void realdec_flush (video_decoder_t *this_gen) { + /* realdec_decoder_t *this = (realdec_decoder_t *) this_gen; */ + + lprintf ("flush\n"); +} + +static void realdec_reset (video_decoder_t *this_gen) { + realdec_decoder_t *this = (realdec_decoder_t *) this_gen; + + this->chunk_buffer_size = 0; +} + +static void realdec_discontinuity (video_decoder_t *this_gen) { + realdec_decoder_t *this = (realdec_decoder_t *) this_gen; + + this->pts = 0; +} + +static void realdec_dispose (video_decoder_t *this_gen) { + + realdec_decoder_t *this = (realdec_decoder_t *) this_gen; + + lprintf ("dispose\n"); + + if (this->context) + this->stream->video_out->close(this->stream->video_out, this->stream); + + if (this->rvyuv_free && this->context) + this->rvyuv_free (this->context); + + if (this->rv_handle) + dlclose (this->rv_handle); + + if (this->frame_buffer) + free (this->frame_buffer); + + if (this->chunk_buffer) + free (this->chunk_buffer); + + free (this); + + lprintf ("dispose done\n"); +} + +static video_decoder_t *open_plugin (video_decoder_class_t *class_gen, + xine_stream_t *stream) { + + real_class_t *cls = (real_class_t *) class_gen; + realdec_decoder_t *this ; + + this = (realdec_decoder_t *) xine_xmalloc (sizeof (realdec_decoder_t)); + + this->video_decoder.decode_data = realdec_decode_data; + this->video_decoder.flush = realdec_flush; + this->video_decoder.reset = realdec_reset; + this->video_decoder.discontinuity = realdec_discontinuity; + this->video_decoder.dispose = realdec_dispose; + this->stream = stream; + this->cls = cls; + + this->context = 0; + this->pts = 0; + + this->duration = 0; + + return &this->video_decoder; +} + +/* + * real plugin class + */ + +static char *get_identifier (video_decoder_class_t *this) { + return "realvdec"; +} + +static char *get_description (video_decoder_class_t *this) { + return "real binary-only codec based video decoder plugin"; +} + +static void dispose_class (video_decoder_class_t *this) { + free (this); +} + +void *init_realvdec (xine_t *xine, void *data) { + + real_class_t *this; + config_values_t *config = xine->config; + + this = (real_class_t *) xine_xmalloc (sizeof (real_class_t)); + + this->decoder_class.open_plugin = open_plugin; + this->decoder_class.get_identifier = get_identifier; + this->decoder_class.get_description = get_description; + this->decoder_class.dispose = dispose_class; + + _x_real_codecs_init(xine); + + return this; +} + +/* + * exported plugin catalog entry + */ + +static uint32_t supported_types[] = { BUF_VIDEO_RV20, + BUF_VIDEO_RV30, + BUF_VIDEO_RV40, + 0 }; + +const decoder_info_t dec_info_realvideo = { + supported_types, /* supported types */ + 7 /* priority */ +}; |