/* mpg1394grab.c v0.1.0 -- a program to capture IEC 61883-4 streams
 *
 * Copyright (C) 2003 Dan Dennedy <dan@dennedy.org>
 *  
 *  Build:
 *  % gcc -lraw1394 -o mpg1394grab mpg1394grab.c -D_FILE_OFFSET_BITS=64
 *  
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
 

/* standard system includes */
#include <sys/poll.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>

/* linux1394 includes */
#include <libraw1394/raw1394.h>

/* constant for the iso receive channel */
#define RX_CHANNEL 63
/* constant for the iso receive packet buffer size */
#define RAW_BUF_SIZE 4096

/* global vars */
unsigned char g_rx_packet[RAW_BUF_SIZE]; /* the received packet data */
int g_rx_length; /* the size of a received packet */
int g_alldone = 0; /* flag to indicate when to quit */
int g_rx_channel = RX_CHANNEL;


/*************************************************************************/
/* raw1394 section - reception                                           */
/*************************************************************************/

/* a callback function executed by libraw1394 when a packet is received. */
/* iso packet header is included */
/* libraw1394 has its own handle type */
int raw_iso_handler(raw1394handle_t handle, int channel, size_t length,
                    quadlet_t *data)
{
    /* is this a packet for me? */
    if (length < RAW_BUF_SIZE && channel == g_rx_channel) {
        //fprintf(stderr, "received a packet with length %03d (%08x) (%08x)\n", length-4, *(unsigned long*)(data+3),*(unsigned long*)(data+4));
        g_rx_length = length;
        memcpy(g_rx_packet, data, length);
    }
    return 0;
}

/* libraw1394 executes this when there is a bus reset. We'll just keep it 
   simple and quit */
int reset_handler(raw1394handle_t handle, unsigned int generation)
{
	raw1394_update_generation(handle, generation);
    g_alldone = 1;
    return 0;
}


raw1394handle_t open_raw1394(void)
{
    int numcards;
    struct raw1394_portinfo pinf[16];
    raw1394handle_t handle;
    struct pollfd raw1394_poll;

    if (!(handle = raw1394_new_handle())) {
		perror("raw1394 - couldn't get handle");
		fprintf(stderr, "This error usually means that the ieee1394 driver is not loaded or that /dev/raw1394 does not exist.\n");
		exit( -1);
    }

    if ((numcards = raw1394_get_port_info(handle, pinf, 16)) < 0) {
		perror("raw1394 - couldn't get card info");
		exit( -1);
    }

    /* port 0 is the first host adapter card */
    if (raw1394_set_port(handle, 0) < 0) {
		perror("raw1394 - couldn't set port");
		exit( -1);
    }
    
    /* tell libraw1394 the names of our callback functions */
    raw1394_set_iso_handler(handle, g_rx_channel, raw_iso_handler);
    raw1394_set_bus_reset_handler(handle, reset_handler);

    /* poll for leftover events */
    raw1394_poll.fd = raw1394_get_fd(handle);
    raw1394_poll.events = POLLIN;
	while(1) {
	   if ( poll( &raw1394_poll, 1, 10) < 1 )
		   break;
	   raw1394_loop_iterate(handle);
	}

    /* Starting iso receive */
    if (raw1394_start_iso_rcv(handle, g_rx_channel) < 0) {
            perror("raw1394 - couldn't start iso receive");
            exit( -1);
    }
    return handle;
}


void close_raw1394(raw1394handle_t handle)
{
    raw1394_stop_iso_rcv(handle, g_rx_channel);
    raw1394_destroy_handle(handle);
}


/* this is a common unix function that gets called when a process 
   receives a signal (e.g. ctrl-c) */
void signal_handler(int sig)
{
    /* replace this signal handler with the default (which aborts) */
    signal(SIGINT, SIG_DFL);
	
    /* setting these variables will let us fall out of the main loop */
    g_alldone = 1;
}


int main(int argc, const char** argv)
{
    raw1394handle_t handle;
	unsigned short dbs_fn_qpc_sph;
	unsigned char fmt;
    struct pollfd raw1394_poll;
        
    signal(SIGINT, signal_handler);

    handle = open_raw1394();
    
    /* initialize the poll control structure */
    raw1394_poll.fd = raw1394_get_fd(handle);
    raw1394_poll.events = POLLIN;
	
	/* the main loop */
    while (g_alldone == 0) {
	    
        /* check for pending events before using raw1394 */
        if ( poll( &raw1394_poll, 1, 10) > 0 ) {
            if (raw1394_poll.revents & POLLIN) {
        
                /* wait for a packet to arrive */
                /* printf("waiting to receive...\n"); */
                raw1394_loop_iterate(handle);

				/* check various fields of CIP header for valid packet */
				dbs_fn_qpc_sph = (htonl(*(unsigned long*)(g_rx_packet+4)) >> 10) & 0x3fff;
				fmt = (htonl(*(unsigned long*)(g_rx_packet+8)) >> 24) & 0x3f;
				if (g_rx_length > 188 && fmt == 0x20 && dbs_fn_qpc_sph == 0x01b1) {
					
					unsigned char *data = g_rx_packet + 16; /* skip over iso header, CIP header, and SPH */
					size_t len = g_rx_length;
					
					/* write each TSP in the iso packet minus SPH */
					for ( ; len > 188; len -= 192, data += 192 )
						if ( fwrite( data, 188, 1, stdout ) != 1 ) {
							g_alldone = 1;
							break;
						}
				}
			}
		}
    }

    close_raw1394(handle);
    fprintf(stderr, "quitting\n");
    return EXIT_SUCCESS;
}


