[FFmpeg-devel] [PATCH] FireWire DV/HDV input device using libiec61883

Stefano Sabatini stefasab at gmail.com
Tue Apr 24 15:50:58 CEST 2012


On date Monday 2012-04-23 16:34:03 +0200, Georg Lippitsch encoded:
> Hi,
> 
> here is a patch that allows capturing from a DV or HDV camera via
> IEEE1394 using libiec61883.
> The current libavdevice/dv1394.c is outdated, because it does
> neither work with the new Linux-kernel FireWire stack, nor does it
> support HDV. The attached patch should fix that.
> 
> 
> Regards,
> 
> Georg

> From 47ba906fb63b9341384e5d916b1af4187fd75271 Mon Sep 17 00:00:00 2001
> From: Georg Lippitsch <georg.lippitsch at gmx.at>
> Date: Mon, 23 Apr 2012 16:01:17 +0200
> Subject: [PATCH] FireWire DV/HDV input device using libiec61883
> 
> ---
>  configure                |    3 +
>  libavdevice/Makefile     |    1 +
>  libavdevice/alldevices.c |    1 +
>  libavdevice/iec61883.c   |  370 ++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 375 insertions(+), 0 deletions(-)
>  create mode 100644 libavdevice/iec61883.c
> 
> diff --git a/configure b/configure
> index b3719f8..1723197 100755
> --- a/configure
> +++ b/configure
> @@ -1627,6 +1627,7 @@ dshow_indev_deps="IBaseFilter"
>  dshow_indev_extralibs="-lpsapi -lole32 -lstrmiids -luuid"
>  dv1394_indev_deps="dv1394 dv_demuxer"
>  fbdev_indev_deps="linux_fb_h"
> +iec61883_indev_deps="iec61883"
>  jack_indev_deps="jack_jack_h sem_timedwait"
>  lavfi_indev_deps="avfilter"
>  libcdio_indev_deps="libcdio"
> @@ -2764,6 +2765,7 @@ case $target_os in
>      linux)
>          add_cppflags -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600
>          enable dv1394
> +        enable iec61883
>          ;;
>      irix*)
>          target_os=irix
> @@ -3194,6 +3196,7 @@ enabled avisynth   && require2 vfw32 "windows.h vfw.h" AVIFileInit -lavifil32
>  enabled fontconfig && require_pkg_config fontconfig "fontconfig/fontconfig.h" FcInit
>  enabled frei0r     && { check_header frei0r.h || die "ERROR: frei0r.h header not found"; }
>  enabled gnutls     && require_pkg_config gnutls gnutls/gnutls.h gnutls_global_init
> +enabled iec61883   && require libiec61883 libiec61883/iec61883.h iec61883_cmp_connect -lraw1394 -lavc1394 -lrom1394 -liec61883
>  enabled libaacplus && require  "libaacplus >= 2.0.0" aacplus.h aacplusEncOpen -laacplus
>  enabled libass     && require_pkg_config libass ass/ass.h ass_library_init
>  enabled libbluray  && require libbluray libbluray/bluray.h bd_open -lbluray

Would you mind adding some generic information in doc/indevs.texi
about the device? Anything which is required to know in order to use
the device and one or more examples may fit well.

