[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