You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

258 lines
6.3 KiB

/*
* Linux audio/video grab interface
* Copyright (c) 2000 Gerard Lantau.
*
* This program 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.
*
* This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <netinet/in.h>
#include <linux/videodev.h>
#include <linux/soundcard.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/time.h>
#include <getopt.h>
#include "mpegenc.h"
#include "mpegvideo.h"
long long gettime(void)
{
struct timeval tv;
gettimeofday(&tv,NULL);
return (long long)tv.tv_sec * 1000000 + tv.tv_usec;
}
/* v4l capture */
const char *v4l_device = "/dev/video";
static struct video_capability video_cap;
int video_fd = -1;
UINT8 *video_buf, *picture_buf;
struct video_mbuf gb_buffers;
struct video_mmap gb_buf;
struct video_audio audio;
int gb_frame = 0;
long long time_frame;
int frame_rate;
int use_mmap = 0;
int v4l_init(int rate, int width, int height)
{
frame_rate = rate;
video_fd = open(v4l_device, O_RDWR);
if (ioctl(video_fd,VIDIOCGCAP,&video_cap) < 0) {
perror("VIDIOCGCAP");
return -1;
}
/* unmute audio */
ioctl(video_fd, VIDIOCGAUDIO, &audio);
audio.flags &= ~VIDEO_AUDIO_MUTE;
ioctl(video_fd, VIDIOCSAUDIO, &audio);
if (!(video_cap.type & VID_TYPE_CAPTURE)) {
/* try to use read based access */
struct video_window win;
int val;
win.x = 0;
win.y = 0;
win.width = width;
win.height = height;
win.chromakey = -1;
win.flags = 0;
ioctl(video_fd, VIDIOCSWIN, &win);
val = 1;
ioctl(video_fd, VIDIOCCAPTURE, &val);
video_buf = malloc( width * height * 2);
picture_buf = malloc( (width * height * 3) / 2);
use_mmap = 0;
return 0;
}
if (ioctl(video_fd,VIDIOCGMBUF,&gb_buffers) < 0) {
perror("ioctl VIDIOCGMBUF");
}
video_buf = mmap(0,gb_buffers.size,PROT_READ|PROT_WRITE,MAP_SHARED,video_fd,0);
if ((unsigned char*)-1 == video_buf) {
perror("mmap");
return -1;
}
gb_frame = 0;
time_frame = gettime();
/* start to grab the first frame */
gb_buf.frame = 1 - gb_frame;
gb_buf.height = height;
gb_buf.width = width;
gb_buf.format = VIDEO_PALETTE_YUV420P;
if (ioctl(video_fd, VIDIOCMCAPTURE, &gb_buf) < 0) {
if (errno == EAGAIN)
fprintf(stderr,"Cannot Sync\n");
else
perror("VIDIOCMCAPTURE");
return -1;
}
use_mmap = 1;
return 0;
}
/* test with read call and YUV422 stream */
static int v4l_basic_read_picture(UINT8 *picture[3],
int width, int height,
int picture_number)
{
int x, y;
UINT8 *p, *lum, *cb, *cr;
if (read(video_fd, video_buf, width * height * 2) < 0)
perror("read");
picture[0] = picture_buf;
picture[1] = picture_buf + width * height;
picture[2] = picture_buf + (width * height) + (width * height) / 4;
/* XXX: optimize */
lum = picture[0];
cb = picture[1];
cr = picture[2];
p = video_buf;
for(y=0;y<height;y+=2) {
for(x=0;x<width;x+=2) {
lum[0] = p[0];
cb[0] = p[1];
lum[1] = p[2];
cr[0] = p[3];
p += 4;
lum += 2;
cb++;
cr++;
}
for(x=0;x<width;x+=2) {
lum[0] = p[0];
lum[1] = p[2];
p += 4;
lum += 2;
}
}
return 0;
}
static int v4l_mm_read_picture(UINT8 *picture[3],
int width, int height,
int picture_number)
{
UINT8 *ptr;
int size;
long long curtime;
/* wait based on the frame rate */
time_frame += 1000000 / frame_rate;
do {
curtime = gettime();
} while (curtime < time_frame);
gb_buf.frame = gb_frame;
if (ioctl(video_fd, VIDIOCMCAPTURE, &gb_buf) < 0) {
if (errno == EAGAIN)
fprintf(stderr,"Cannot Sync\n");
else
perror("VIDIOCMCAPTURE");
return -1;
}
gb_frame = 1 - gb_frame;
if (ioctl(video_fd, VIDIOCSYNC, &gb_frame) < 0) {
if (errno != EAGAIN) {
perror("VIDIOCSYNC");
}
}
size = width * height;
ptr = video_buf + gb_buffers.offsets[gb_frame];
picture[0] = ptr;
picture[1] = ptr + size;
picture[2] = ptr + size + (size / 4);
return 0;
}
int v4l_read_picture(UINT8 *picture[3],
int width, int height,
int picture_number)
{
if (use_mmap) {
return v4l_mm_read_picture(picture, width, height, picture_number);
} else {
return v4l_basic_read_picture(picture, width, height, picture_number);
}
}
/* open audio device */
int audio_open(int freq, int channels)
{
int audio_fd, tmp, err;
audio_fd = open("/dev/dsp",O_RDONLY);
if (audio_fd == -1) {
perror("/dev/dsp");
return -1;
}
/* non blocking mode */
fcntl(audio_fd, F_SETFL, O_NONBLOCK);
#if 0
tmp=(NB_FRAGMENTS << 16) | FRAGMENT_BITS;
err=ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &tmp);
if (err < 0) {
perror("SNDCTL_DSP_SETFRAGMENT");
}
#endif
/* always set to this size */
/* XXX: incorrect if big endian */
tmp=AFMT_S16_LE;
err=ioctl(audio_fd,SNDCTL_DSP_SETFMT,&tmp);
if (err < 0) {
perror("SNDCTL_DSP_SETFMT");
}
tmp= (channels == 2);
err=ioctl(audio_fd,SNDCTL_DSP_STEREO,&tmp);
if (err < 0) {
perror("SNDCTL_DSP_STEREO");
}
/* should be last */
tmp = freq;
err=ioctl(audio_fd, SNDCTL_DSP_SPEED, &tmp);
if (err < 0) {
perror("SNDCTL_DSP_SPEED");
}
return audio_fd;
}