> diff --git a/libavdevice/Makefile b/libavdevice/Makefile
> index 7f0c1d3..3db43fa 100644
> --- a/libavdevice/Makefile
> +++ b/libavdevice/Makefile
> @@ -19,6 +19,7 @@ OBJS-$(CONFIG_DSHOW_INDEV)               += dshow.o dshow_enummediatypes.o \
>                                              dshow_pin.o dshow_common.o
>  OBJS-$(CONFIG_DV1394_INDEV)              += dv1394.o
>  OBJS-$(CONFIG_FBDEV_INDEV)               += fbdev.o
> +OBJS-$(CONFIG_IEC61883_INDEV)            += iec61883.o
>  OBJS-$(CONFIG_JACK_INDEV)                += jack_audio.o timefilter.o
>  OBJS-$(CONFIG_LAVFI_INDEV)               += lavfi.o
>  OBJS-$(CONFIG_OPENAL_INDEV)              += openal-dec.o
> diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
> index 86ebfee..2a0bffb 100644
> --- a/libavdevice/alldevices.c
> +++ b/libavdevice/alldevices.c
> @@ -43,6 +43,7 @@ void avdevice_register_all(void)
>      REGISTER_INDEV    (DSHOW, dshow);
>      REGISTER_INDEV    (DV1394, dv1394);
>      REGISTER_INDEV    (FBDEV, fbdev);
> +    REGISTER_INDEV    (IEC61883, iec61883);
>      REGISTER_INDEV    (JACK, jack);
>      REGISTER_INDEV    (LAVFI, lavfi);
>      REGISTER_INDEV    (OPENAL, openal);
> diff --git a/libavdevice/iec61883.c b/libavdevice/iec61883.c
> new file mode 100644
> index 0000000..f8e96fa
> --- /dev/null
> +++ b/libavdevice/iec61883.c
> @@ -0,0 +1,370 @@
> +
> +/*
> + * libiec61883 interface
> + * Copyright (c) 2012 Georg Lippitsch <georg.lippitsch at gmx.at>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg 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.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg 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 FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +

> +#include "avdevice.h"
> +#include "libavformat/dv.h"
> +#include "libavformat/mpegts.h"
> +#include "libavutil/opt.h"
> +#include <libavc1394/avc1394.h>
> +#include <libavc1394/rom1394.h>
> +#include <libiec61883/iec61883.h>
> +#include <libraw1394/raw1394.h>
> +#include <sys/poll.h>

Nit+: inverted order (from system, to libav* library to current dir is
preferred)

>
> +#define MOTDCT_SPEC_ID      0x00005068

> +#define IEC61883_AUTO       0
> +#define IEC61883_DV         1
> +#define IEC61883_HDV        2

Nit+: this could be an enum

> +
> +/**

> + * For DV, one packet correspondets exactly to one frame.

corresponds

> + * For HDV, these are MPEG2 transport stream packets.
> + * Note: A queue is implemented as linked list only for HDV.
> + * For DV, only one packet is allocated and re-used.
> + */

> +typedef struct DVPacket {
> +    uint8_t *buf;                       ///< Actual buffer data
> +    int len;                            ///< Size of buffer allocated
> +    int used;                           ///< Size actually used
> +    struct DVPacket *next;              ///< Next DVPacket
> +} DVPacket;

nit+: here and below, non complete sentences should not be
Capitalized, like in:

This is a complete sentence.
an incomplete sentence

> +
> +struct iec61883_data {
> +    AVClass *class;
> +    raw1394handle_t handle;             ///< Handle for libraw1394
> +    iec61883_dv_fb_t iec61883_dv;       ///< Handle for libiec61883 when used with DV
> +    iec61883_mpeg2_t iec61883_mpeg2;    ///< Handle for libiec61883 when used with HDV
> +
> +    DVDemuxContext *dv_demux;           ///< Generic DV muxing/demuxing context
> +    MpegTSContext *mpeg_demux;          ///< Generic HDV muxing/demuxing context
> +
> +    DVPacket *queue_first;              ///< First element of packet queue

> +    DVPacket *queue_last;               ///< Last element of packet quueue

typo: quueue 

> +
> +    int bandwidth;                      ///< Returned by libiec61883
> +    int buffersize;                     ///< Buffer size for libiec61883, set as an option
> +    int channel;                        ///< Returned by libiec61883
> +    int input_port;                     ///< Returned by libiec61883

> +    int isHDV;                          ///< Before connecting, find out if DV/HDV

Nit: camelStyle is avoided in libav*, snake_style is preferred

Also if I understand it correctly this is not a binary field, but a
type (auto/DV/HDV) (may slightly help understandibility)

> +    int node;                           ///< Returned by libiec61883
> +    int output_port;
> +
> +    struct pollfd raw1394_poll;         ///< To poll for new data from libraw1394
> +
> +    /** Parse function for DV/HDV differs, so this is set before packets arrive */
> +    int (*parse_queue)(struct iec61883_data *dv, AVPacket *pkt);
> +};
> +

