changed audio and video grab interface (simpler now)
authorFabrice Bellard <fabrice@bellard.org>
Mon, 24 Sep 2001 23:27:06 +0000 (23:27 +0000)
committerFabrice Bellard <fabrice@bellard.org>
Mon, 24 Sep 2001 23:27:06 +0000 (23:27 +0000)
Originally committed as revision 148 to svn://svn.ffmpeg.org/ffmpeg/trunk

libav/audio.c
libav/avio.h
libav/grab.c

index 4ff54bf..2f4ef75 100644 (file)
 
 const char *audio_device = "/dev/dsp";
 
+#define AUDIO_BLOCK_SIZE 4096
+
 typedef struct {
     int fd;
-    int rate;
+    int sample_rate;
     int channels;
+    int frame_size; /* in bytes ! */
+    int codec_id;
+    UINT8 buffer[AUDIO_BLOCK_SIZE];
+    int buffer_ptr;
 } AudioData;
 
-#define AUDIO_BLOCK_SIZE 4096
-
-/* audio read support */
-
-static int audio_read(URLContext *h, UINT8 *buf, int size)
-{
-    AudioData *s = h->priv_data;
-    int ret;
-
-    ret = read(s->fd, buf, size);
-    if (ret < 0)
-        return -errno;
-    else
-        return ret;
-}
-
-static int audio_write(URLContext *h, UINT8 *buf, int size)
-{
-    AudioData *s = h->priv_data;
-    int ret;
-
-    ret = write(s->fd, buf, size);
-    if (ret < 0)
-        return -errno;
-    else
-        return ret;
-}
-
-static int audio_get_format(URLContext *h, URLFormat *f)
-{
-    AudioData *s = h->priv_data;
-
-    strcpy(f->format_name, "pcm");
-    f->sample_rate = s->rate;
-    f->channels = s->channels;
-    return 0;
-}
-
-/* URI syntax: 'audio:[rate[,channels]]' 
-   default: rate=44100, channels=2
- */
-static int audio_open(URLContext *h, const char *uri, int flags)
+static int audio_open(AudioData *s, int is_output)
 {
-    AudioData *s;
-    const char *p;
-    int freq, channels, audio_fd;
+    int audio_fd;
     int tmp, err;
 
-    h->is_streamed = 1;
-    h->packet_size = AUDIO_BLOCK_SIZE;
-
-    s = malloc(sizeof(AudioData));
-    if (!s)
-        return -ENOMEM;
-    h->priv_data = s;
-
-    /* extract parameters */
-    p = uri;
-    strstart(p, "audio:", &p);
-    freq = strtol(p, (char **)&p, 0);
-    if (freq <= 0)
-        freq = 44100;
-    if (*p == ',')
-        p++;
-    channels = strtol(p, (char **)&p, 0);
-    if (channels <= 0)
-            channels = 2;
-    s->rate = freq;
-    s->channels = channels;
-    
     /* open linux audio device */
-    if (flags & URL_WRONLY) 
-        audio_fd = open(audio_device,O_WRONLY);
+    if (is_output)
+        audio_fd = open(audio_device, O_WRONLY);
     else
-        audio_fd = open(audio_device,O_RDONLY);
+        audio_fd = open(audio_device, O_RDONLY);
     if (audio_fd < 0) {
         perror(audio_device);
         return -EIO;
@@ -119,60 +60,233 @@ static int audio_open(URLContext *h, const char *uri, int flags)
     /* non blocking mode */
     fcntl(audio_fd, F_SETFL, O_NONBLOCK);
 
+    s->frame_size = AUDIO_BLOCK_SIZE;
 #if 0
-    tmp=(NB_FRAGMENTS << 16) | FRAGMENT_BITS;
-    err=ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &tmp);
+    tmp = (NB_FRAGMENTS << 16) | FRAGMENT_BITS;
+    err = ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &tmp);
     if (err < 0) {
         perror("SNDCTL_DSP_SETFRAGMENT");
     }
 #endif
 
