/*
* Copyright ( c ) 2012 Georg Lippitsch < georg . lippitsch @ 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
*/
/**
* @ file
* libiec61883 interface
*/
# include "config_components.h"
# include <poll.h>
# include <libraw1394/raw1394.h>
# include <libavc1394/avc1394.h>
# include <libavc1394/rom1394.h>
# include <libiec61883/iec61883.h>
# include "libavformat/dv.h"
# include "libavformat/mpegts.h"
# include "libavutil/opt.h"
# include "avdevice.h"
# define THREADS HAVE_PTHREADS
# if THREADS
# include <pthread.h>
# endif
# define MOTDCT_SPEC_ID 0x00005068
# define IEC61883_AUTO 0
# define IEC61883_DV 1
# define IEC61883_HDV 2
/**
* For DV , one packet corresponds exactly to one frame .
* For HDV , these are MPEG2 transport stream packets .
* The queue is implemented as linked list .
*/
typedef struct DVPacket {
uint8_t * buf ; ///< actual buffer data
int len ; ///< size of buffer allocated
struct DVPacket * next ; ///< next DVPacket
} DVPacket ;
struct iec61883_data {
AVClass * class ;
raw1394handle_t raw1394 ; ///< 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 queue
char * device_guid ; ///< to select one of multiple DV devices
int packets ; ///< Number of packets queued
int max_packets ; ///< Max. number of packets in queue
int bandwidth ; ///< returned by libiec61883
int channel ; ///< returned by libiec61883
int input_port ; ///< returned by libiec61883
int type ; ///< Stream type, to distinguish DV/HDV
int node ; ///< returned by libiec61883
int output_port ; ///< returned by libiec61883
int thread_loop ; ///< Condition for thread while-loop
int receiving ; ///< True as soon data from device available
int receive_error ; ///< Set in receive task in case of error
int eof ; ///< True as soon as no more data available
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 ) ;
# if THREADS
pthread_t receive_task_thread ;
pthread_mutex_t mutex ;
pthread_cond_t cond ;
# endif
} ;
static int iec61883_callback ( unsigned char * data , int length ,
int complete , void * callback_data )
{
struct iec61883_data * dv = callback_data ;
DVPacket * packet ;
int ret ;
# if THREADS
pthread_mutex_lock ( & dv - > mutex ) ;
# endif
if ( dv - > packets > = dv - > max_packets ) {
av_log ( NULL , AV_LOG_ERROR , " DV packet queue overrun, dropping. \n " ) ;
ret = 0 ;
goto exit ;
}
packet = av_mallocz ( sizeof ( * packet ) ) ;
if ( ! packet ) {
ret = - 1 ;
goto exit ;
}
packet - > buf = av_malloc ( length + AV_INPUT_BUFFER_PADDING_SIZE ) ;
if ( ! packet - > buf ) {
av_free ( packet ) ;
ret = - 1 ;
goto exit ;
}
packet - > len = length ;
memcpy ( packet - > buf , data , length ) ;
memset ( packet - > buf + length , 0 , AV_INPUT_BUFFER_PADDING_SIZE ) ;
if ( dv - > queue_first ) {
dv - > queue_last - > next = packet ;
dv - > queue_last = packet ;
} else {
dv - > queue_first = packet ;
dv - > queue_last = packet ;
}
dv - > packets + + ;
ret = 0 ;
exit :
# if THREADS
pthread_cond_broadcast ( & dv - > cond ) ;
pthread_mutex_unlock ( & dv - > mutex ) ;
# endif
return ret ;
}
static void * iec61883_receive_task ( void * opaque )
{
struct iec61883_data * dv = ( struct iec61883_data * ) opaque ;
int result ;
# if THREADS
while ( dv - > thread_loop )
# endif
{
while ( ( result = poll ( & dv - > raw1394_poll , 1 , 200 ) ) < 0 ) {
if ( ! ( errno = = EAGAIN | | errno = = EINTR ) ) {
av_log ( NULL , AV_LOG_ERROR , " Raw1394 poll error occurred. \n " ) ;
dv - > receive_error = AVERROR ( EIO ) ;
return NULL ;
}
}
if ( result > 0 & & ( ( dv - > raw1394_poll . revents & POLLIN )
| | ( dv - > raw1394_poll . revents & POLLPRI ) ) ) {
dv - > receiving = 1 ;
raw1394_loop_iterate ( dv - > raw1394 ) ;
} else if ( dv - > receiving ) {
av_log ( NULL , AV_LOG_ERROR , " No more input data available \n " ) ;
# if THREADS
pthread_mutex_lock ( & dv - > mutex ) ;
dv - > eof = 1 ;
pthread_cond_broadcast ( & dv - > cond ) ;
pthread_mutex_unlock ( & dv - > mutex ) ;
# else
dv - > eof = 1 ;
# endif
return NULL ;
}
}
return NULL ;
}
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 )
return - 1 ;
size = avpriv_dv_produce_packet ( dv - > dv_demux , pkt ,
packet - > buf , packet - > len , - 1 ) ;
dv - > queue_first = packet - > next ;
if ( size < 0 )
av_free ( packet - > buf ) ;
av_free ( packet ) ;
dv - > packets - - ;
if ( size < 0 )
return - 1 ;
if ( av_packet_from_data ( pkt , pkt - > data , pkt - > size ) < 0 ) {
av_freep ( & pkt - > data ) ;
av_packet_unref ( pkt ) ;
return - 1 ;
}
return size ;
}
static int iec61883_parse_queue_hdv ( struct iec61883_data * dv , AVPacket * pkt )
{
# if CONFIG_MPEGTS_DEMUXER
DVPacket * packet ;
int size ;
while ( dv - > queue_first ) {
packet = dv - > queue_first ;
size = avpriv_mpegts_parse_packet ( dv - > mpeg_demux , pkt , packet - > buf ,
packet - > len ) ;
dv - > queue_first = packet - > next ;
av_freep ( & packet - > buf ) ;
av_freep ( & packet ) ;
dv - > packets - - ;
if ( size > 0 )
return size ;
}
# endif
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 nb_ports ;
int port = - 1 ;
int response ;
int i , j = 0 ;
uint64_t guid = 0 ;
dv - > input_port = - 1 ;
dv - > output_port = - 1 ;
dv - > channel = - 1 ;
dv - > raw1394 = raw1394_new_handle ( ) ;
if ( ! dv - > raw1394 ) {
av_log ( context , AV_LOG_ERROR , " Failed to open IEEE1394 interface. \n " ) ;
return AVERROR ( EIO ) ;
}
if ( ( nb_ports = raw1394_get_port_info ( dv - > raw1394 , pinf , 16 ) ) < 0 ) {
av_log ( context , AV_LOG_ERROR , " Failed to get number of IEEE1394 ports. \n " ) ;
goto fail ;
}
inport = strtol ( context - > url , & endptr , 10 ) ;
if ( endptr ! = context - > url & & * endptr = = ' \0 ' ) {
av_log ( context , AV_LOG_INFO , " Selecting IEEE1394 port: %d \n " , inport ) ;
j = inport ;
nb_ports = inport + 1 ;
} else if ( strcmp ( context - > url , " auto " ) ) {
av_log ( context , AV_LOG_ERROR , " Invalid input \" %s \" , you should specify "
" \" auto \" for auto-detection, or the port number. \n " , context - > url ) ;
goto fail ;
}
if ( dv - > device_guid ) {
if ( sscanf ( dv - > device_guid , " % " SCNu64 , & guid ) ! = 1 ) {
av_log ( context , AV_LOG_INFO , " Invalid dvguid parameter: %s \n " ,
dv - > device_guid ) ;
goto fail ;
}
}
for ( ; j < nb_ports & & port = = - 1 ; + + j ) {
raw1394_destroy_handle ( dv - > raw1394 ) ;
if ( ! ( dv - > raw1394 = raw1394_new_handle_on_port ( j ) ) ) {
av_log ( context , AV_LOG_ERROR , " Failed setting IEEE1394 port. \n " ) ;
goto fail ;
}
for ( i = 0 ; i < raw1394_get_nodecount ( dv - > raw1394 ) ; + + i ) {
/* Select device explicitly by GUID */
if ( guid > 1 ) {
if ( guid = = rom1394_get_guid ( dv - > raw1394 , i ) ) {
dv - > node = i ;
port = j ;
break ;
}
} else {
/* Select first AV/C tape recorder player node */
if ( rom1394_get_directory ( dv - > raw1394 , i , & rom_dir ) < 0 )
continue ;
if ( ( ( rom1394_get_node_type ( & rom_dir ) = = ROM1394_NODE_TYPE_AVC ) & &
avc1394_check_subunit_type ( dv - > raw1394 , 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 ;
}
/* Provide bus sanity for multiple connections */
iec61883_cmp_normalize_output ( dv - > raw1394 , 0xffc0 | dv - > node ) ;
/* Find out if device is DV or HDV */
if ( dv - > type = = IEC61883_AUTO ) {
response = avc1394_transaction ( dv - > raw1394 , 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 - > type = ( response = = 0x10 | | response = = 0x90 | | response = = 0x1A | | response = = 0x9A ) ?
IEC61883_HDV : IEC61883_DV ;
}
/* Connect to device, and do initialization */
dv - > channel = iec61883_cmp_connect ( dv - > raw1394 , dv - > node , & dv - > output_port ,
raw1394_get_local_id ( dv - > raw1394 ) ,
& dv - > input_port , & dv - > bandwidth ) ;
if ( dv - > channel < 0 )
dv - > channel = 63 ;
if ( ! dv - > max_packets )
dv - > max_packets = 100 ;
if ( CONFIG_MPEGTS_DEMUXER & & dv - > type = = IEC61883_HDV ) {
/* Init HDV receive */
avformat_new_stream ( context , NULL ) ;
dv - > mpeg_demux = avpriv_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 - > raw1394 ,
( iec61883_mpeg2_recv_t ) iec61883_callback ,
dv ) ;
dv - > max_packets * = 766 ;
} else {
/* Init DV receive */
dv - > dv_demux = avpriv_dv_init_demux ( context ) ;
if ( ! dv - > dv_demux )
goto fail ;
dv - > parse_queue = iec61883_parse_queue_dv ;
dv - > iec61883_dv = iec61883_dv_fb_init ( dv - > raw1394 , iec61883_callback , dv ) ;
}
dv - > raw1394_poll . fd = raw1394_get_fd ( dv - > raw1394 ) ;
dv - > raw1394_poll . events = POLLIN | POLLERR | POLLHUP | POLLPRI ;
/* Actually start receiving */
if ( dv - > type = = IEC61883_HDV )
iec61883_mpeg2_recv_start ( dv - > iec61883_mpeg2 , dv - > channel ) ;
else
iec61883_dv_fb_start ( dv - > iec61883_dv , dv - > channel ) ;
# if THREADS
dv - > thread_loop = 1 ;
if ( pthread_mutex_init ( & dv - > mutex , NULL ) )
goto fail ;
if ( pthread_cond_init ( & dv - > cond , NULL ) )
goto fail ;
if ( pthread_create ( & dv - > receive_task_thread , NULL , iec61883_receive_task , dv ) )
goto fail ;
# endif
return 0 ;
fail :
raw1394_destroy_handle ( dv - > raw1394 ) ;
return AVERROR ( EIO ) ;
}
static int iec61883_read_packet ( AVFormatContext * context , AVPacket * pkt )
{
struct iec61883_data * dv = context - > priv_data ;
int size ;
/**
* Try to parse frames from queue
*/
# if THREADS
pthread_mutex_lock ( & dv - > mutex ) ;
while ( ( size = dv - > parse_queue ( dv , pkt ) ) = = - 1 )
if ( ! dv - > eof )
pthread_cond_wait ( & dv - > cond , & dv - > mutex ) ;
else
break ;
pthread_mutex_unlock ( & dv - > mutex ) ;
# else
int result ;
while ( ( size = dv - > parse_queue ( dv , pkt ) ) = = - 1 ) {
iec61883_receive_task ( ( void * ) dv ) ;
if ( dv - > receive_error )
return dv - > receive_error ;
}
# endif
return size ;
}
static int iec61883_close ( AVFormatContext * context )
{
struct iec61883_data * dv = context - > priv_data ;
# if THREADS
dv - > thread_loop = 0 ;
pthread_join ( dv - > receive_task_thread , NULL ) ;
pthread_cond_destroy ( & dv - > cond ) ;
pthread_mutex_destroy ( & dv - > mutex ) ;
# endif
if ( CONFIG_MPEGTS_DEMUXER & & dv - > type = = IEC61883_HDV ) {
iec61883_mpeg2_recv_stop ( dv - > iec61883_mpeg2 ) ;
iec61883_mpeg2_close ( dv - > iec61883_mpeg2 ) ;
avpriv_mpegts_parse_close ( dv - > mpeg_demux ) ;
} else {
iec61883_dv_fb_stop ( dv - > iec61883_dv ) ;
iec61883_dv_fb_close ( dv - > iec61883_dv ) ;
av_freep ( & dv - > dv_demux ) ;
}
while ( dv - > queue_first ) {
DVPacket * packet = dv - > queue_first ;
dv - > queue_first = packet - > next ;
av_freep ( & packet - > buf ) ;
av_freep ( & packet ) ;
}
iec61883_cmp_disconnect ( dv - > raw1394 , dv - > node , dv - > output_port ,
raw1394_get_local_id ( dv - > raw1394 ) ,
dv - > input_port , dv - > channel , dv - > bandwidth ) ;
raw1394_destroy_handle ( dv - > raw1394 ) ;
return 0 ;
}
static const AVOption options [ ] = {
{ " dvtype " , " override autodetection of DV/HDV " , offsetof ( struct iec61883_data , type ) , AV_OPT_TYPE_INT , { . i64 = IEC61883_AUTO } , IEC61883_AUTO , IEC61883_HDV , AV_OPT_FLAG_DECODING_PARAM , " dvtype " } ,
{ " auto " , " auto detect DV/HDV " , 0 , AV_OPT_TYPE_CONST , { . i64 = IEC61883_AUTO } , 0 , 0 , AV_OPT_FLAG_DECODING_PARAM , " dvtype " } ,
{ " dv " , " force device being treated as DV device " , 0 , AV_OPT_TYPE_CONST , { . i64 = IEC61883_DV } , 0 , 0 , AV_OPT_FLAG_DECODING_PARAM , " dvtype " } ,
{ " hdv " , " force device being treated as HDV device " , 0 , AV_OPT_TYPE_CONST , { . i64 = IEC61883_HDV } , 0 , 0 , AV_OPT_FLAG_DECODING_PARAM , " dvtype " } ,
{ " dvbuffer " , " set queue buffer size (in packets) " , offsetof ( struct iec61883_data , max_packets ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , INT_MAX , AV_OPT_FLAG_DECODING_PARAM } ,
{ " dvguid " , " select one of multiple DV devices by its GUID " , offsetof ( struct iec61883_data , device_guid ) , AV_OPT_TYPE_STRING , { . str = NULL } , 0 , 0 , AV_OPT_FLAG_DECODING_PARAM } ,
{ NULL } ,
} ;
static const AVClass iec61883_class = {
. class_name = " iec61883 indev " ,
. item_name = av_default_item_name ,
. option = options ,
. version = LIBAVUTIL_VERSION_INT ,
. category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT ,
} ;
const AVInputFormat ff_iec61883_demuxer = {
. name = " iec61883 " ,
. long_name = NULL_IF_CONFIG_SMALL ( " libiec61883 (new DV1394) A/V input device " ) ,
. priv_data_size = sizeof ( struct iec61883_data ) ,
. read_header = iec61883_read_header ,
. read_packet = iec61883_read_packet ,
. read_close = iec61883_close ,
. flags = AVFMT_NOFILE ,
. priv_class = & iec61883_class ,
} ;