> +static int iec61883_callback_dv(unsigned char *data, int length,
> +                                 int complete, void *callback_data)

weird indent

> +{
> +    struct iec61883_data *dv = callback_data;
> +
> +    DVPacket *packet = dv->queue_first;
> +
> +    if (!packet)
> +        return -1;
> +
> +    if (packet->len != length) {
> +        av_free(packet->buf);

> +        packet->buf = av_malloc(length);

missing NULL check?

> +        packet->len = length;
> +    }
> +
> +    memcpy(packet->buf, data, length);
> +    packet->used = length;
> +
> +    return 0;
> +}
> +
> +static int iec61883_callback_hdv(unsigned char *data, int length,
> +                                 unsigned int dropped, void *callback_data)
> +{
> +    struct iec61883_data *dv = callback_data;
> +
> +    DVPacket *packet;
> +
> +    packet = av_mallocz(sizeof(*packet));
> +    if (!packet)
> +        return -1;
> +
> +    packet->len = length;
> +    packet->used = length;
> +    packet->buf = av_malloc(length);

missing check?

> +    memcpy(packet->buf, data, length);
> +
> +    if (dv->queue_first) {
> +        dv->queue_last->next = packet;
> +        dv->queue_last = packet;
> +    } else {
> +        dv->queue_first = packet;
> +        dv->queue_last = packet;
> +    }
> +
> +    return 0;
> +}
>
> +static int iec61883_parse_queue_dv(struct iec61883_data *dv, AVPacket *pkt)
> +{
> +    DVPacket *packet;
> +    int size;
> +
> +    size = avpriv_dv_get_packet(dv->dv_demux, pkt);
> +    if (size > 0)
> +        return size;
> +
> +    packet = dv->queue_first;
> +    if (!packet || packet->used <= 0)
> +        return -1;
> +
> +    size = avpriv_dv_produce_packet(dv->dv_demux, pkt,
> +                                    packet->buf, packet->len, -1);
> +    packet->used = 0;
> +
> +    return size;
> +}
> +
> +static int iec61883_parse_queue_hdv(struct iec61883_data *dv, AVPacket *pkt)
> +{
> +    DVPacket *packet;
> +    int size;
> +

> +    while(dv->queue_first) {

nit++: while_(

> +        packet = dv->queue_first;
> +        size = ff_mpegts_parse_packet(dv->mpeg_demux, pkt, packet->buf,
> +                                      packet->len);
> +        dv->queue_first = packet->next;
> +        av_free(packet->buf);
> +        av_free(packet);
> +
> +        if (size > 0)
> +            return size;
> +    }
> +
> +    return -1;
> +}
> +
> +static int iec61883_read_header(AVFormatContext *context)
> +{
> +    struct iec61883_data *dv = context->priv_data;
> +    struct raw1394_portinfo pinf[16];
> +    rom1394_directory rom_dir;
> +    char *endptr;
> +    int inport;
> +    int ports;
> +    int port = -1;
> +    int response;
> +    int i, j = 0;
> +
> +    dv->input_port = -1;
> +    dv->output_port = -1;
> +    dv->channel = -1;
> +
> +    dv->handle = raw1394_new_handle();
> +
> +    if (!dv->handle) {
> +        av_log(context, AV_LOG_ERROR, "Failed to open IEEE1394 interface.\n");
> +        return AVERROR(EIO);
> +    }
> +
> +    if ((ports = raw1394_get_port_info(dv->handle, pinf, 16)) < 0) {
> +        av_log(context, AV_LOG_ERROR, "Failed to get number of IEEE1394 ports.\n");
> +        goto fail;
> +    }
> +
> +    inport = strtol(context->filename, &endptr, 10);
> +    if (endptr != context->filename && *endptr == '\0') {
> +        av_log(context, AV_LOG_INFO, "Selecting IEEE1394 port: %d\n", inport);
> +        j = inport;
> +        ports = inport + 1;
> +    }
> +

> +    /* Select first AV/C tape reccorder player node */

typo: reccorder

> +
> +    for (; j < ports && port==-1; ++j) {
> +        if (raw1394_set_port(dv->handle, j)) {
> +            av_log(context, AV_LOG_ERROR, "Failed setting IEEE1394 port.\n");
> +            goto fail;
> +        }
> +        for (i=0; i<raw1394_get_nodecount(dv->handle); ++i) {
> +            if (rom1394_get_directory(dv->handle, i, &rom_dir) < 0)
> +                continue;
> +            if (((rom1394_get_node_type(&rom_dir) == ROM1394_NODE_TYPE_AVC) &&
> +                 avc1394_check_subunit_type(dv->handle, i, AVC1394_SUBUNIT_TYPE_VCR)) ||
> +                (rom_dir.unit_spec_id == MOTDCT_SPEC_ID)) {
> +                rom1394_free_directory(&rom_dir);
> +                dv->node = i;
> +                port = j;
> +                break;
> +            }
> +            rom1394_free_directory(&rom_dir);
> +        }
> +    }
> +
> +    if (port == -1) {
> +        av_log(context, AV_LOG_ERROR, "No AV/C devices found.\n");
> +        goto fail;
> +    }
> +
> +    iec61883_cmp_normalize_output(dv->handle, 0xffc0 | dv->node);
> +
> +    /* Find out if device is DV or HDV */
> +
> +    if (!dv->isHDV) {
> +        response = avc1394_transaction(dv->handle, dv->node,
> +                                       AVC1394_CTYPE_STATUS |
> +                                       AVC1394_SUBUNIT_TYPE_TAPE_RECORDER |
> +                                       AVC1394_SUBUNIT_ID_0 |
> +                                       AVC1394_VCR_COMMAND_OUTPUT_SIGNAL_MODE |
> +                                       0xFF, 2);
> +        response = AVC1394_GET_OPERAND0(response);
> +        dv->isHDV = (response == 0x10 || response == 0x90 || response == 0x1A || response == 0x9A);
> +    } else
> +        dv->isHDV = dv->isHDV == IEC61883_HDV ? 1 : 0;
> +
> +    /* Connect to device, and do initialization */
> +
> +    dv->channel = iec61883_cmp_connect(dv->handle, dv->node, &dv->output_port,
> +                                       raw1394_get_local_id(dv->handle),
> +                                       &dv->input_port, &dv->bandwidth);
> +
> +    if (dv->channel < 0)
> +        dv->channel = 63;
> +
> +    if (dv->isHDV) {
> +        avformat_new_stream(context, NULL);
> +
> +        dv->mpeg_demux = ff_mpegts_parse_open(context);
> +        if (!dv->mpeg_demux)
> +            goto fail;
> +
> +        dv->parse_queue = iec61883_parse_queue_hdv;
> +
> +        dv->iec61883_mpeg2 = iec61883_mpeg2_recv_init(dv->handle, iec61883_callback_hdv, dv);
> +
> +        if (dv->buffersize)
> +            iec61883_mpeg2_set_buffers(dv->iec61883_mpeg2, dv->buffersize);
> +    } else {
> +        dv->dv_demux = avpriv_dv_init_demux(context);
> +        if (!dv->dv_demux)
> +            goto fail;
> +
> +        dv->queue_first = av_mallocz(sizeof(*dv->queue_first));
> +        dv->queue_last = dv->queue_first;
> +
> +        dv->parse_queue = iec61883_parse_queue_dv;
> +
> +        dv->iec61883_dv = iec61883_dv_fb_init(dv->handle, iec61883_callback_dv, dv);
> +    }
> +
> +    dv->raw1394_poll.fd = raw1394_get_fd(dv->handle);
> +    dv->raw1394_poll.events = POLLIN | POLLERR | POLLHUP | POLLPRI;
> +
> +    /* Actually start receiving */
> +
> +    if (dv->isHDV)
> +        iec61883_mpeg2_recv_start(dv->iec61883_mpeg2, dv->channel);
> +    else
> +        iec61883_dv_fb_start(dv->iec61883_dv, dv->channel);
> +
> +    return 0;
> +
> +fail:
> +    raw1394_destroy_handle(dv->handle);
> +    return AVERROR(EIO);
> +}
> +
> +static int iec61883_read_packet(AVFormatContext *context, AVPacket *pkt)
> +{
> +    struct iec61883_data *dv = context->priv_data;
> +    int size;
> +    int result;
> +
> +    /**
> +     * Try to parse frames from queue. If there are none,
> +     * poll for new data from the device, and try again.
> +     */
> +
> +    while ((size = dv->parse_queue(dv, pkt)) == -1) {
> +        while ((result = poll(&dv->raw1394_poll, 1, 200)) < 0) {
> +            if (!(errno == EAGAIN || errno == EINTR)) {

> +                av_log(context, AV_LOG_ERROR, "Raw1394 poll.\n");

"Raw1394 poll error occurred.\n"

Or you could store the error and print the corresponding description
to the output, I mean:
ret = ...
if ((!(ret == ...)
   av_log(context, ERROR, "Raw1394 poll error occurred: %s\n", strerror_r(errbuf, errbuf_size, err));

but maybe overkill

> +                return AVERROR(EIO);
> +            }
> +        }
> +        if (result > 0 && ((dv->raw1394_poll.revents & POLLIN)
> +                           || (dv->raw1394_poll.revents & POLLPRI)))
> +            raw1394_loop_iterate(dv->handle);
> +    }
> +
> +    return size;
> +}
> +
> +static int iec61883_close(AVFormatContext * context)
> +{
> +    struct iec61883_data *dv = context->priv_data;
> +
> +    if (dv->isHDV) {
> +        iec61883_mpeg2_recv_stop(dv->iec61883_mpeg2);
> +        iec61883_mpeg2_close(dv->iec61883_mpeg2);
> +    } else {
> +        iec61883_dv_fb_stop(dv->iec61883_dv);
> +        iec61883_dv_fb_close(dv->iec61883_dv);
> +        av_free(dv->queue_first->buf);
> +        av_free(dv->queue_first);
> +    }
> +
> +    iec61883_cmp_disconnect(dv->handle, dv->node, dv->output_port,
> +                            raw1394_get_local_id(dv->handle),
> +                            dv->input_port, dv->channel, dv->bandwidth);
> +
> +    raw1394_destroy_handle(dv->handle);
> +
> +    return 0;
> +}
> +
> +static const AVOption options[] = {
> +    { "dvtype", "Override autodetection of DV/HDV", offsetof(struct iec61883_data, isHDV), AV_OPT_TYPE_INT, {.dbl = IEC61883_AUTO}, IEC61883_AUTO, IEC61883_HDV, AV_OPT_FLAG_DECODING_PARAM, "dvtype" },

> +    { "auto",   "", 0, AV_OPT_TYPE_CONST, {.dbl = IEC61883_AUTO}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "dvtype" },
> +    { "dv",     "", 0, AV_OPT_TYPE_CONST, {.dbl = IEC61883_DV},   0, 0, AV_OPT_FLAG_DECODING_PARAM, "dvtype" },
> +    { "hdv" ,   "", 0, AV_OPT_TYPE_CONST, {.dbl = IEC61883_HDV},  0, 0, AV_OPT_FLAG_DECODING_PARAM, "dvtype" },

please add a short description

> +    { "hdvbuffer", "For HDV, buffer size (in packets) used by libiec61883", offsetof(struct iec61883_data, buffersize), AV_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },

Nit: "for HDV, set buffer size ..."

> +    { NULL },
> +};
> +
> +static const AVClass iec61883_class = {
> +    .class_name = "iec61883 indev",
> +    .item_name  = av_default_item_name,
> +    .option     = options,
> +    .version    = LIBAVUTIL_VERSION_INT,
> +};
> +

> +AVInputFormat ff_iec61883_demuxer = {
> +    .name           = "iec61883",
> +    .long_name      = NULL_IF_CONFIG_SMALL("libiec61883 (new DV1394) A/V grab"),

Nit, subjective: A/V grab => A/V input device
-- 
FFmpeg = Forgiving & Fast Multimedia Pitiful Exploitable Governor


More information about the ffmpeg-devel mailing list