-    tmp=AFMT_S16_LE;
-    err=ioctl(audio_fd,SNDCTL_DSP_SETFMT,&tmp);
+    /* select format : favour native format */
+    err = ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &tmp);
+    
+#ifdef WORDS_BIGENDIAN
+    if (tmp & AFMT_S16_BE) {
+        tmp = AFMT_S16_BE;
+    } else if (tmp & AFMT_S16_LE) {
+        tmp = AFMT_S16_LE;
+    } else {
+        tmp = 0;
+    }
+#else
+    if (tmp & AFMT_S16_LE) {
+        tmp = AFMT_S16_LE;
+    } else if (tmp & AFMT_S16_BE) {
+        tmp = AFMT_S16_BE;
+    } else {
+        tmp = 0;
+    }
+#endif
+
+    switch(tmp) {
+    case AFMT_S16_LE:
+        s->codec_id = CODEC_ID_PCM_S16LE;
+        break;
+    case AFMT_S16_BE:
+        s->codec_id = CODEC_ID_PCM_S16BE;
+        break;
+    default:
+        fprintf(stderr, "Soundcard does not support 16 bit sample format\n");
+        close(audio_fd);
+        return -EIO;
+    }
+    err=ioctl(audio_fd, SNDCTL_DSP_SETFMT, &tmp);
     if (err < 0) {
         perror("SNDCTL_DSP_SETFMT");
         goto fail;
     }
     
-    tmp= (channels == 2);
-    err=ioctl(audio_fd,SNDCTL_DSP_STEREO,&tmp);
+    tmp = (s->channels == 2);
+    err = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
     if (err < 0) {
         perror("SNDCTL_DSP_STEREO");
         goto fail;
     }
     
-    tmp = freq;
-    err=ioctl(audio_fd, SNDCTL_DSP_SPEED, &tmp);
+    tmp = s->sample_rate;
+    err = ioctl(audio_fd, SNDCTL_DSP_SPEED, &tmp);
     if (err < 0) {
         perror("SNDCTL_DSP_SPEED");
         goto fail;
     }
-
-    s->rate = tmp;
+    s->sample_rate = tmp; /* store real sample rate */
     s->fd = audio_fd;
 
     return 0;
  fail:
     close(audio_fd);
-    free(s);
     return -EIO;
 }
 
-static int audio_close(URLContext *h)
+static int audio_close(AudioData *s)
 {
-    AudioData *s = h->priv_data;
-
     close(s->fd);
+    return 0;
+}
+
+/* sound output support */
+static int audio_write_header(AVFormatContext *s1)
+{
+    AudioData *s;
+    AVStream *st;
+    int ret;
+
+    s = av_mallocz(sizeof(AudioData));
+    if (!s)
+        return -ENOMEM;
+    s1->priv_data = s;
+
+    st = s1->streams[0];
+    s->sample_rate = st->codec.sample_rate;
+    s->channels = st->codec.channels;
+    ret = audio_open(s, 1);
+    if (ret < 0) {
+        free(s);
+        return -EIO;
+    } else {
+        return 0;
+    }
+}
+
+static int audio_write_packet(AVFormatContext *s1, int stream_index,
+                              UINT8 *buf, int size)
+{
+    AudioData *s = s1->priv_data;
+    int len, ret;
+
+    while (size > 0) {
+        len = AUDIO_BLOCK_SIZE - s->buffer_ptr;
+        if (len > size)
+            len = size;
+        memcpy(s->buffer + s->buffer_ptr, buf, len);
+        s->buffer_ptr += len;
+        if (s->buffer_ptr >= AUDIO_BLOCK_SIZE) {
+            for(;;) {
+                ret = write(s->fd, s->buffer, AUDIO_BLOCK_SIZE);
+                if (ret != 0)
+                    break;
+                if (ret < 0 && (errno != EAGAIN && errno != EINTR))
+                    return -EIO;
+            }
+            s->buffer_ptr = 0;
+        }
+        buf += len;
+        size -= len;
+    }
+    return 0;
+}
+
+static int audio_write_trailer(AVFormatContext *s1)
+{
+    AudioData *s = s1->priv_data;
+
+    audio_close(s);
     free(s);
     return 0;
 }
 
