diff options
-rw-r--r-- | cfg-common.h | 3 | ||||
-rw-r--r-- | libmpdemux/demuxer.c | 8 | ||||
-rw-r--r-- | libmpdemux/open.c | 9 | ||||
-rw-r--r-- | libmpdemux/tv.c | 168 | ||||
-rw-r--r-- | libmpdemux/tv.h | 84 | ||||
-rw-r--r-- | libmpdemux/tvi_def.h | 12 | ||||
-rw-r--r-- | libmpdemux/tvi_dummy.c | 50 | ||||
-rw-r--r-- | libmpdemux/tvi_v4l.c | 555 |
8 files changed, 731 insertions, 158 deletions
diff --git a/cfg-common.h b/cfg-common.h index 1a0dba2c32..b2fd24dc06 100644 --- a/cfg-common.h +++ b/cfg-common.h @@ -94,11 +94,12 @@ struct config tvopts_conf[]={ {"on", &tv_param_on, CONF_TYPE_FLAG, 0, 0, 1}, {"driver", &tv_param_driver, CONF_TYPE_STRING, 0, 0, 0}, {"device", &tv_param_device, CONF_TYPE_STRING, 0, 0, 0}, - {"freq", &tv_param_freq, CONF_TYPE_FLOAT, CONF_RANGE, 0, 2500000}, + {"freq", &tv_param_freq, CONF_TYPE_INT, CONF_RANGE, 0, 2500000}, {"channel", &tv_param_channel, CONF_TYPE_STRING, 0, 0, 0}, {"norm", &tv_param_norm, CONF_TYPE_STRING, 0, 0, 0}, {"width", &tv_param_width, CONF_TYPE_INT, 0, 0, 4096}, {"height", &tv_param_height, CONF_TYPE_INT, 0, 0, 4096}, + {"input", &tv_param_input, CONF_TYPE_INT, 0, 0, 20}, {NULL, NULL, 0, 0, 0, 0} }; #endif diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c index 76ee019e52..3b39177f87 100644 --- a/libmpdemux/demuxer.c +++ b/libmpdemux/demuxer.c @@ -158,7 +158,9 @@ int demux_asf_fill_buffer(demuxer_t *demux); int demux_mov_fill_buffer(demuxer_t *demux,demux_stream_t* ds); int demux_vivo_fill_buffer(demuxer_t *demux); #ifdef USE_TV -int demux_tv_fill_buffer(demuxer_t *demux); +#include "tv.h" +extern tvi_handle_t *tv_handler; +int demux_tv_fill_buffer(demuxer_t *demux, tvi_handle_t *tvh); #endif int demux_fill_buffer(demuxer_t *demux,demux_stream_t *ds){ @@ -174,7 +176,7 @@ int demux_fill_buffer(demuxer_t *demux,demux_stream_t *ds){ case DEMUXER_TYPE_MOV: return demux_mov_fill_buffer(demux,ds); case DEMUXER_TYPE_VIVO: return demux_vivo_fill_buffer(demux); #ifdef USE_TV - case DEMUXER_TYPE_TV: return demux_tv_fill_buffer(demux); + case DEMUXER_TYPE_TV: return demux_tv_fill_buffer(demux, tv_handler); #endif } return 0; @@ -554,7 +556,7 @@ switch(file_format){ } #ifdef USE_TV case DEMUXER_TYPE_TV: { - demux_open_tv(demuxer); + demux_open_tv(demuxer, tv_handler); break; } #endif diff --git a/libmpdemux/open.c b/libmpdemux/open.c index 6b820697d4..298d7064c5 100644 --- a/libmpdemux/open.c +++ b/libmpdemux/open.c @@ -72,7 +72,7 @@ extern int vcd_get_track_end(int fd,int track); #ifdef USE_TV #include "tv.h" -extern tvi_handle_t *tv_handler; +tvi_handle_t *tv_handler; #endif // Open a new stream (stdin/file/vcd/url) @@ -251,12 +251,13 @@ if(dvd_title){ //============ Check for TV-input ==== if (tv_param_on==1) { - stream=new_stream(-1,STREAMTYPE_TV); + stream = new_stream(-1,STREAMTYPE_TV); tv_handler = tv_begin(); if (!tv_handler) return(NULL); - tv_init(tv_handler); - return(stream); + if (tv_init(tv_handler) == 1) + return(stream); + return(NULL); } #endif diff --git a/libmpdemux/tv.c b/libmpdemux/tv.c index ed1377433c..48f089bad3 100644 --- a/libmpdemux/tv.c +++ b/libmpdemux/tv.c @@ -13,19 +13,16 @@ #include "config.h" #ifdef USE_TV -#include "tv.h" #include "mp_msg.h" #include "help_mp.h" #include "stream.h" #include "demuxer.h" #include "stheader.h" - -/* global! */ -tvi_handle_t *tv_handler; +#include "tv.h" /* some default values */ -float tv_param_freq = 0.0; +unsigned long tv_param_freq = 0; char *tv_param_channel = "0"; char *tv_param_norm = "pal"; int tv_param_on = 0; @@ -33,6 +30,7 @@ char *tv_param_device = NULL; char *tv_param_driver = "dummy"; int tv_param_width = -1; int tv_param_height = -1; +int tv_param_input = 0; /* used in v4l and bttv */ /* ================== DEMUX_TV ===================== */ @@ -42,25 +40,32 @@ int tv_param_height = -1; 1 = successfully read a packet */ /* fill demux->video and demux->audio */ -int demux_tv_fill_buffer(demuxer_t *demux) +int demux_tv_fill_buffer(demuxer_t *demux, tvi_handle_t *tvh) { - int seq; + int seq = tvh->seq; demux_stream_t *ds_video = NULL; demux_packet_t *dp_video = NULL; demux_stream_t *ds_audio = NULL; demux_packet_t *dp_audio = NULL; int len_video, len_audio; + printf("demux_tv_fill_buffer(sequence:%d) called!\n", seq); + demux->filepos = -1; + seq++; + tvh->seq++; + /* ================== ADD VIDEO PACKET =================== */ - len_video = tv_handler->functions->get_video_framesize(tv_handler->priv); + len_video = tvh->functions->get_video_framesize(tvh->priv); ds_video = demux->video; - if (!ds_video) - { + if (!ds_video->asf_packet) + { + /* create new packet */ dp_video = new_demux_packet(len_video); - tv_handler->functions->grab_video_frame(tv_handler->priv, dp_video->buffer, len_video); +// printf("new dp_video->buffer: %p (%d bytes)\n", dp_video, len_video); + tvh->functions->grab_video_frame(tvh->priv, dp_video->buffer, len_video); dp_video->pos = demux->filepos; ds_video->asf_packet = dp_video; ds_video->asf_seq = seq; @@ -69,14 +74,17 @@ int demux_tv_fill_buffer(demuxer_t *demux) { if (ds_video->asf_seq != seq) { + /* close segment, finalize packet */ ds_add_packet(ds_video, ds_video->asf_packet); ds_video->asf_packet = NULL; } else { + /* append data to segment */ dp_video = ds_video->asf_packet; dp_video->buffer = realloc(dp_video->buffer, dp_video->len+len_video); - tv_handler->functions->grab_video_frame(tv_handler->priv, dp_video->buffer+dp_video->len, len_video); +// printf("dp_video->buffer: %p (%d bytes)\n", dp_video, dp_video->len+len_video); + tvh->functions->grab_video_frame(tvh->priv, dp_video->buffer+dp_video->len, len_video); mp_dbg(MSGT_DEMUX,MSGL_DBG4, "video data appended %d+%d\n", dp_video->len, len_video); dp_video->len += len_video; } @@ -84,13 +92,16 @@ int demux_tv_fill_buffer(demuxer_t *demux) /* ================== ADD AUDIO PACKET =================== */ - len_audio = tv_handler->functions->get_audio_framesize(tv_handler->priv); + if (tvh->functions->control(tvh->priv, TVI_CONTROL_IS_AUDIO, 0) != TVI_CONTROL_TRUE) + return 1; /* no audio, only video */ + + len_audio = tvh->functions->get_audio_framesize(tvh->priv); ds_audio = demux->audio; - if (!ds_audio) + if (!ds_audio->asf_packet) { dp_audio = new_demux_packet(len_audio); - tv_handler->functions->grab_audio_frame(tv_handler->priv, dp_audio->buffer, len_audio); + tvh->functions->grab_audio_frame(tvh->priv, dp_audio->buffer, len_audio); dp_audio->pos = demux->filepos; ds_audio->asf_packet = dp_audio; ds_audio->asf_seq = seq; @@ -106,7 +117,7 @@ int demux_tv_fill_buffer(demuxer_t *demux) { dp_audio = ds_audio->asf_packet; dp_audio->buffer = realloc(dp_audio->buffer, dp_audio->len+len_audio); - tv_handler->functions->grab_audio_frame(tv_handler->priv, dp_audio->buffer+dp_audio->len, len_audio); + tvh->functions->grab_audio_frame(tvh->priv, dp_audio->buffer+dp_audio->len, len_audio); mp_dbg(MSGT_DEMUX,MSGL_DBG4, "audio data appended %d+%d\n", dp_audio->len, len_audio); dp_audio->len += len_audio; } @@ -115,18 +126,30 @@ int demux_tv_fill_buffer(demuxer_t *demux) return 1; } -int demux_open_tv(demuxer_t *demuxer) +int demux_open_tv(demuxer_t *demuxer, tvi_handle_t *tvh) { - sh_video_t *sh_video; - tvi_handle_t *tvh = tv_handler; + sh_video_t *sh_video = NULL; + sh_audio_t *sh_audio = NULL; tvi_functions_t *funcs = tvh->functions; - sh_video = new_sh_video(demuxer,0); + if (funcs->control(tvh->priv, TVI_CONTROL_IS_VIDEO, 0) != TVI_CONTROL_TRUE) + { + printf("Error: no video input present!\n"); + return; + } + + sh_video = new_sh_video(demuxer, 0); -// sh->format=0x7476696e; /* "tvin" */ - if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FORMAT, &sh_video->format) != TVI_CONTROL_TRUE) - sh_video->format = 0x00000000; + /* hack to use YUV 4:2:0 format ;) */ + sh_video->format = IMGFMT_YV12; + funcs->control(tvh->priv, TVI_CONTROL_VID_SET_FORMAT, &sh_video->format); + /* get IMGFMT_ */ + funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FORMAT, &sh_video->format); + if (IMGFMT_IS_RGB(sh_video->format) || IMGFMT_IS_BGR(sh_video->format)) + sh_video->format = 0x0; + + /* set FPS and FRAMETIME */ if(!sh_video->fps) { if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FPS, &sh_video->fps) != TVI_CONTROL_TRUE) @@ -169,47 +192,100 @@ int demux_open_tv(demuxer_t *demuxer) } else funcs->control(tvh->priv, TVI_CONTROL_VID_GET_HEIGHT, &sh_video->disp_h); - - /* emulate BITMAPINFOHEADER */ - sh_video->bih = malloc(sizeof(BITMAPINFOHEADER)); - memset(sh_video->bih, 0, sizeof(BITMAPINFOHEADER)); - sh_video->bih->biSize = 40; - sh_video->bih->biWidth = sh_video->disp_w; - sh_video->bih->biHeight = sh_video->disp_h; - if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_PLANES, &sh_video->bih->biPlanes) != TVI_CONTROL_TRUE) - sh_video->bih->biPlanes = 1; - if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_BITS, &sh_video->bih->biBitCount) != TVI_CONTROL_TRUE) - sh_video->bih->biBitCount = 12; - sh_video->bih->biCompression = sh_video->format; - sh_video->bih->biSizeImage = sh_video->bih->biWidth * sh_video->bih->biHeight * 3; + + printf("Output size: %dx%d\n", sh_video->disp_w, sh_video->disp_h); demuxer->video->sh = sh_video; sh_video->ds = demuxer->video; demuxer->video->id = 0; /* here comes audio init */ + if (funcs->control(tvh->priv, TVI_CONTROL_IS_AUDIO, 0) == TVI_CONTROL_TRUE) + { + int audio_format; + + sh_audio = new_sh_audio(demuxer, 0); + + sh_audio->wf = malloc(sizeof(WAVEFORMATEX)); + memset(sh_audio->wf, 0, sizeof(WAVEFORMATEX)); + + /* yeah, audio is present */ + if (funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_FORMAT, &audio_format) != TVI_CONTROL_TRUE) + goto no_audio; + switch(audio_format) + { + case AFMT_U8: + case AFMT_S8: + case AFMT_U16_LE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_S16_BE: + case AFMT_S32_LE: + case AFMT_S32_BE: + sh_audio->format = 0x1; /* PCM */ + break; + case AFMT_IMA_ADPCM: + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_MPEG: + case AFMT_AC3: + default: + printf("%s unsupported!\n", audio_out_format_name(audio_format)); + goto no_audio; + } + + funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_CHANNELS, &sh_audio->wf->nChannels); + funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_SAMPLERATE, &sh_audio->wf->nSamplesPerSec); + funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_SAMPLESIZE, &sh_audio->wf->nAvgBytesPerSec); + + demuxer->audio->sh = sh_audio; + sh_audio->ds = demuxer->audio; + demuxer->audio->id = 0; + } +no_audio: + + /* set some params got from cmdline */ + funcs->control(tvh->priv, TVI_CONTROL_SPC_SET_INPUT, &tv_param_input); + + /* set freq in MHz - change this to float ! */ + funcs->control(tvh->priv, TVI_CONTROL_TUN_SET_FREQ, &tv_param_freq); + + funcs->control(tvh->priv, TVI_CONTROL_TUN_GET_FREQ, &tv_param_freq); + printf("freq: %lu\n", tv_param_freq); + + /* also start device! */ + funcs->start(tvh->priv); } /* ================== STREAM_TV ===================== */ -tvi_handle_t *tv_begin() +tvi_handle_t *tv_begin(void) { if (!strcmp(tv_param_driver, "dummy")) - return tvi_init_dummy(tv_param_device); + return (tvi_handle_t *)tvi_init_dummy(tv_param_device); if (!strcmp(tv_param_driver, "v4l")) - return tvi_init_v4l(tv_param_device); + return (tvi_handle_t *)tvi_init_v4l(tv_param_device); mp_msg(MSGT_TV, MSGL_ERR, "No such driver: %s\n", tv_param_driver); return(NULL); } -void tv_init(tvi_handle_t *tvi) +int tv_init(tvi_handle_t *tvh) { - printf("Using driver: %s\n", tvi->info->short_name); - printf(" name: %s\n", tvi->info->name); - printf(" author: %s\n", tvi->info->author); - if (tvi->info->comment) - printf(" comment: %s\n", tvi->info->comment); + tvi_param_t *params; + + printf("Selected driver: %s\n", tvh->info->short_name); + printf(" name: %s\n", tvh->info->name); + printf(" author: %s\n", tvh->info->author); + if (tvh->info->comment) + printf(" comment: %s\n", tvh->info->comment); + + params = malloc(sizeof(tvi_param_t)*2); + params[0].opt = malloc(strlen("input")); + sprintf((char *)params[0].opt, "input"); + params[0].value = malloc(sizeof(int)); + (int)*(void **)params[0].value = tv_param_input; + params[1].opt = params[1].value = NULL; - return tvi->functions->init(tvi->priv); + return tvh->functions->init(tvh->priv, params); } #endif /* USE_TV */ diff --git a/libmpdemux/tv.h b/libmpdemux/tv.h index c9e7e6daa6..0241eddb03 100644 --- a/libmpdemux/tv.h +++ b/libmpdemux/tv.h @@ -1,7 +1,10 @@ #include "config.h" #ifdef USE_TV -extern float tv_param_freq; +#include "../libao2/afmt.h" +#include "../libvo/img_format.h" + +extern unsigned long tv_param_freq; extern char *tv_param_channel; extern char *tv_param_norm; extern int tv_param_on; @@ -9,6 +12,7 @@ extern char *tv_param_device; extern char *tv_param_driver; extern int tv_param_width; extern int tv_param_height; +extern int tv_param_input; typedef struct tvi_info_s { @@ -21,42 +25,78 @@ typedef struct tvi_info_s typedef struct tvi_functions_s { int (*init)(); - int (*exit)(); + int (*uninit)(); int (*control)(); + int (*start)(); int (*grab_video_frame)(); int (*get_video_framesize)(); int (*grab_audio_frame)(); int (*get_audio_framesize)(); } tvi_functions_t; +typedef struct tvi_param_s { + const char *opt; + void *value; +} tvi_param_t; + typedef struct tvi_handle_s { - tvi_info_t *info; - tvi_functions_t *functions; - void *priv; + tvi_info_t *info; + tvi_functions_t *functions; + void *priv; + tvi_param_t *params; + int seq; } tvi_handle_t; + #define TVI_CONTROL_FALSE 0 #define TVI_CONTROL_TRUE 1 #define TVI_CONTROL_NA -1 #define TVI_CONTROL_UNKNOWN -2 +/* ======================== CONTROLS =========================== */ + +/* GENERIC controls */ +#define TVI_CONTROL_IS_AUDIO 0x1 +#define TVI_CONTROL_IS_VIDEO 0x2 +#define TVI_CONTROL_IS_TUNER 0x3 + +/* VIDEO controls */ +#define TVI_CONTROL_VID_GET_FPS 0x101 +#define TVI_CONTROL_VID_GET_PLANES 0x102 +#define TVI_CONTROL_VID_GET_BITS 0x103 +#define TVI_CONTROL_VID_CHK_BITS 0x104 +#define TVI_CONTROL_VID_SET_BITS 0x105 +#define TVI_CONTROL_VID_GET_FORMAT 0x106 +#define TVI_CONTROL_VID_CHK_FORMAT 0x107 +#define TVI_CONTROL_VID_SET_FORMAT 0x108 +#define TVI_CONTROL_VID_GET_WIDTH 0x109 +#define TVI_CONTROL_VID_CHK_WIDTH 0x110 +#define TVI_CONTROL_VID_SET_WIDTH 0x111 +#define TVI_CONTROL_VID_GET_HEIGHT 0x112 +#define TVI_CONTROL_VID_CHK_HEIGHT 0x113 +#define TVI_CONTROL_VID_SET_HEIGHT 0x114 + +/* TUNER controls */ +#define TVI_CONTROL_TUN_GET_FREQ 0x201 +#define TVI_CONTROL_TUN_SET_FREQ 0x202 +#define TVI_CONTROL_TUN_GET_TUNER 0x203 /* update priv->tuner struct for used input */ +#define TVI_CONTROL_TUN_SET_TUNER 0x204 /* update priv->tuner struct for used input */ +#define TVI_CONTROL_TUN_GET_NORM 0x205 +#define TVI_CONTROL_TUN_SET_NORM 0x206 + +/* AUDIO controls */ +#define TVI_CONTROL_AUD_GET_FORMAT 0x301 +#define TVI_CONTROL_AUD_GET_SAMPLERATE 0x302 +#define TVI_CONTROL_AUD_GET_SAMPLESIZE 0x303 +#define TVI_CONTROL_AUD_GET_CHANNELS 0x304 + +/* SPECIFIC controls */ +#define TVI_CONTROL_SPC_GET_INPUT 0x401 /* set input channel (tv,s-video,composite..) */ +#define TVI_CONTROL_SPC_SET_INPUT 0x402 /* set input channel (tv,s-video,composite..) */ -#define TVI_CONTROL_VID_GET_FPS 1 -#define TVI_CONTROL_VID_GET_PLANES 2 -#define TVI_CONTROL_VID_GET_BITS 3 -#define TVI_CONTROL_VID_CHK_BITS 4 -#define TVI_CONTROL_VID_SET_BITS 5 -#define TVI_CONTROL_VID_GET_FORMAT 6 -#define TVI_CONTROL_VID_CHK_FORMAT 7 -#define TVI_CONTROL_VID_SET_FORMAT 8 -#define TVI_CONTROL_VID_GET_WIDTH 9 -#define TVI_CONTROL_VID_CHK_WIDTH 10 -#define TVI_CONTROL_VID_SET_WIDTH 11 -#define TVI_CONTROL_VID_GET_HEIGHT 12 -#define TVI_CONTROL_VID_CHK_HEIGHT 13 -#define TVI_CONTROL_VID_SET_HEIGHT 14 - -#define TVI_CONTROL_TUN_GET_FREQ 100 -#define TVI_CONTROL_TUN_SET_FREQ 101 +//extern int demux_tv_fill_buffer(demuxer_t *demux, tvi_handle_t *tvh); +//extern int demux_open_tv(demuxer_t *demux, tvi_handle_t *tvh); +extern tvi_handle_t *tv_begin(void); +extern int tv_init(tvi_handle_t *tvh); #endif /* USE_TV */ diff --git a/libmpdemux/tvi_def.h b/libmpdemux/tvi_def.h index 9e9973b5f9..87511951d7 100644 --- a/libmpdemux/tvi_def.h +++ b/libmpdemux/tvi_def.h @@ -1,6 +1,7 @@ -static int init(priv_t *priv); -static int exit(priv_t *priv); +static int init(priv_t *priv, tvi_param_t *params); +static int uninit(priv_t *priv); static int control(priv_t *priv, int cmd, void *arg); +static int start(priv_t *priv); static int grab_video_frame(priv_t *priv, char *buffer, int len); static int get_video_framesize(priv_t *priv); static int grab_audio_frame(priv_t *priv, char *buffer, int len); @@ -9,8 +10,9 @@ static int get_audio_framesize(priv_t *priv); static tvi_functions_t functions = { init, - exit, + uninit, control, + start, grab_video_frame, get_video_framesize, grab_audio_frame, @@ -23,7 +25,7 @@ static tvi_handle_t *new_handle() if (!h) return(NULL); - h->priv = malloc(sizeof(priv_t)); + h->priv = (priv_t *)malloc(sizeof(priv_t)); if (!h->priv) { free(h); @@ -32,6 +34,8 @@ static tvi_handle_t *new_handle() memset(h->priv, 0, sizeof(priv_t)); h->info = &info; h->functions = &functions; + h->params = NULL; + h->seq = 0; return(h); } diff --git a/libmpdemux/tvi_dummy.c b/libmpdemux/tvi_dummy.c index 82d2c1e691..d2f9f71473 100644 --- a/libmpdemux/tvi_dummy.c +++ b/libmpdemux/tvi_dummy.c @@ -1,58 +1,92 @@ -#include <stdio.h> +/* + Only a sample! +*/ #include "config.h" #ifdef USE_TV + +#include <stdio.h> #include "tv.h" +/* information about this file */ static tvi_info_t info = { "NULL-TV", "dummy", "alex", - "non-completed" + NULL }; +/* private data's */ typedef struct { + int width; + int height; } priv_t; #include "tvi_def.h" +/* handler creator - entry point ! */ tvi_handle_t *tvi_init_dummy(char *device) { return new_handle(); } -static int init(priv_t *priv) +/* initialisation */ +static int init(priv_t *priv, tvi_param_t *params) +{ + return 1; +} + +/* that's the real start, we'got the format parameters (checked with control) */ +static int start(priv_t *priv) { } -static int close(priv_t *priv) +static int uninit(priv_t *priv) { } static int control(priv_t *priv, int cmd, void *arg) { + switch(cmd) + { + case TVI_CONTROL_IS_VIDEO: + return(TVI_CONTROL_TRUE); + case TVI_CONTROL_VID_GET_FORMAT: + (int)*(void **)arg = IMGFMT_YV12; + return(TVI_CONTROL_TRUE); + case TVI_CONTROL_VID_SET_WIDTH: + priv->width = (int)*(void **)arg; + return(TVI_CONTROL_TRUE); + case TVI_CONTROL_VID_SET_HEIGHT: + priv->height = (int)*(void **)arg; + return(TVI_CONTROL_TRUE); + case TVI_CONTROL_VID_CHK_WIDTH: + case TVI_CONTROL_VID_CHK_HEIGHT: + return(TVI_CONTROL_TRUE); + } return(TVI_CONTROL_UNKNOWN); } static int grab_video_frame(priv_t *priv, char *buffer, int len) { - memset(buffer, 0x77, len); + memset(buffer, 0x42, len); } static int get_video_framesize(priv_t *priv) { - return 0; + /* YV12 */ + return priv->width*priv->height*12/8; } static int grab_audio_frame(priv_t *priv, char *buffer, int len) { - memset(buffer, 0x77, len); + memset(buffer, 0x42, len); } static int get_audio_framesize(priv_t *priv) { - return 0; + return 1; } #endif /* USE_TV */ diff --git a/libmpdemux/tvi_v4l.c b/libmpdemux/tvi_v4l.c index 43fb8713f4..11b80fa587 100644 --- a/libmpdemux/tvi_v4l.c +++ b/libmpdemux/tvi_v4l.c @@ -1,3 +1,14 @@ +/* + v4l interface for libmpemux/tvi + + (C) Alex Beregszaszi <alex@naxine.org> + + Some ideas are based on xawtv/libng's grab-v4l.c written by + Gerd Knorr <kraxel@bytesex.org> + + CODE IS UNDER DEVELOPMENT, NO FEATURE REQUESTS PLEASE! +*/ + #include "config.h" #ifdef USE_TV @@ -5,6 +16,7 @@ #include <stdio.h> #include <errno.h> #include <fcntl.h> +#include <signal.h> #include <sys/ioctl.h> #include <sys/types.h> #include <linux/videodev.h> @@ -16,35 +28,168 @@ static tvi_info_t info = { "Video for Linux TV Input", "v4l", - "alex", - "non-completed" + "Alex Beregszaszi <alex@naxine.org>", + "under development" +}; + +struct vid_fmt { + int fmtid; + int width; + int height; + int bytesperline; }; typedef struct { + /* general */ char *video_device; int fd; struct video_capability capability; struct video_channel *channels; struct video_tuner tuner; - struct video_audio audio; + + /* video */ struct video_picture picture; + int format; /* output format */ + int width; + int height; + int bytesperline; - int buffered; struct video_mbuf mbuf; - unsigned int *mmap; + unsigned char *mmap; struct video_mmap *buf; - - int width; - int height; + int nbuf; + int queue; + + /* audio */ + struct video_audio audio; } priv_t; #include "tvi_def.h" static const char *device_cap[] = { "capture", "tuner", "teletext", "overlay", "chromakey", "clipping", - "frameram", "scales", "monochrome", NULL + "frameram", "scales", "monochrome", "subcapture", "mpeg-decoder", + "mpeg-encoder", "mjpeg-decoder", "mjpeg-encoder", NULL }; +static const char *device_pal[] = { + "-", "grey", "hi240", "rgb16", "rgb24", "rgb32", "rgb15", "yuv422", + "yuyv", "uyvy", "yuv420", "yuv411", "raw", "yuv422p", "yuv411p", + "yuv420p", "yuv410p", NULL +}; +#define PALETTE(x) ((x < sizeof(device_pal)/sizeof(char*)) ? device_pal[x] : "UNKNOWN") + +static int palette2depth(int palette) +{ + if (palette == VIDEO_PALETTE_YUV420P) + return 12; + return 32; +} + +static int format2palette(int format) +{ + if (format == IMGFMT_YV12) + return VIDEO_PALETTE_YUV420P; + return VIDEO_PALETTE_RGB24; +} + +#if 0 +struct STRTAB { + long nr; + const char *str; +}; + +static struct STRTAB stereo[] = { + { 0, "auto" }, + { VIDEO_SOUND_MONO, "mono" }, + { VIDEO_SOUND_STEREO, "stereo" }, + { VIDEO_SOUND_LANG1, "lang1" }, + { VIDEO_SOUND_LANG2, "lang1" }, + { -1, NULL } +}; + +static struct STRTAB norms_v4l[] = { + { VIDEO_MODE_PAL, "PAL" }, + { VIDEO_MODE_NTSC, "NTSC" }, + { VIDEO_MODE_SECAM, "SECAM" }, + { VIDEO_MODE_AUTO, "AUTO" }, + { -1, NULL } +}; + +static struct STRTAB norms_bttv[] = { + { VIDEO_MODE_PAL, "PAL" }, + { VIDEO_MODE_NTSC, "NTSC" }, + { VIDEO_MODE_SECAM, "SECAM" }, + { 3, "PAL-NC" }, + { 4, "PAL-M" }, + { 5, "PAL-N" }, + { 6, "NTSC-JP" }, + { -1, NULL } +}; + +static unsigned short _format2palette[VIDEO_FMT_COUNT] = { + 0, /* unused */ + VIDEO_PALETTE_HI240, /* RGB8 */ + VIDEO_PALETTE_GREY, + VIDEO_PALETTE_RGB555, + VIDEO_PALETTE_RGB565, + 0, + 0, + VIDEO_PALETTE_RGB24, + VIDEO_PALETTE_RGB32, + 0, + 0, + 0, + 0, + VIDEO_PALETTE_YUV422, + VIDEO_PALETTE_YUV422P, + VIDEO_PALETTE_YUV420P, +}; +#define FMT2PAL(fmt) ((fmt < sizeof(format2palette)/sizeof(unsigned short)) ? \ + format2palette[fmt] : 0); + +const unsigned int vfmt_to_depth[] = { + 0, + 8, + 8, + 16, + 16, + 16, + 16, + 24, + 32, + 24, + 32, + 16, + 32, + 16, + 16, + 12, + 0, + 0, +}; +#endif + +static int one = 1, zero = 0; + +static int alarms; + +static void sigalarm(int signal) +{ + alarms++; + printf("v4l: timeout (got SIGALRM), hardware/driver problems?\n"); +} + +static void siginit(void) +{ + struct sigaction act, old; + + memset(&act, 0, sizeof(act)); + act.sa_handler = sigalarm; + sigemptyset(&act.sa_mask); + sigaction(SIGALRM, &act, &old); +} + tvi_handle_t *tvi_init_v4l(char *device) { tvi_handle_t *h; @@ -56,10 +201,11 @@ tvi_handle_t *tvi_init_v4l(char *device) priv = h->priv; + /* set video device name */ if (!device) { priv->video_device = malloc(strlen("/dev/video0")); - strcpy(priv->video_device, &"/dev/video0"); + sprintf(priv->video_device, "/dev/video0"); } else { @@ -70,40 +216,52 @@ tvi_handle_t *tvi_init_v4l(char *device) return(h); } -static int init(priv_t *priv) +static int init(priv_t *priv, tvi_param_t *params) { int i; priv->fd = open(priv->video_device, O_RDONLY); if (priv->fd == -1) { - printf("v4l: open %s: %s\n", priv->video_device, strerror(errno)); + printf("v4l: unable to open '%s': %s\n", + priv->video_device, strerror(errno)); goto err; } printf("fd: %d\n", priv->fd); - /* get capabilities */ + /* get capabilities (priv->capability is needed!) */ if (ioctl(priv->fd, VIDIOCGCAP, &priv->capability) == -1) { - printf("v4l: ioctl error: %s\n", strerror(errno)); + printf("ioctl get capabilites error: %s\n", strerror(errno)); goto err; } fcntl(priv->fd, F_SETFD, FD_CLOEXEC); - - printf("capabilites: "); + siginit(); + +#if 0 + for (i=0; params[i].opt; i++) + { + if (!strcmp(params[i].opt, "input")) + priv->input = (int)*(void **)params[i].value; + } + printf("priv->input: %d\n", priv->input); +#endif + + printf("Selected device: %s\n", priv->capability.name); + printf(" Capabilites: "); for (i = 0; device_cap[i] != NULL; i++) if (priv->capability.type & (1 << i)) - printf(" %s", device_cap[i]); + printf("%s ", device_cap[i]); printf("\n"); - printf(" type: %d\n", priv->capability.type); - printf(" size: %dx%d => %dx%d\n", + printf(" Device type: %d\n", priv->capability.type); + printf(" Supported sizes: %dx%d => %dx%d\n", priv->capability.minwidth, priv->capability.minheight, priv->capability.maxwidth, priv->capability.maxheight); priv->width = priv->capability.minwidth; priv->height = priv->capability.minheight; - printf(" channels: %d\n", priv->capability.channels); + printf(" Inputs: %d\n", priv->capability.channels); priv->channels = malloc(sizeof(struct video_channel)*priv->capability.channels); memset(priv->channels, 0, sizeof(struct video_channel)*priv->capability.channels); @@ -111,40 +269,51 @@ static int init(priv_t *priv) { priv->channels[i].channel = i; ioctl(priv->fd, VIDIOCGCHAN, &priv->channels[i]); - printf(" %s: tuners:%d %s%s %s%s\n", + printf(" %d: %s: %s%s%s%s (tuner:%d, norm:%d)\n", i, priv->channels[i].name, - priv->channels[i].tuners, (priv->channels[i].flags & VIDEO_VC_TUNER) ? "tuner " : "", (priv->channels[i].flags & VIDEO_VC_AUDIO) ? "audio " : "", (priv->channels[i].flags & VIDEO_TYPE_TV) ? "tv " : "", - (priv->channels[i].flags & VIDEO_TYPE_CAMERA) ? "camera " : ""); + (priv->channels[i].flags & VIDEO_TYPE_CAMERA) ? "camera " : "", + priv->channels[i].tuners, + priv->channels[i].norm); } - if (priv->capability.type & VID_TYPE_CAPTURE) + if (!(priv->capability.type & VID_TYPE_CAPTURE)) { - if (ioctl(priv->fd, VIDIOCGMBUF, &priv->mbuf) == 0) - { - printf("mbuf: size=%d, frames=%d (first offset: %p)\n", - priv->mbuf.size, priv->mbuf.frames, priv->mbuf.offsets[0]); - priv->mmap = mmap(0, priv->mbuf.size, PROT_READ|PROT_WRITE, - MAP_SHARED, priv->fd, 0); - if (priv->mmap == -1) - perror("mmap"); - } - else - priv->mmap = -1; + printf("Only grabbing supported (for overlay use another program)\n"); + goto err; + } + + /* map grab buffer */ + if (ioctl(priv->fd, VIDIOCGMBUF, &priv->mbuf) == -1) + { + printf("ioctl get mbuf failed: %s\n", strerror(errno)); + goto err; + } - if (priv->mmap != -1) - { - priv->buf = malloc(priv->mbuf.frames * sizeof(struct video_mmap)); - memset(priv->buf, 0, priv->mbuf.frames * sizeof(struct video_mmap)); - priv->buffered = 1; - } - else - priv->buffered = 0; + printf("mbuf: size=%d, frames=%d\n", + priv->mbuf.size, priv->mbuf.frames); + priv->mmap = mmap(0, priv->mbuf.size, PROT_READ|PROT_WRITE, + MAP_SHARED, priv->fd, 0); + if (priv->mmap == -1) + { + printf("Unabel to map memory for buffers: %s\n", strerror(errno)); + priv->mmap = malloc(priv->mbuf.size); /* our buffer */ } + if (!priv->mmap) + { + printf("Unable to allocate memory for buffers: %s\n", strerror(errno)); + goto err; + } + printf("our buffer: %p\n", priv->mmap); + + /* num of buffers */ + priv->nbuf = priv->mbuf.frames; - printf("buffered: %d\n", priv->buffered); + /* video buffers */ + priv->buf = malloc(priv->nbuf * sizeof(struct video_mmap)); + memset(priv->buf, 0, priv->nbuf * sizeof(struct video_mmap)); return(1); @@ -154,32 +323,119 @@ err: return(0); } -static int exit(priv_t *priv) +static int uninit(priv_t *priv) { +#warning "Implement uninit!" } -static int tune(priv_t *priv, int freq, int chan, int norm) +static int start(priv_t *priv) { - if (freq) + int i; + + + if (ioctl(priv->fd, VIDIOCGPICT, &priv->picture) == -1) { - ioctl(priv->fd, VIDIOCSFREQ, &freq); - return(1); + printf("ioctl get picture failed: %s\n", strerror(errno)); + return(0); } - if (chan && norm) + priv->picture.palette = format2palette(priv->format); + priv->picture.depth = palette2depth(priv->picture.palette); + priv->bytesperline = priv->width * priv->picture.depth / 8; + + printf("Picture values:\n"); + printf(" Depth: %d, Palette: %d (Format: %s)\n", priv->picture.depth, + priv->picture.palette, vo_format_name(priv->format)); + printf(" Brightness: %d, Hue: %d, Colour: %d, Contrast: %d\n", + priv->picture.brightness, priv->picture.hue, + priv->picture.colour, priv->picture.contrast); + + + if (ioctl(priv->fd, VIDIOCSPICT, &priv->picture) == -1) { - /* set channel & norm ! */ + printf("ioctl set picture failed: %s\n", strerror(errno)); + return(0); } - return(0); + priv->nbuf = priv->mbuf.frames; + for (i=0; i < priv->nbuf; i++) + { + priv->buf[i].format = priv->picture.palette; + priv->buf[i].frame = i; + priv->buf[i].width = priv->width; + priv->buf[i].height = priv->height; + printf("buffer: %d => %p\n", i, &priv->buf[i]); + } + + /* start capture */ + if (ioctl(priv->fd, VIDIOCCAPTURE, &one) == -1) + { + printf("ioctl capture failed: %s\n", strerror(errno)); + return(0); + } } static int control(priv_t *priv, int cmd, void *arg) { + printf("debug: control(priv=%p, cmd=%d, arg=%p)\n", + priv, cmd, arg); switch(cmd) { + /* ========== GENERIC controls =========== */ + case TVI_CONTROL_IS_VIDEO: + { + if (priv->capability.type & VID_TYPE_CAPTURE) + return(TVI_CONTROL_TRUE); + return(TVI_CONTROL_FALSE); + } + case TVI_CONTROL_IS_AUDIO: + return(TVI_CONTROL_FALSE); /* IMPLEMENT CHECK! */ + case TVI_CONTROL_IS_TUNER: + { + if (priv->capability.type & VID_TYPE_TUNER) + return(TVI_CONTROL_TRUE); + return(TVI_CONTROL_FALSE); + } + + /* ========== VIDEO controls =========== */ case TVI_CONTROL_VID_GET_FORMAT: - (int)*(void **)arg = 0x0; + { + int output_fmt = -1; + +#if 0 + switch(priv->palette) + { + case VIDEO_PALETTE_RGB555: + output_fmt = IMGFMT_RGB15; + break; + case VIDEO_PALETTE_RGB565: + output_fmt = IMGFMT_RGB16; + break; + case VIDEO_PALETTE_RGB24: + output_fmt = IMGFMT_RGB24; + break; + case VIDEO_PALETTE_RGB32: + output_fmt = IMGFMT_RGB32; + break; + case VIDEO_PALETTE_UYVY: + output_fmt = IMGFMT_UYVY; + break; + case VIDEO_PALETTE_YUV420P: + output_fmt = IMGFMT_YV12; + break; + default: + printf("no suitable output format found (%s)\n", + PALETTE(priv->palette)); + return(TVI_CONTROL_FALSE); + } +#endif + output_fmt = priv->format; + (int)*(void **)arg = output_fmt; + printf("Output format: %s\n", vo_format_name(output_fmt)); + return(TVI_CONTROL_TRUE); + } + case TVI_CONTROL_VID_SET_FORMAT: + priv->format = (int)*(void **)arg; return(TVI_CONTROL_TRUE); case TVI_CONTROL_VID_GET_PLANES: (int)*(void **)arg = 1; @@ -194,7 +450,7 @@ static int control(priv_t *priv, int cmd, void *arg) { int req_width = (int)*(void **)arg; - printf("req_width: %d\n", req_width); + printf("Requested width: %d\n", req_width); if ((req_width > priv->capability.minwidth) && (req_width < priv->capability.maxwidth)) return(TVI_CONTROL_TRUE); @@ -210,7 +466,7 @@ static int control(priv_t *priv, int cmd, void *arg) { int req_height = (int)*(void **)arg; - printf("req_height: %d\n", req_height); + printf("Requested height: %d\n", req_height); if ((req_height > priv->capability.minheight) && (req_height < priv->capability.maxheight)) return(TVI_CONTROL_TRUE); @@ -220,14 +476,163 @@ static int control(priv_t *priv, int cmd, void *arg) priv->height = (int)*(void **)arg; return(TVI_CONTROL_TRUE); + /* ========== TUNER controls =========== */ + case TVI_CONTROL_TUN_GET_FREQ: + { + unsigned long freq; + + if (ioctl(priv->fd, VIDIOCGFREQ, &freq) == -1) + { + printf("ioctl get freq failed: %s\n", strerror(errno)); + return(TVI_CONTROL_FALSE); + } + + /* tuner uses khz not mhz ! */ + if (priv->tuner.flags & VIDEO_TUNER_LOW) + freq /= 1000; + (unsigned long)*(void **)arg = freq; + return(TVI_CONTROL_TRUE); + } case TVI_CONTROL_TUN_SET_FREQ: { - long freq = (long)*(void **)arg; /* shit: long -> freq */ + /* argument is in MHz ! */ + unsigned long freq = (unsigned long)*(void **)arg; - printf("requested frequency: %f\n", freq); - if (ioctl(priv->fd, VIDIOCSFREQ, &freq) != -1) - return(TVI_CONTROL_TRUE); - return(TVI_CONTROL_FALSE); + printf("requested frequency: %lu MHz\n", (float)freq/16); + + /* tuner uses khz not mhz ! */ + if (priv->tuner.flags & VIDEO_TUNER_LOW) + freq *= 1000; + printf(" requesting from driver: freq=%.3f\n", (float)freq/16); + if (ioctl(priv->fd, VIDIOCSFREQ, &freq) == -1) + { + printf("ioctl set freq failed: %s\n", strerror(errno)); + return(TVI_CONTROL_FALSE); + } + return(TVI_CONTROL_TRUE); + } + case TVI_CONTROL_TUN_GET_TUNER: + { + if (ioctl(priv->fd, VIDIOCGTUNER, &priv->tuner) == -1) + { + printf("ioctl get tuner failed: %s\n", strerror(errno)); + return(TVI_CONTROL_FALSE); + } + + printf("Tuner (%s) range: %lu -> %lu\n", priv->tuner.name, + priv->tuner.rangelow, priv->tuner.rangehigh); + return(TVI_CONTROL_TRUE); + } + case TVI_CONTROL_TUN_SET_TUNER: + { + if (ioctl(priv->fd, VIDIOCSTUNER, &priv->tuner) == -1) + { + printf("ioctl get tuner failed: %s\n", strerror(errno)); + return(TVI_CONTROL_FALSE); + } + return(TVI_CONTROL_TRUE); + } + case TVI_CONTROL_TUN_SET_NORM: + { + int req_mode = (int)*(void **)arg; + + if ((!(priv->tuner.flags & VIDEO_TUNER_NORM)) || + ((req_mode == VIDEO_MODE_PAL) && !(priv->tuner.flags & VIDEO_TUNER_PAL)) || + ((req_mode == VIDEO_MODE_NTSC) && !(priv->tuner.flags & VIDEO_TUNER_NTSC)) || + ((req_mode == VIDEO_MODE_SECAM) && !(priv->tuner.flags & VIDEO_TUNER_SECAM))) + { + printf("tuner isn't capable to set norm!\n"); + return(TVI_CONTROL_FALSE); + } + + priv->tuner.mode = req_mode; + + if (control(priv->fd, TVI_CONTROL_TUN_SET_TUNER, &priv->tuner) != TVI_CONTROL_TRUE) + return(TVI_CONTROL_FALSE); + return(TVI_CONTROL_TRUE); + } + case TVI_CONTROL_TUN_GET_NORM: + { + (int)*(void **)arg = priv->tuner.mode; + + return(TVI_CONTROL_TRUE); + } + + /* ========== AUDIO controls =========== */ + case TVI_CONTROL_AUD_GET_FORMAT: + { + (int)*(void **)arg = AFMT_S16_LE; + return(TVI_CONTROL_TRUE); + } + case TVI_CONTROL_AUD_GET_CHANNELS: + { + (int)*(void **)arg = 2; + return(TVI_CONTROL_TRUE); + } + case TVI_CONTROL_AUD_GET_SAMPLERATE: + { + (int)*(void **)arg = 44100; + return(TVI_CONTROL_TRUE); + } + case TVI_CONTROL_AUD_GET_SAMPLESIZE: + { + (int)*(void **)arg = 76000; + return(TVI_CONTROL_TRUE); + } + + /* ========== SPECIFIC controls =========== */ + case TVI_CONTROL_SPC_GET_INPUT: + { + int req_chan = (int)*(void **)arg; + int i; + + for (i = 0; i < priv->capability.channels; i++) + { + if (priv->channels[i].channel == req_chan) + break; + } + + if (ioctl(priv->fd, VIDIOCGCHAN, &priv->channels[i]) == -1) + { + printf("ioctl get channel failed: %s\n", strerror(errno)); + return(TVI_CONTROL_FALSE); + } + return(TVI_CONTROL_TRUE); + } + + case TVI_CONTROL_SPC_SET_INPUT: + { + struct video_channel chan; + int req_chan = (int)*(void **)arg; + int i; + + if (req_chan >= priv->capability.channels) + { + printf("Invalid input requested: %d, valid: 0-%d\n", + req_chan, priv->capability.channels); + return(TVI_CONTROL_FALSE); + } + + for (i = 0; i < priv->capability.channels; i++) + { + if (priv->channels[i].channel == req_chan) + chan = priv->channels[i]; + } + + if (ioctl(priv->fd, VIDIOCSCHAN, &chan) == -1) + { + printf("ioctl set chan failed: %s\n", strerror(errno)); + return(TVI_CONTROL_FALSE); + } + printf("Using input '%s'\n", chan.name); + + /* update tuner state */ + if (priv->capability.type & VID_TYPE_TUNER) + control(priv, TVI_CONTROL_TUN_GET_TUNER, 0); + + /* update local channel list */ + control(priv, TVI_CONTROL_SPC_GET_INPUT, &req_chan); + return(TVI_CONTROL_TRUE); } } @@ -236,23 +641,33 @@ static int control(priv_t *priv, int cmd, void *arg) static int grab_video_frame(priv_t *priv, char *buffer, int len) { - priv->buf[0].frame = 0; - priv->buf[0].width = 320; - priv->buf[0].height = 240; - priv->buf[0].format = VIDEO_PALETTE_YUV422; - - if (ioctl(priv->fd, VIDIOCMCAPTURE, priv->buf) == -1) + int frame = priv->queue % priv->nbuf; + + printf("grab_video_frame(priv=%p, buffer=%p, len=%d\n", + priv, buffer, len); + + printf("buf: %p + frame: %d => %p\n", + priv->buf, frame, &priv->buf[frame]); + if (ioctl(priv->fd, VIDIOCMCAPTURE, &priv->buf[frame]) == -1) { - printf("grab_video_frame failed: %s\n", strerror(errno)); - return 0; + printf("ioctl mcapture failed: %s\n", strerror(errno)); + return(0); } - return 1; + ioctl(priv->fd, VIDIOCSYNC, &priv->buf[frame]); + priv->queue++; + + printf("mmap: %p + offset: %d => %p\n", + priv->mmap, priv->mbuf.offsets[frame], + priv->mmap+priv->mbuf.offsets[frame]); + memcpy(buffer, priv->mmap+priv->mbuf.offsets[frame], len); + + return(len); } static int get_video_framesize(priv_t *priv) { - return 65536; + return priv->bytesperline * priv->height; } static int grab_audio_frame(priv_t *priv, char *buffer, int len) |