File:  [DVB] / libsoftmpeg / src / v_directfb.c
Revision 1.5: download - view: text, annotated - select for diffs
Wed Mar 17 19:14:00 2004 UTC (20 years, 3 months ago) by hunold
Branches: MAIN
CVS tags: HEAD
- remove all references to av_frame, put data into new video_frame structure
- remove software image conversion stuff, no hw support, no libsoftmpeg 8-)
- clean up video_prepare() function, remove libavcodec specific stuff

/*
   (c) Copyright 2004  convergence GmbH

   All rights reserved.

   Written by Michael Hunold <hunold@convergence.de> and
	      Denis Oliver Kropp <dok@directfb.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#include "video.h"

static struct v_directfb_data
{
	IDirectFB *DirectFB;
	IDirectFBDisplayLayer *layer;
	IDirectFBSurface *window_surface;
	IDirectFBSurface *video_surface;
	DFBSurfacePixelFormat format;
	unsigned int width;
	unsigned int height;
	int use_layer;
	int field;
} priv;

/* fixme: belongs to DirectFB */
#include <linux/fb.h>
#include <sys/ioctl.h>
static int get_refresh_rates(int *v)
{
	int fh;
	struct fb_var_screeninfo si;
	double drate;
	double hrate;
	double vrate;
	long htotal;
	long vtotal;

	fh = open("/dev/fb0", O_RDONLY);
	if (-1 == fh) {
		return -1;
	}

	if (ioctl(fh, FBIOGET_VSCREENINFO, &si)) {
		close(fh);
		return -1;
	}

	htotal = si.left_margin + si.xres + si.right_margin + si.hsync_len;
	vtotal = si.upper_margin + si.yres + si.lower_margin + si.vsync_len;

	switch (si.vmode & FB_VMODE_MASK) {
	case FB_VMODE_INTERLACED:
		break;
	case FB_VMODE_DOUBLE:
		vtotal <<= 2;
		break;
	default:
		vtotal <<= 1;
		break;
	}

	drate = 1E12 / si.pixclock;
	hrate = drate / htotal;
	vrate = hrate / vtotal * 2;

	*v = 1E3 / vrate;
	/* h = hrate / 1E3; */
	
	close(fh);
	return 0;
}

