Mercurial > mplayer.hg
view vidix/ivtv_vid.c @ 33671:a460339acfdf
Fix volume and balance bug.
Changing the volume changed the balance as well,
because the calculation for the balance was wrong.
Additionally, use macro FFMAX() and replace
identical code by a call to existing code.
author | ib |
---|---|
date | Tue, 28 Jun 2011 17:52:30 +0000 |
parents | 8fa2f43cb760 |
children | 945eab072c9d |
line wrap: on
line source
/* * VIDIX driver for Hauppauge PVR 350 * * Copyright 2007 Lutz Koschorreck * Based on genfb_vid.c and ivtv_xv.c * * This file is part of MPlayer. * * MPlayer 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. * * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <math.h> #include <inttypes.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/types.h> #include <linux/version.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) #include <linux/videodev2.h> #endif #include <linux/ivtv.h> #include <linux/fb.h> #include "vidix.h" #include "fourcc.h" #include "dha.h" #include "pci_ids.h" #include "pci_names.h" #define VIDIX_STATIC ivtv_ #define IVTV_MSG "[ivtv-vid] " #define MAXLINE 128 #define IVTVMAXWIDTH 720 #define IVTVMAXHEIGHT 576 static int fbdev = -1; static int yuvdev = -1; static void *memBase = NULL; static int frameSize = 0; static int probed = 0; static int ivtv_verbose; static vidix_rect_t destVideo; static vidix_rect_t srcVideo; static unsigned char *outbuf = NULL; double fb_width; double fb_height; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) static struct ivtvfb_ioctl_state_info fb_state_old; static struct ivtvfb_ioctl_state_info fb_state_hide; #else struct v4l2_format format_old, format_hide; #endif int alpha_disable = 0; /* VIDIX exports */ static vidix_capability_t ivtv_cap = { "Hauppauge PVR 350 YUV Video", "Lutz Koschorreck", TYPE_OUTPUT, { 0, 0, 0, 0 }, IVTVMAXWIDTH, IVTVMAXHEIGHT, 4, 4, -1, FLAG_UPSCALER|FLAG_DOWNSCALER, -1, -1, { 0, 0, 0, 0 } }; static void de_macro_y(unsigned char *src, unsigned char *dst, unsigned int w, unsigned int h, int src_x, int src_y, int height, int width) { unsigned int x, y, i; unsigned char *dst_2; unsigned int h_tail, w_tail; unsigned int h_size, w_size; // Always round the origin, but compensate by increasing the size if (src_x & 15) { w += src_x & 15; src_x &= ~15; } if (src_y & 15) { h += src_y & 15; src_y &= ~15; } // The right / bottom edge might not be a multiple of 16 h_tail = h & 15; w_tail = w & 15; // One block is 16 pixels high h_size = 16; // descramble Y plane for (y = 0; y < h; y += 16) { // Clip if we've reached the bottom & the size isn't a multiple of 16 if (y + 16 > h) h_size = h_tail; for (x = 0; x < w; x += 16) { if (x + 16 > w) w_size = w_tail; else w_size = 16; dst_2 = dst + (720 * y) + (720 * src_y) + (256 * (src_x>>4)) + (x * 16); for (i = 0; i < h_size; i++) { memcpy(dst_2, src + src_x + x + (y + i) * width + (src_y * width), w_size); dst_2 += 16; } } } } static void de_macro_uv(unsigned char *srcu, unsigned char *srcv, unsigned char *dst, unsigned int w, unsigned int h, int src_x, int src_y, int height, int width) { unsigned int x, y, i, f; unsigned char *dst_2; unsigned int h_tail, w_tail; unsigned int h_size; // The uv plane is half the size of the y plane, so 'correct' all dimensions. w /= 2; h /= 2; src_x /= 2; src_y /= 2; height /= 2; width /= 2; // Always round the origin, but compensate by increasing the size if (src_x & 7) { w += src_x & 7; src_x &= ~7; } if (src_y & 15) { h += src_y & 15; src_y &= ~15; } // The right / bottom edge may not be a multiple of 16 h_tail = h & 15; w_tail = w & 7; h_size = 16; // descramble U/V plane for (y = 0; y < h; y += 16) { if ( y + 16 > h ) h_size = h_tail; for (x = 0; x < w; x += 8) { dst_2 = dst + (720 * y) + (720 * src_y) + (256 * (src_x>>3)) + (x * 32); if (x + 8 <= w) { for (i = 0; i < h_size; i++) { int idx = src_x + x + ((y + i) * width) + (src_y * width); dst_2[0] = srcu[idx + 0]; dst_2[1] = srcv[idx + 0]; dst_2[2] = srcu[idx + 1]; dst_2[3] = srcv[idx + 1]; dst_2[4] = srcu[idx + 2]; dst_2[5] = srcv[idx + 2]; dst_2[6] = srcu[idx + 3]; dst_2[7] = srcv[idx + 3]; dst_2[8] = srcu[idx + 4]; dst_2[9] = srcv[idx + 4]; dst_2[10] = srcu[idx + 5]; dst_2[11] = srcv[idx + 5]; dst_2[12] = srcu[idx + 6]; dst_2[13] = srcv[idx + 6]; dst_2[14] = srcu[idx + 7]; dst_2[15] = srcv[idx + 7]; dst_2 += 16; } } else { for (i = 0; i < h_size; i ++) { int idx = src_x + x + ((y + i) * width) + (src_y * width); for (f = 0; f < w_tail; f++) { dst_2[0] = srcu[idx + f]; dst_2[1] = srcv[idx + f]; dst_2 += 2; } /* // Used for testing edge cutoff. Sets colour to Green for (f = w_tail;f < 8;f ++) { dst_2[0] = 0; dst_2[1] = 0; dst_2 += 2; } */ dst_2 += 16 - (w_tail << 1); } } } } } int ivtv_probe(int verbose, int force) { unsigned char fb_number = 0; char *device_name = NULL; char *alpha = NULL; struct fb_var_screeninfo vinfo; char fb_dev_name[] = "/dev/fb0\0"; pciinfo_t lst[MAX_PCI_DEVICES]; int err = 0; unsigned int i, num_pci = 0; unsigned char yuv_device_number = 48, yuv_device = 48 + fb_number; char yuv_device_name[] = "/dev/videoXXX\0"; if(verbose) printf(IVTV_MSG"probe\n"); ivtv_verbose = verbose; err = pci_scan(lst, &num_pci); if(err) { printf(IVTV_MSG"Error occured during pci scan: %s\n", strerror(err)); return err; } if(ivtv_verbose) printf(IVTV_MSG"Found %d pci devices\n", num_pci); for(i = 0; i < num_pci; i++) { if(2 == ivtv_verbose) printf(IVTV_MSG"Found chip [%04X:%04X] '%s' '%s'\n" ,lst[i].vendor ,lst[i].device ,pci_vendor_name(lst[i].vendor) ,pci_device_name(lst[i].vendor,lst[i].device)); if(VENDOR_INTERNEXT == lst[i].vendor) { switch(lst[i].device) { case DEVICE_INTERNEXT_ITVC15_MPEG_2_ENCODER: if(ivtv_verbose) printf(IVTV_MSG"Found PVR 350\n"); goto card_found; } } } if(ivtv_verbose) printf(IVTV_MSG"Can't find chip\n"); return ENXIO; card_found: device_name = getenv("FRAMEBUFFER"); if(NULL == device_name) { device_name = fb_dev_name; } fb_number = atoi(device_name+strlen("/dev/fb")); fbdev = open(device_name, O_RDWR); if(-1 != fbdev) { if(ioctl(fbdev, FBIOGET_VSCREENINFO, &vinfo) < 0) { printf(IVTV_MSG"Unable to read screen info\n"); close(fbdev); return ENXIO; } else { fb_width = vinfo.xres; fb_height = vinfo.yres; if(2 == ivtv_verbose) { printf(IVTV_MSG"framebuffer width : %3.0f\n",fb_width); printf(IVTV_MSG"framebuffer height: %3.0f\n",fb_height); } } if(NULL != (alpha = getenv("VIDIXIVTVALPHA"))) { if(0 == strcmp(alpha, "disable")) { alpha_disable = 1; } } } else { printf(IVTV_MSG"Failed to open /dev/fb%u\n", fb_number); return ENXIO; } /* Try to find YUV device */ do { sprintf(yuv_device_name, "/dev/video%u", yuv_device); yuvdev = open(yuv_device_name, O_RDWR); if(-1 != yuvdev) { if(ivtv_verbose) printf(IVTV_MSG"YUV device found /dev/video%u\n", yuv_device); goto yuv_found; } else { if(ivtv_verbose) printf(IVTV_MSG"YUV device not found: /dev/video%u\n", yuv_device); } } while(yuv_device-- > yuv_device_number); return ENXIO; yuv_found: if(0 == alpha_disable) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) if(ioctl(fbdev, IVTVFB_IOCTL_GET_STATE, &fb_state_old) < 0) { printf(IVTV_MSG"Unable to read fb state\n"); close(yuvdev); close(fbdev); return ENXIO; } else { if(ivtv_verbose) { printf(IVTV_MSG"old alpha : %ld\n",fb_state_old.alpha); printf(IVTV_MSG"old status: 0x%lx\n",fb_state_old.status); } fb_state_hide.alpha = 0; fb_state_hide.status = fb_state_old.status | IVTVFB_STATUS_GLOBAL_ALPHA; } #else memset(&format_old, 0, sizeof(format_old)); format_old.type = format_hide.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY; if(ioctl(yuvdev, VIDIOC_G_FMT , &format_old) < 0) { printf(IVTV_MSG"Unable to read fb state\n"); close(yuvdev); close(fbdev); return ENXIO; } else { if(ivtv_verbose) { printf(IVTV_MSG"old alpha : %d\n",format_old.fmt.win.global_alpha); } memcpy(&format_hide, &format_old, sizeof(format_old)); format_hide.fmt.win.global_alpha = 0; } #endif } probed = 1; return 0; } int ivtv_init(const char *args) { if(ivtv_verbose) printf(IVTV_MSG"init\n"); if (!probed) { if(ivtv_verbose) printf(IVTV_MSG"Driver was not probed but is being initialized\n"); return EINTR; } outbuf = malloc((IVTVMAXHEIGHT * IVTVMAXWIDTH) + (IVTVMAXHEIGHT * IVTVMAXWIDTH / 2)); if(NULL == outbuf) { if(ivtv_verbose) printf(IVTV_MSG"Not enough memory availabe!\n"); return EINTR; } return 0; } void ivtv_destroy(void) { if(ivtv_verbose) printf(IVTV_MSG"destroy\n"); if(-1 != yuvdev) close(yuvdev); if(-1 != fbdev) close(fbdev); free(outbuf); free(memBase); } int ivtv_get_caps(vidix_capability_t *to) { if(ivtv_verbose) printf(IVTV_MSG"GetCap\n"); memcpy(to, &ivtv_cap, sizeof(vidix_capability_t)); return 0; } int ivtv_query_fourcc(vidix_fourcc_t *to) { int supports = 0; if(ivtv_verbose) printf(IVTV_MSG"query fourcc (%x)\n", to->fourcc); switch(to->fourcc) { case IMGFMT_YV12: supports = 1; break; default: supports = 0; } if(!supports) { to->depth = to->flags = 0; return ENOTSUP; } to->depth = VID_DEPTH_12BPP | VID_DEPTH_15BPP | VID_DEPTH_16BPP | VID_DEPTH_24BPP | VID_DEPTH_32BPP; to->flags = 0; return 0; } int ivtv_config_playback(vidix_playback_t *info) { if(ivtv_verbose) printf(IVTV_MSG"config playback\n"); if(2 == ivtv_verbose){ printf(IVTV_MSG"src : x:%d y:%d w:%d h:%d\n", info->src.x, info->src.y, info->src.w, info->src.h); printf(IVTV_MSG"dest: x:%d y:%d w:%d h:%d\n", info->dest.x, info->dest.y, info->dest.w, info->dest.h); } memcpy(&destVideo, &info->dest, sizeof(vidix_rect_t)); memcpy(&srcVideo, &info->src, sizeof(vidix_rect_t)); info->num_frames = 2; info->frame_size = frameSize = info->src.w*info->src.h+(info->src.w*info->src.h)/2; info->dest.pitch.y = 1; info->dest.pitch.u = info->dest.pitch.v = 2; info->offsets[0] = 0; info->offsets[1] = info->frame_size; info->offset.y = 0; info->offset.u = info->src.w * info->src.h; info->offset.v = info->offset.u + ((info->src.w * info->src.h)/4); info->dga_addr = memBase = malloc(info->num_frames*info->frame_size); if(ivtv_verbose) printf(IVTV_MSG"frame_size: %d, dga_addr: %p\n", info->frame_size, info->dga_addr); return 0; } int ivtv_playback_on(void) { if(ivtv_verbose) printf(IVTV_MSG"playback on\n"); if(0 == alpha_disable) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) if (-1 != fbdev) { if (ioctl(fbdev, IVTVFB_IOCTL_SET_STATE, &fb_state_hide) < 0) printf (IVTV_MSG"Failed to set fb state\n"); } #else if (-1 != yuvdev) { if (ioctl(yuvdev, VIDIOC_S_FMT, &format_hide) < 0) printf (IVTV_MSG"Failed to set fb state\n"); } #endif } return 0; } int ivtv_playback_off(void) { if(ivtv_verbose) printf(IVTV_MSG"playback off\n"); if(0 == alpha_disable) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) if (-1 != fbdev) { if (ioctl(fbdev, IVTVFB_IOCTL_SET_STATE, &fb_state_old) < 0) printf (IVTV_MSG"Failed to restore fb state\n"); } #else if (-1 != yuvdev) { if (ioctl(yuvdev, VIDIOC_S_FMT, &format_old) < 0) printf (IVTV_MSG"Failed to restore fb state\n"); } #endif } return 0; } int ivtv_frame_sel(unsigned int frame) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) struct ivtvyuv_ioctl_dma_host_to_ivtv_args args; #else struct ivtv_dma_frame args; #endif unsigned char *curMemBase = (unsigned char *)memBase + (frame * frameSize); de_macro_y(curMemBase, outbuf, srcVideo.w, srcVideo.h, srcVideo.x, srcVideo.y, srcVideo.h, srcVideo.w); de_macro_uv(curMemBase + (srcVideo.w * srcVideo.h) + srcVideo.w * srcVideo.h / 4, curMemBase + (srcVideo.w * srcVideo.h), outbuf + (IVTVMAXHEIGHT * IVTVMAXWIDTH), srcVideo.w, srcVideo.h, srcVideo.x, srcVideo.y, srcVideo.h, srcVideo.w); args.y_source = outbuf; args.uv_source = outbuf + (IVTVMAXHEIGHT * IVTVMAXWIDTH); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) args.src_x = srcVideo.x; args.src_y = srcVideo.y; args.dst_x = destVideo.x; args.dst_y = destVideo.y; args.src_w = srcVideo.w; args.dst_w = destVideo.w; args.srcBuf_width = srcVideo.w; args.src_h = srcVideo.h; args.dst_h = destVideo.h; args.srcBuf_height = srcVideo.h; args.yuv_type = 0; #else args.src.left = srcVideo.x; args.src.top = srcVideo.y; args.dst.left = destVideo.x; args.dst.top = destVideo.y; args.src.width = srcVideo.w; args.dst.width = destVideo.w; args.src_width = srcVideo.w; args.src.height = srcVideo.h; args.dst.height = destVideo.h; args.src_height = srcVideo.h; args.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) if(ioctl(yuvdev, IVTV_IOC_PREP_FRAME_YUV, &args) == -1) { #else if(ioctl(yuvdev, IVTV_IOC_DMA_FRAME, &args) == -1) { #endif printf("Ioctl IVTV_IOC_DMA_FRAME returned failed Error\n"); } return 0; } VDXDriver ivtv_drv = { "ivtv", NULL, .probe = ivtv_probe, .get_caps = ivtv_get_caps, .query_fourcc = ivtv_query_fourcc, .init = ivtv_init, .destroy = ivtv_destroy, .config_playback = ivtv_config_playback, .playback_on = ivtv_playback_on, .playback_off = ivtv_playback_off, .frame_sel = ivtv_frame_sel, };