-URLProtocol audio_protocol = {
-    "audio",
-    audio_open,
-    audio_read,
-    audio_write,
-    NULL, /* seek */
-    audio_close,
-    audio_get_format,
+/* grab support */
+
+static int audio_read_header(AVFormatContext *s1, AVFormatParameters *ap)
+{
+    AudioData *s;
+    AVStream *st;
+    int ret;
+
+    if (!ap || ap->sample_rate <= 0 || ap->channels <= 0)
+        return -1;
+
+    s = av_mallocz(sizeof(AudioData));
+    if (!s)
+        return -ENOMEM;
+    st = av_mallocz(sizeof(AVStream));
+    if (!st) {
+        free(s);
+        return -ENOMEM;
+    }
+    s1->priv_data = s;
+    s1->nb_streams = 1;
+    s1->streams[0] = st;
+    s->sample_rate = ap->sample_rate;
+    s->channels = ap->channels;
+
+    ret = audio_open(s, 0);
+    if (ret < 0) {
+        free(st);
+        free(s);
+        return -EIO;
+    } else {
+        /* take real parameters */
+        st->codec.codec_type = CODEC_TYPE_AUDIO;
+        st->codec.codec_id = s->codec_id;
+        st->codec.sample_rate = s->sample_rate;
+        st->codec.channels = s->channels;
+        return 0;
+    }
+}
+
+static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
+{
+    AudioData *s = s1->priv_data;
+    int ret;
+
+    if (av_new_packet(pkt, s->frame_size) < 0)
+        return -EIO;
+    for(;;) {
+        ret = read(s->fd, pkt->data, pkt->size);
+        if (ret > 0)
+            break;
+        if (!(ret == 0 || (ret == -1 && (errno == EAGAIN || errno == EINTR)))) {
+            av_free_packet(pkt);
+            return -EIO;
+        }
+    }
+    pkt->size = ret;
+    return 0;
+}
+
+static int audio_read_close(AVFormatContext *s1)
+{
+    AudioData *s = s1->priv_data;
+
+    audio_close(s);
+    free(s);
+    return 0;
+}
+
+AVFormat audio_device_format = {
+    "audio_device",
+    "audio grab and output",
+    "",
+    "",
+    /* XXX: we make the assumption that the soundcard accepts this format */
+    /* XXX: find better solution with "preinit" method, needed also in
+       other formats */
+#ifdef WORDS_BIGENDIAN
+    CODEC_ID_PCM_S16BE,
+#else
+    CODEC_ID_PCM_S16LE,
+#endif
+    CODEC_ID_NONE,
+    audio_write_header,
+    audio_write_packet,
+    audio_write_trailer,
+
+    audio_read_header,
+    audio_read_packet,
+    audio_read_close,
+    NULL,
+    AVFMT_NOFILE,
 };
index 7d67171..faa2b54 100644 (file)
@@ -141,11 +141,3 @@ extern URLProtocol udp_protocol;
 
 /* http.c */
 extern URLProtocol http_protocol;
-
-/* audio.c */
-extern const char *audio_device;
-extern URLProtocol audio_protocol;
-
-/* grab.c */
-extern const char *v4l_device;
-extern URLProtocol video_protocol;
index 6203d83..774c5b0 100644 (file)
@@ -30,8 +30,9 @@ typedef struct {
     int frame_format; /* see VIDEO_PALETTE_xxx */
     int use_mmap;
     int width, height;
-    float rate;
+    int frame_rate;
     INT64 time_frame;
+    int frame_size;
 } VideoData;
 
 const char *v4l_device = "/dev/video";
@@ -45,20 +46,41 @@ static struct video_mmap gb_buf;
 static struct video_audio audio, audio_saved;
 static int gb_frame = 0;
 
-static int v4l_init(URLContext *h)
+static int grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
 {
-    VideoData *s = h->priv_data;
+    VideoData *s;
+    AVStream *st;
     int width, height;
-    int ret;
     int video_fd, frame_size;
+    int ret, frame_rate;
+
+    if (!ap || ap->width <= 0 || ap->height <= 0 || ap->frame_rate <= 0)
+        return -1;
     
-    width = s->width;
-    height = s->height;
+    width = ap->width;
+    height = ap->height;
+    frame_rate = ap->frame_rate;
+
+    s = av_mallocz(sizeof(VideoData));
+    if (!s)
+        return -ENOMEM;
+    st = av_mallocz(sizeof(AVStream));
+    if (!st) {
+        free(s);
+        return -ENOMEM;
+    }
+    s1->priv_data = s;
+    s1->nb_streams = 1;
+    s1->streams[0] = st;
+
+    s->width = width;
+    s->height = height;
+    s->frame_rate = frame_rate;
 
     video_fd = open(v4l_device, O_RDWR);
     if (video_fd < 0) {
         perror(v4l_device);
-        return -EIO;
+        goto fail;
     }
     
     if (ioctl(video_fd,VIDIOCGCAP,&video_cap) < 0) {
@@ -166,27 +188,38 @@ static int v4l_init(URLContext *h)
     switch(s->frame_format) {
     case VIDEO_PALETTE_YUV420P:
         frame_size = (width * height * 3) / 2;
+        st->codec.pix_fmt = PIX_FMT_YUV420P;
         break;
     case VIDEO_PALETTE_YUV422:
         frame_size = width * height * 2;
+        st->codec.pix_fmt = PIX_FMT_YUV422;
         break;
     case VIDEO_PALETTE_RGB24:
         frame_size = width * height * 3;
+        st->codec.pix_fmt = PIX_FMT_BGR24; /* NOTE: v4l uses BGR24, not RGB24 ! */
         break;
     default:
         goto fail;
     }
     s->fd = video_fd;
-    h->packet_size = frame_size;
+    s->frame_size = frame_size;
+    
+    st->codec.codec_id = CODEC_ID_RAWVIDEO;
+    st->codec.width = width;
+    st->codec.height = height;
+    st->codec.frame_rate = frame_rate;
+
     return 0;
  fail:
-    close(video_fd);
+    if (video_fd >= 0)
+        close(video_fd);
+    free(st);
+    free(s);
     return -EIO;
 }
 
-static int v4l_mm_read_picture(URLContext *h, UINT8 *buf)
+static int v4l_mm_read_picture(VideoData *s, UINT8 *buf)
 {
-    VideoData *s = h->priv_data;
     UINT8 *ptr;
 
     gb_buf.frame = gb_frame;
@@ -203,105 +236,44 @@ static int v4l_mm_read_picture(URLContext *h, UINT8 *buf)
            (errno == EAGAIN || errno == EINTR));
 
     ptr = video_buf + gb_buffers.offsets[gb_frame];
-    memcpy(buf, ptr, h->packet_size);
-    return h->packet_size;
+    memcpy(buf, ptr, s->frame_size);
+    return s->frame_size;
 }
 
-/* note: we support only one picture read at a time */
-static int video_read(URLContext *h, UINT8 *buf, int size)
+static int grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
 {
-    VideoData *s = h->priv_data;
-    INT64 curtime;
-
-    if (size != h->packet_size)
-        return -EINVAL;
+    VideoData *s = s1->priv_data;
+    INT64 curtime, delay;
+    struct timespec ts;
 
     /* wait based on the frame rate */
-    s->time_frame += (int)(1000000 / s->rate);
-    do {
+    s->time_frame += (INT64_C(1000000) * FRAME_RATE_BASE) / s->frame_rate;
+    for(;;) {
         curtime = gettime();
-    } while (curtime < s->time_frame);
+        delay = s->time_frame - curtime;
+        if (delay <= 0)
+            break;
+        ts.tv_sec = delay / 1000000;
+        ts.tv_nsec = (delay % 1000000) * 1000;
+        nanosleep(&ts, NULL);
+    }
+
+    if (av_new_packet(pkt, s->frame_size) < 0)
+        return -EIO;
 
     /* read one frame */
     if (s->use_mmap) {
-        return v4l_mm_read_picture(h, buf);
+        return v4l_mm_read_picture(s, pkt->data);
     } else {
-        if (read(s->fd, buf, size) != size)
+        if (read(s->fd, pkt->data, pkt->size) != pkt->size)
             return -EIO;
-        return h->packet_size;
+        return s->frame_size;
     }
 }
 
-static int video_get_format(URLContext *h, URLFormat *f)
+static int grab_read_close(AVFormatContext *s1)
 {
-    VideoData *s = h->priv_data;
-
-    f->width = s->width;
-    f->height = s->height;
-    f->frame_rate = (int)(s->rate * FRAME_RATE_BASE);
-    strcpy(f->format_name, "rawvideo");
-
-    switch(s->frame_format) {
-    case VIDEO_PALETTE_YUV420P:
-        f->pix_fmt = PIX_FMT_YUV420P;
-        break;
-    case VIDEO_PALETTE_YUV422:
-        f->pix_fmt = PIX_FMT_YUV422;
-        break;
-    case VIDEO_PALETTE_RGB24:
-        f->pix_fmt = PIX_FMT_BGR24; /* NOTE: v4l uses BGR24, not RGB24 ! */
-        break;
-    default:
-        abort();
-    }
-    return 0;
-}
-
-/* URI syntax: 'video:width,height,rate'
- */
-static int video_open(URLContext *h, const char *uri, int flags)
-{
-    VideoData *s;
-    const char *p;
-    int width, height;
-    int ret;
-    float rate;
-
-    /* extract parameters */
-    p = uri;
-    strstart(p, "video:", &p);
-    width = strtol(p, (char **)&p, 0);
-    if (width <= 0)
-        return -EINVAL;
-    if (*p == ',')
-        p++;
-    height = strtol(p, (char **)&p, 0);
-    if (height <= 0)
-        return -EINVAL;
-    if (*p == ',')
-        p++;
-    rate = strtod(p, (char **)&p);
-    if (rate <= 0)
-        return -EINVAL;
-
-    s = malloc(sizeof(VideoData));
-    if (!s)
-        return -ENOMEM;
-    h->priv_data = s;
-    h->is_streamed = 1;
-    s->width = width;
-    s->height = height;
-    s->rate = rate;
-    ret = v4l_init(h);
-    if (ret)
-        free(s);
-    return ret;
-}
-
-static int video_close(URLContext *h)
-{
-    VideoData *s = h->priv_data;
-
+    VideoData *s = s1->priv_data;
     /* restore audio settings */
     ioctl(s->fd, VIDIOCSAUDIO, &audio_saved);
 
@@ -310,12 +282,20 @@ static int video_close(URLContext *h)
     return 0;
 }
 
-URLProtocol video_protocol = {
-    "video",
-    video_open,
-    video_read,
+AVFormat video_grab_device_format = {
+    "video_grab_device",
+    "video grab",
+    "",
+    "",
+    CODEC_ID_NONE,
+    CODEC_ID_NONE,
+    NULL,
+    NULL,
+    NULL,
+
+    grab_read_header,
+    grab_read_packet,
+    grab_read_close,
     NULL,
-    NULL, /* seek */
-    video_close,
-    video_get_format,
+    AVFMT_NOFILE,
 };