static int init (struct video_decoder *d, void *init_data, int *v_dura)
{
	struct v_directfb_data *data = &priv;
	struct softmpeg_directfb_initdata *init = (struct softmpeg_directfb_initdata*)init_data;
	DFBDisplayLayerID layerid;
	
	DFBDisplayLayerConfig dlc;
	DFBDisplayLayerDescription dldesc;

	if (init->layer_id != -1) {
		layerid = init->layer_id;
	} else {
		layerid = DLID_PRIMARY;
	}

	SOFTMPEG_DEBUG("setting up DirectFB: layer_id:%d window_id:%d\n", layerid, init->window_id);

	if (0 != get_refresh_rates(v_dura)) {
		VIDEO_OUTPUT("cannot calculate vertical refresh rate\n");
		return -1;
	}
	
	DirectFBInit(0, NULL);

	if (DFB_OK != DirectFBCreate(&data->DirectFB)) {
		VIDEO_OUTPUT("DirectFB master interface not available..\n");
		return -1;
	}

	if (data->DirectFB->GetDisplayLayer(data->DirectFB, layerid, &data->layer) != DFB_OK) {
		VIDEO_OUTPUT("GetDisplayLayer() failed.\n");
		return -1;
	}

	if (init->window_id != -1) {
		IDirectFBWindow *dfb_window;

		if (DFB_OK != data->layer->SetCooperativeLevel(data->layer, DLSCL_ADMINISTRATIVE)) {
			VIDEO_OUTPUT("SetCooperativeLevel() failed.\n");
			return -1;
		}

		if (data->layer->GetWindow(data->layer, init->window_id, &dfb_window) != DFB_OK) {
			VIDEO_OUTPUT("cannot get window from window_id\n");
			return -1;
		}

		dfb_window->GetSurface(dfb_window, &data->window_surface);
		dfb_window->Release(dfb_window);

		data->layer->Release(data->layer);

		if (data->window_surface == NULL) {
			VIDEO_OUTPUT("window does not have a surface!?\n");
			return -1;
		}

		data->window_surface->GetPixelFormat(data->window_surface, &data->format);

		/*   Test if blits from DSPF_I420 are hardware accelerated;
		 *   we can avoid the software format conversion then.
		 */
		if (data->format != DSPF_I420) {
			IDirectFBSurface *dest = data->window_surface;
			IDirectFBSurface *src;
			DFBSurfaceDescription desc;
			DFBAccelerationMask acc;

			desc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT;
			desc.width = 32;
			desc.height = 32;
			desc.pixelformat = DSPF_UYVY;

			if (data->DirectFB->CreateSurface(data->DirectFB, &desc, &src) == DFB_OK) {
				dest->GetAccelerationMask(dest, src, &acc);

				if (acc & DFXL_STRETCHBLIT) {
					VIDEO_OUTPUT("Looks like our gfx hardware can stretchblit from UYVY to the video window :-)\n");
					data->format = DSPF_UYVY;
				}

				src->Release(src);
			} else {
				VIDEO_OUTPUT("Arg, no hw stretchblit available.\n");
			}
		}

		switch (data->format) {
			case DSPF_UYVY:
			case DSPF_I420:
			case DSPF_YV12:
				break;
			default: {
				SOFTMPEG_DEBUG("unsupported pixelformat for the destination surface, no hw acceleration available.");
				return -1;
			}
		}

		data->use_layer = 0;
		SOFTMPEG_DEBUG("using non-layer configuration for video output via DirectFB\n");
		return 0;
	}

	if (DFB_OK != data->layer->SetCooperativeLevel(data->layer, DLSCL_ADMINISTRATIVE)) {
		VIDEO_OUTPUT("SetCooperativeLevel() failed.\n");
		return -1;
	}

	data->layer->GetDescription(data->layer, &dldesc);

	/* check for usable layer */

	dlc.flags = DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_PIXELFORMAT | DLCONF_BUFFERMODE | DLCONF_OPTIONS;
	dlc.width = init->width;
	dlc.height = init->height;
	dlc.pixelformat = DSPF_YV12;
	dlc.buffermode = DLBM_BACKVIDEO;
	dlc.options = (dldesc.caps & DLCAPS_DEINTERLACING) ? DLOP_DEINTERLACING : DLOP_NONE;

	if (DFB_OK != data->layer->SetConfiguration(data->layer, &dlc)) {
		VIDEO_OUTPUT("SetConfiguration() failed.\n");
		return -1;
	}

	data->layer->SetScreenLocation(data->layer, 0.0f, 0.0f, 1.0f, 1.0f);
	data->layer->GetSurface(data->layer, &data->video_surface);
	data->format = DSPF_YV12;

	SOFTMPEG_DEBUG("using layer configuration for video output via DirectFB\n");
	data->use_layer = 1;

	return 0;
}

static int prepare (struct video_decoder *d, struct video_frame *frame)
{
	struct v_directfb_data *data = &priv;
	DFBRectangle rect;
	int pitch;
	void *dst;
	
	data->video_surface->Lock(data->video_surface, DSLF_WRITE, &dst, &pitch);
	
	switch (data->format) {
		case DSPF_I420: {
		int i;

		for (i = 0; i < data->height; i++)
			dfb_memcpy(dst + i * pitch, frame->data[0] + i * frame->linesize[0], pitch);

		dst += pitch * data->height;

		for (i = 0; i < data->height / 2; i++)
			dfb_memcpy(dst + i * pitch / 2, frame->data[1] + i * frame->linesize[1], pitch / 2);

		dst += (pitch * data->height) / 4;

		for (i = 0; i < data->height / 2; i++)
			dfb_memcpy(dst + i * pitch / 2, frame->data[2] + i * frame->linesize[2], pitch / 2);
		break;
		}
		case DSPF_YV12: {
		int i;

		for (i = 0; i < data->height; i++)
			dfb_memcpy(dst + i * pitch, frame->data[0] + i * frame->linesize[0], pitch);

		dst += pitch * data->height;

		for (i = 0; i < data->height / 2; i++)
			dfb_memcpy(dst + i * (pitch / 2), frame->data[2] + i * frame->linesize[2], pitch / 2);

		dst += (pitch / 2) * (data->height / 2);

		for (i = 0; i < data->height / 2; i++)
			dfb_memcpy(dst + i * (pitch / 2), frame->data[1] + i * frame->linesize[1], pitch / 2);
		break;
		}
		case DSPF_UYVY: {
		int y, x;

		uint8_t *src_y = frame->data[0];
		uint8_t *src_u = frame->data[1];
		uint8_t *src_v = frame->data[2];
		uint16_t *dst16 = dst;

		for (y = 0; y < data->height; y++) {
			for (x = 0; x < data->width; x++) {
				if (x & 1)
					dst16[x] = (src_y[x] << 8) | src_v[x / 2];
				else
					dst16[x] = (src_y[x] << 8) | src_u[x / 2];
			}

			src_y += frame->linesize[0];

			if (y & 1) {
				src_u += frame->linesize[1];
				src_v += frame->linesize[2];
			}

			dst16 += pitch / 2;
		}
		break;
		}
		default: {
			VIDEO_OUTPUT("illegal surface format\n");
			break;
		}
	}
	
	data->video_surface->Unlock(data->video_surface);
		
	/* flip backbuffer if necessary */

	if (data->use_layer != 0) {
		return 0;
	}

	/* copy stuff to the surface's backbuffer, but don't flip it */
	rect.x = 0;
	rect.y = 0;
	data->window_surface->GetSize(data->window_surface, &rect.w, &rect.h);
	data->window_surface->StretchBlit(data->window_surface, data->video_surface, NULL, &rect);

	return 0;
}

static int resize (struct video_decoder *d, unsigned int width, unsigned int height)
{
	struct v_directfb_data *data = &priv;
	DFBSurfaceDescription desc;
	int ret;

	data->width = width;
	data->height = height;

	if (data->use_layer != 0) {
		DFBDisplayLayerConfig dlc;

		SOFTMPEG_DEBUG("changing layer configuration, w:%d, h:%d\n",width,height);

		dlc.flags = DLCONF_WIDTH | DLCONF_HEIGHT;
		dlc.width = width;
		dlc.height = height;

		data->layer->SetConfiguration(data->layer, &dlc);
		data->layer->SetScreenLocation(data->layer, 0.0f, 0.0f, 1.0f, 1.0f);

		return 0;
	}

	SOFTMPEG_DEBUG("changing window configuration, w:%d, h:%d\n",width,height);

	if (data->video_surface) {
		data->video_surface->Release(data->video_surface);
		data->video_surface = NULL;
	}

	desc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT | DSDESC_CAPS;
	/* fixme: possible bug in DirectFB (or double lock?) -- check and remove me */
	desc.caps = DSCAPS_VIDEOONLY;
	desc.width = width;
	desc.height = height;
	desc.pixelformat = data->format;

	ret = data->DirectFB->CreateSurface(data->DirectFB, &desc, &data->video_surface);
	return ret;
}

static int clear (struct video_decoder *d)
{
	struct v_directfb_data *data = &priv;

	if (data->use_layer != 0) {
		data->video_surface->Clear(data->video_surface,0,0,0,0);
		data->video_surface->Flip(data->video_surface, NULL, 0);
		return 0;
	}

	data->window_surface->Clear(data->window_surface,0,0,0,0);
	data->window_surface->Flip(data->window_surface, NULL, 0);

	return 0;
}

static int wait_for_sync (struct video_decoder *d)
{
	struct v_directfb_data *data = &priv;

	data->DirectFB->WaitForSync(data->DirectFB);

	return 0;
}

static int flip (struct video_decoder *d)
{
	struct v_directfb_data *data = &priv;
		
	if (data->use_layer != 0) {
		long long t = softmpeg_get_millis();
		long long diff;
		
		data->video_surface->Flip(data->video_surface, NULL, DSFLIP_ONSYNC);

		data->DirectFB->WaitForSync(data->DirectFB);

		if ((softmpeg_get_millis() - t) < 3) {
			VIDEO_AVSYNC("*");
			data->DirectFB->WaitForSync(data->DirectFB);
		} else {
			VIDEO_AVSYNC(" ");
		}

		data->video_surface->SetField(data->video_surface, 1);

		diff = softmpeg_get_millis() - t;
		VIDEO_AVSYNC("[%2lld] ", diff);

		return 0;
	}

	data->window_surface->Flip(data->window_surface, NULL, DSFLIP_WAITFORSYNC);
	return 0;
}

struct video_operations v_directfb_ops = 
{
	.init 		= &init,
	.resize 	= &resize,
	.clear		= &clear,
	.prepare	= &prepare,
	.wait_for_sync	= &wait_for_sync,
	.flip		= &flip,
	.priv		= &priv,
};


LinuxTV legacy CVS <linuxtv.org/cvs>