Add MPEG2-TS/HDV support to fwcontrol.

Submitted by: Petr Holub" <hopet@ics.muni.cz>
Clean up by: mi@
Pr: 98134
This commit is contained in:
Warner Losh 2006-10-26 22:33:38 +00:00
parent 3750d1ecad
commit 6d815a7d8c
6 changed files with 464 additions and 56 deletions

View File

@ -1,8 +1,9 @@
# $FreeBSD$
PROG= fwcontrol
SRCS= fwcontrol.c fwcrom.c fwdv.c
SRCS= fwcontrol.c fwcrom.c fwdv.c fwmpegts.c
MAN= fwcontrol.8
WARNS= 3
.PATH: ${.CURDIR}/../../sys/dev/firewire

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd December 14, 2004
.Dd October 24, 2006
.Dt FWCONTROL 8
.Os
.Sh NAME
@ -41,6 +41,7 @@
.Op Fl l Ar file
.Op Fl g Ar gap_count
.Op Fl b Ar pri_req
.Op Fl M Ar mode
.Op Fl R Ar filename
.Op Fl S Ar filename
.Op Fl m Ar EUI64 | hostname
@ -84,8 +85,18 @@ by phy_config packet.
Set the
.Dv PRIORITY_BUDGET
register on all supported nodes.
.It Fl M Ar mode
Explicitly specify either
.Ar dv
or
.Ar mpeg
mode for the incoming stream.
Only meaningful in case of and must preceed the
.Fl R
option. If not specified, the program will try to guess. If you get
an error complaining about "format 0x20", try to force the "mpeg" mode.
.It Fl R Ar filename
Receive DV stream and dump it to a file.
Receive DV or MPEG TS stream and dump it to a file.
Use Ctrl-C to stop the receiving.
Some DV cameras seem not to send the stream if a bus manager exits.
If you cannot get the stream, try the following commands:
@ -100,7 +111,14 @@ It can be handled by
.Nm libdv
in the
.Fx
Ports Collection.
Ports Collection. Resulting MPEG TS stream can be played and sent over a
network using the VideoLAN
.Nm vlc
tool in the
.Fx
Ports Collection. The stream can be piped directly to
.Nm vlc,
see EXAMPLES.
.It Fl S Ar filename
Send a DV file as isochronous stream.
.It Fl m Ar EUI64 | hostname
@ -117,7 +135,7 @@ Each DV frame has a fixed size and it is easy to edit the frame order.
.Pp
.Dl "fwcontrol -R original.dv"
.Pp
Receive stream.
Receive a DV stream with DV camera attached.
.Pp
.Dl "dd if=original.dv of=first.dv bs=120000 count=30"
.Pp
@ -135,12 +153,42 @@ For PAL, replace
.Dq Li bs=120000
with
.Dq Li bs=144000 .
.Pp
.Dl "fwcontrol -R file.m2t
.Pp
Receive an MPEG TS stream from a camera producing MPEG transport stream. This
has been tested with SONY HDR-FX1E camera that produces HD MPEG-2 stream at
25 Mbps bandwidth.
.Pp
To send the stream from the camera over the network using TCP (which supprisingly works better with vlc), you can use
.Dl "fwcontrol -R - | nc 192.168.10.11 9000
with
.Nm netcat
from ports and to receive the stream, use
.Dl nc -l -p 9000 | vlc -
.Pp
To netcast via UDP, you need to use
.Nm buffer
program from ports, since vlc is not fast enough to read UDP packets from
buffers and thus it experiences dropouts when run directly. The sending side
can use
.Dl "fwcontrol -R - | nc 192.168.10.11 9000
and to receive the stream, use
.Dl nc -l -u -p 9000 | buffer -s 10k -b 1000 -m 20m -p 5 | vlc -
.Pp
.Pp
For more information on how to work with
.Nm vlc
see its docs.
.Sh SEE ALSO
.Xr firewire 4 ,
.Xr fwe 4 ,
.Xr fwip 4 ,
.Xr fwohci 4 ,
.Xr sbp 4
.Xr sbp 4 ,
.Xr mplayer 1 ,
.Xr vlc 1
.Sh HISTORY
The
.Nm
@ -148,5 +196,9 @@ utility first appeared in
.Fx 5.0 .
.Sh AUTHORS
.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org
.An Petr Holub Aq hopet@ics.muni.cz
- MPEG TS mode.
.Sh BUGS
This utility is still under development and provided for debugging purposes.
Especially MPEG TS reception support is very rudimental and supports only
high-bandwidth MPEG-2 streams (fn field in CIP header equals 3).

View File

@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#include <dev/firewire/firewire.h>
#include <dev/firewire/iec13213.h>
#include <dev/firewire/fwphyreg.h>
#include <dev/firewire/iec68113.h>
#include <netinet/in.h>
#include <fcntl.h>
@ -53,12 +54,11 @@ __FBSDID("$FreeBSD$");
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include "fwmethods.h"
extern int dvrecv(int, char *, char, int);
extern int dvsend(int, char *, char, int);
int sysctl_set_int(const char *, int);
static void sysctl_set_int(const char *, int);
static void
usage(void)
@ -77,10 +77,10 @@ usage(void)
"\t-t: read topology map\n"
"\t-d: hex dump of configuration ROM\n"
"\t-l: load and parse hex dump file of configuration ROM\n"
"\t-R: Receive DV stream\n"
"\t-R: Receive DV or MPEG TS stream\n"
"\t-S: Send DV stream\n"
"\t-m: set fwmem target\n");
exit(0);
exit(EX_USAGE);
}
static void
@ -276,7 +276,7 @@ reset_start(int fd, int node)
}
static void
set_pri_req(int fd, int pri_req)
set_pri_req(int fd, u_int32_t pri_req)
{
struct fw_devlstreq *data;
struct fw_devinfo *devinfo;
@ -296,7 +296,7 @@ set_pri_req(int fd, int pri_req)
eui64_ntoa(&eui, addr, sizeof(addr));
printf("%d %s, %08x",
devinfo->dst, addr, reg);
if (reg > 0 && pri_req >= 0) {
if (reg > 0) {
old = (reg & 0x3f);
max = (reg & 0x3f00) >> 8;
if (pri_req > max)
@ -311,7 +311,7 @@ set_pri_req(int fd, int pri_req)
}
static void
parse_bus_info_block(u_int32_t *p, int info_len)
parse_bus_info_block(u_int32_t *p)
{
char addr[EUI64_SIZ];
struct bus_info *bi;
@ -392,7 +392,7 @@ show_crom(u_int32_t *crom_buf)
printf("(OK)\n");
else
printf("(NG)\n");
parse_bus_info_block(crom_buf+1, hdr->info_len);
parse_bus_info_block(crom_buf+1);
crom_init_context(&cc, crom_buf);
dir = cc.stack[0].dir;
@ -603,21 +603,71 @@ open_dev(int *fd, char *devbase)
}
}
int
static void
sysctl_set_int(const char *name, int val)
{
if (sysctlbyname(name, NULL, NULL, &val, sizeof(int)) < 0)
err(1, "sysctl %s failed.", name);
}
static fwmethod *
detect_recv_fn(int fd, char ich)
{
char *buf;
struct fw_isochreq isoreq;
struct fw_isobufreq bufreq;
int len;
u_int32_t *ptr;
struct ciphdr *ciph;
fwmethod *retfn;
bufreq.rx.nchunk = 8;
bufreq.rx.npacket = 16;
bufreq.rx.psize = 1024;
bufreq.tx.nchunk = 0;
bufreq.tx.npacket = 0;
bufreq.tx.psize = 0;
if (ioctl(fd, FW_SSTBUF, &bufreq) < 0)
err(1, "ioctl FW_SSTBUF");
isoreq.ch = ich & 0x3f;
isoreq.tag = (ich >> 6) & 3;
if (ioctl(fd, FW_SRSTREAM, &isoreq) < 0)
err(1, "ioctl FW_SRSTREAM");
buf = (char *)malloc(1024*16);
len = read(fd, buf, 1024*16);
ptr = (u_int32_t *) buf;
ciph = (struct ciphdr *)(ptr + 1);
switch(ciph->fmt) {
case CIP_FMT_DVCR:
fprintf(stderr, "Detected DV format on input.\n");
retfn = dvrecv;
break;
case CIP_FMT_MPEG:
fprintf(stderr, "Detected MPEG TS format on input.\n");
retfn = mpegtsrecv;
break;
default:
errx(1, "Unsupported format for receiving: fmt=0x%x", ciph->fmt);
}
free(buf);
return retfn;
}
int
main(int argc, char **argv)
{
u_int32_t crom_buf[1024/4];
char devbase[1024] = "/dev/fw0";
int fd, tmp, ch, len=1024;
int fd, ch, len=1024;
long tmp;
struct fw_eui64 eui;
struct eui64 target;
fwmethod *recvfn = NULL;
fd = -1;
@ -626,10 +676,12 @@ main(int argc, char **argv)
list_dev(fd);
}
while ((ch = getopt(argc, argv, "g:m:o:s:b:prtc:d:l:u:R:S:")) != -1)
while ((ch = getopt(argc, argv, "M:g:m:o:s:b:prtc:d:l:u:R:S:")) != -1)
switch(ch) {
case 'b':
tmp = strtol(optarg, NULL, 0);
if (tmp < 0 || tmp > (long)0xffffffff)
errx(EX_USAGE, "invalid number: %s", optarg);
open_dev(&fd, devbase);
set_pri_req(fd, tmp);
break;
@ -657,7 +709,7 @@ main(int argc, char **argv)
case 'm':
if (eui64_hostton(optarg, &target) != 0 &&
eui64_aton(optarg, &target) != 0)
errx(1, "invalid target: %s", optarg);
errx(EX_USAGE, "invalid target: %s", optarg);
eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
sysctl_set_int("hw.firewire.fwmem.eui64_hi", eui.hi);
@ -688,7 +740,7 @@ main(int argc, char **argv)
break;
case 'u':
tmp = strtol(optarg, NULL, 0);
snprintf(devbase, sizeof(devbase), "/dev/fw%d", tmp);
snprintf(devbase, sizeof(devbase), "/dev/fw%ld", tmp);
if (fd > 0) {
close(fd);
fd = -1;
@ -700,9 +752,27 @@ main(int argc, char **argv)
break;
#define TAG (1<<6)
#define CHANNEL 63
case 'M':
switch (optarg[0]) {
case 'm':
recvfn = mpegtsrecv;
break;
case 'd':
recvfn = dvrecv;
break;
default:
errx(EX_USAGE, "unrecognized method: %s",
optarg);
}
break;
case 'R':
open_dev(&fd, devbase);
dvrecv(fd, optarg, TAG | CHANNEL, -1);
if (recvfn == NULL) /* guess... */
recvfn = detect_recv_fn(fd, TAG | CHANNEL);
close(fd);
fd = -1;
open_dev(&fd, devbase);
(*recvfn)(fd, optarg, TAG | CHANNEL, -1);
break;
case 'S':
open_dev(&fd, devbase);

View File

@ -1,7 +1,7 @@
/*
* Copyright (C) 2003
* Hidetoshi Shimokawa. All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -18,7 +18,7 @@
* 4. Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@ -30,7 +30,7 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
* $FreeBSD$
*/
#include <sys/param.h>
@ -50,15 +50,18 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <dev/firewire/firewire.h>
#include <dev/firewire/iec68113.h>
#include "fwmethods.h"
#define DEBUG 0
#define FIX_FRAME 1
struct frac {
int n,d;
int n,d;
};
struct frac frame_cycle[2] = {
@ -88,8 +91,8 @@ int frame_rate[] = {30, 25};
#define MAXBLOCKS (300)
#define CYCLE_FRAC 0xc00
int
dvrecv(int d, char *filename, char ich, int count)
void
dvrecv(int d, const char *filename, char ich, int count)
{
struct fw_isochreq isoreq;
struct fw_isobufreq bufreq;
@ -102,9 +105,15 @@ dvrecv(int d, char *filename, char ich, int count)
int nblocks[] = {250 /* NTSC */, 300 /* PAL */};
struct iovec wbuf[NPACKET_R];
fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660);
buf = (char *)malloc(RBUFSIZE);
pad = (char *)malloc(DSIZE*MAXBLOCKS);
if(strcmp(filename, "-") == 0) {
fd = STDOUT_FILENO;
} else {
fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660);
if (fd == -1)
err(EX_NOINPUT, filename);
}
buf = malloc(RBUFSIZE);
pad = malloc(DSIZE*MAXBLOCKS);
memset(pad, 0xff, DSIZE*MAXBLOCKS);
bzero(wbuf, sizeof(wbuf));
@ -114,14 +123,13 @@ dvrecv(int d, char *filename, char ich, int count)
bufreq.tx.nchunk = 0;
bufreq.tx.npacket = 0;
bufreq.tx.psize = 0;
if (ioctl(d, FW_SSTBUF, &bufreq) < 0) {
err(1, "ioctl");
}
if (ioctl(d, FW_SSTBUF, &bufreq) < 0)
err(1, "ioctl FW_SSTBUF");
isoreq.ch = ich & 0x3f;
isoreq.tag = (ich >> 6) & 3;
if( ioctl(d, FW_SRSTREAM, &isoreq) < 0)
if (ioctl(d, FW_SRSTREAM, &isoreq) < 0)
err(1, "ioctl");
k = m = 0;
@ -147,7 +155,7 @@ dvrecv(int d, char *filename, char ich, int count)
tlen = len = read(d, buf, RBUFSIZE);
if (len < 0) {
if (errno == EAGAIN) {
fprintf(stderr, "(EAGAIN)\n");
fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
fflush(stderr);
if (len <= 0)
continue;
@ -158,7 +166,7 @@ dvrecv(int d, char *filename, char ich, int count)
vec = 0;
ptr = (u_int32_t *) buf;
again:
pkt = (struct fw_pkt *) ptr;
pkt = (struct fw_pkt *) ptr;
#if DEBUG
fprintf(stderr, "%08x %08x %08x %08x\n",
htonl(ptr[0]), htonl(ptr[1]),
@ -186,7 +194,7 @@ dvrecv(int d, char *filename, char ich, int count)
if (dv->sct == DV_SCT_HEADER && dv->dseq == 0) {
if (system < 0) {
system = ciph->fdf.dv.fs;
printf("%s\n", system_name[system]);
fprintf(stderr, "%s\n", system_name[system]);
}
/* Fix DSF bit */
@ -237,21 +245,22 @@ dvrecv(int d, char *filename, char ich, int count)
if (vec > 0)
writev(fd, wbuf, vec);
}
close(fd);
if(fd != STDOUT_FILENO) {
close(fd);
}
fprintf(stderr, "\n");
return 0;
}
int
dvsend(int d, char *filename, char ich, int count)
void
dvsend(int d, const char *filename, char ich, int count)
{
struct fw_isochreq isoreq;
struct fw_isobufreq bufreq;
struct dvdbc *dv;
struct fw_pkt *pkt;
int len, tlen, header, fd, frames, packets, vec, offset, nhdr, i;
int system=-1, pad_acc, cycle_acc, cycle, f_cycle, f_frac;
int system=-1, pad_acc, cycle_acc, cycle, f_cycle, f_frac;
struct iovec wbuf[TNBUF*2 + NEMPTY];
char *pbuf;
u_int32_t iso_data, iso_empty, hdr[TNBUF + NEMPTY][3];
@ -260,7 +269,10 @@ dvsend(int d, char *filename, char ich, int count)
double rtime;
fd = open(filename, O_RDONLY);
pbuf = (char *)malloc(DSIZE * TNBUF);
if (fd == -1)
err(EX_NOINPUT, filename);
pbuf = malloc(DSIZE * TNBUF);
bzero(wbuf, sizeof(wbuf));
bufreq.rx.nchunk = 0;
@ -269,15 +281,14 @@ dvsend(int d, char *filename, char ich, int count)
bufreq.tx.nchunk = NCHUNK;
bufreq.tx.npacket = NPACKET_T;
bufreq.tx.psize = PSIZE;
if (ioctl(d, FW_SSTBUF, &bufreq) < 0) {
err(1, "ioctl");
}
if (ioctl(d, FW_SSTBUF, &bufreq) < 0)
err(1, "ioctl FW_SSTBUF");
isoreq.ch = ich & 0x3f;
isoreq.tag = (ich >> 6) & 3;
if( ioctl(d, FW_STSTREAM, &isoreq) < 0)
err(1, "ioctl");
if (ioctl(d, FW_STSTREAM, &isoreq) < 0)
err(1, "ioctl FW_STSTREAM");
iso_data = 0;
pkt = (struct fw_pkt *) &iso_data;
@ -298,9 +309,8 @@ dvsend(int d, char *filename, char ich, int count)
ciph->eoh1 = 1;
ciph->fdf.dv.cyc = 0xffff;
for (i = 1; i < TNBUF; i++) {
for (i = 1; i < TNBUF; i++)
bcopy(hdr[0], hdr[i], sizeof(hdr[0]));
}
gettimeofday(&start, NULL);
#if DEBUG
@ -320,7 +330,7 @@ dvsend(int d, char *filename, char ich, int count)
if (len < 0)
warn("read");
else
printf("\nend of file\n");
fprintf(stderr, "\nend of file\n");
goto send_end;
}
tlen += len;
@ -391,8 +401,7 @@ dvsend(int d, char *filename, char ich, int count)
len = writev(d, wbuf, vec);
if (len < 0) {
if (errno == EAGAIN) {
fprintf(stderr, "(EAGAIN)\n");
fflush(stderr);
fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
goto again;
}
err(1, "write failed");
@ -402,9 +411,8 @@ dvsend(int d, char *filename, char ich, int count)
fprintf(stderr, "\n");
send_end:
gettimeofday(&end, NULL);
rtime = end.tv_sec - start.tv_sec
rtime = end.tv_sec - start.tv_sec
+ (end.tv_usec - start.tv_usec) * 1e-6;
fprintf(stderr, "%d frames, %.2f secs, %.2f frames/sec\n",
frames, rtime, frames/rtime);
return 0;
}

View File

@ -0,0 +1,10 @@
/*-
* This file is in the public domain.
*
* $FreeBSD$
*/
typedef void (fwmethod)(int dev_fd, const char *filename, char ich, int count);
extern fwmethod dvrecv;
extern fwmethod dvsend;
extern fwmethod mpegtsrecv;

View File

@ -0,0 +1,267 @@
/*
* Copyright (C) 2005
* Petr Holub, Hidetoshi Shimokawa. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
*
* This product includes software developed by Hidetoshi Shimokawa.
*
* 4. Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#if __FreeBSD_version >= 500000
#include <arpa/inet.h>
#endif
#include <err.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <dev/firewire/firewire.h>
#include <dev/firewire/iec68113.h>
#include "fwmethods.h"
#define DEBUG 0
/*****************************************************************************
MPEG-2 Transport Stream (MPEG TS) packet format according to IEC 61883:
31 15 0
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ --------
| len |tag| channel | tcode | sy |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1394
| header_CRC |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ --------
|0|0| sid | dbs |fn | qpc |S|RSV| dbc |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ CIP
|1|0| fmt | fdf | fdf/syt |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ --------
| reserved | cycle_count | cycle_offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | N x
. . MPEG
. MPEG TS payload 188 bytes .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ --------
| data_CRC |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
N.b. that CRCs are removed by firewire layer!
The following fiels are fixed for IEEE-1394:
tag = 01b
tcode = 1010b
The length is payload length, i.e. includes CIP header and data size.
The following fields are constant for MPEG TS:
sph = 1 (denoted as S in CIP header above)
dbs = 6
fmt = (1<<5)
fdf = reserved
In the supported streams we also require
qpc = 0
fn = 3
and thus the payload is divided in 8 blocks as follows:
+-----+-----+-----+-----+-----+-----+-----+-----+
| db0 | db1 | db2 | db3 | db4 | db5 | db6 | db7 |
+-----+-----+-----+-----+-----+-----+-----+-----+
We have several cases of payload distribution based on stream
bandwidth (R):
1) R < 1.5 Mbps: any of db0..db7 may be payload,
2) 1.5 < R < 3 Mbps: db0/db1 or db2/db3 or db4/db5 or db6/db7 is payload,
3) 3 < R < 6 Mbps: db0/db1/db2/db3 or db4/db5/db6/db7 is payload,
4) R > 6 Mbps: all db0..db7 contain the payload.
Curently, only case (4) is supported in fwmpegts.c
Each packet may contain N MPEG TS data blocks with timestamp header,
which are (4+188)B long. Experimentally, the N ranges from 0 through 3.
*****************************************************************************/
typedef uint8_t mpeg_ts_pld[188];
struct mpeg_pldt {
#if BYTE_ORDER == BIG_ENDIAN
uint32_t :7,
c_count:13,
c_offset:12;
#else /* BYTE_ORDER != BIG_ENDIAN */
uint32_t c_offset:12,
c_count:13,
:7;
#endif /* BYTE_ORDER == BIG_ENDIAN */
mpeg_ts_pld payload;
};
#define NCHUNK 8
#define PSIZE 596
#define NPACKET_R 4096
#define RBUFSIZE (PSIZE * NPACKET_R)
void
mpegtsrecv(int d, const char *filename, char ich, int count)
{
struct ciphdr *ciph;
struct fw_isochreq isoreq;
struct fw_isobufreq bufreq;
struct fw_pkt *pkt;
struct mpeg_pldt *pld;
uint32_t *ptr;
int fd, k, len, m, pkt_size, startwr, tlen;
char *buf;
startwr = 0;
if (strcmp(filename, "-") == 0)
fd = STDOUT_FILENO;
else {
fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660);
if (fd == -1)
err(EX_NOINPUT, filename);
}
buf = malloc(RBUFSIZE);
bufreq.rx.nchunk = NCHUNK;
bufreq.rx.npacket = NPACKET_R;
bufreq.rx.psize = PSIZE;
bufreq.tx.nchunk = 0;
bufreq.tx.npacket = 0;
bufreq.tx.psize = 0;
if (ioctl(d, FW_SSTBUF, &bufreq) < 0)
err(1, "ioctl");
isoreq.ch = ich & 0x3f;
isoreq.tag = (ich >> 6) & 3;
if (ioctl(d, FW_SRSTREAM, &isoreq) < 0)
err(1, "ioctl");
k = m = 0;
while (count <= 0 || k <= count) {
len = tlen = read(d, buf, RBUFSIZE);
#if DEBUG
fprintf(stderr, "Read %d bytes.\n", len);
#endif /* DEBUG */
if (len < 0) {
if (errno == EAGAIN) {
fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
if (len <= 0)
continue;
} else
err(1, "read failed");
}
ptr = (uint32_t *) buf;
do {
pkt = (struct fw_pkt *) ptr;
#if DEBUG
fprintf(stderr, "\nReading new packet.\n");
fprintf(stderr, "%08x %08x %08x %08x\n",
htonl(ptr[0]), htonl(ptr[1]),
htonl(ptr[2]), htonl(ptr[3]));
#endif /* DEBUG */
/* there is no CRC in the 1394 header */
ciph = (struct ciphdr *)(ptr + 1); /* skip iso header */
if (ciph->fmt != CIP_FMT_MPEG)
errx(1, "unknown format 0x%x", ciph->fmt);
if (ciph->fn != 3) {
errx(1,
"unsupported MPEG TS stream, fn=%d (only fn=3 is supported)",
ciph->fn);
}
ptr = (uint32_t *) (ciph + 1); /* skip cip header */
if (pkt->mode.stream.len <= sizeof(struct ciphdr)) {
/* no payload */
/* tlen needs to be decremented before end of the loop */
goto next;
}
#if DEBUG
else {
fprintf(stderr,
"Packet net payload length (IEEE1394 header): %d\n",
pkt->mode.stream.len - sizeof(struct ciphdr));
fprintf(stderr, "Data block size (CIP header): %d [q], %d [B]\n",
ciph->len, ciph->len * 4);
fprintf(stderr,
"Data fraction number (CIP header): %d => DBC increments with %d\n",
ciph->fn, (1<<ciph->fn) );
fprintf(stderr, "QCP (CIP header): %d\n", ciph->qpc );
fprintf(stderr, "DBC counter (CIP header): %d\n", ciph->dbc );
fprintf(stderr, "MPEG payload type size: %d\n",
sizeof(struct mpeg_pldt));
}
#endif /* DEBUG */
/* This is a condition that needs to be satisfied to start
writing the data */
if (ciph->dbc % (1<<ciph->fn) == 0)
startwr = 1;
/* Read out all the MPEG TS data blocks from current packet */
for (pld = (struct mpeg_pldt *)ptr;
(intptr_t)pld < (intptr_t)((char *)ptr +
pkt->mode.stream.len - sizeof(struct ciphdr));
pld++) {
if (startwr == 1)
write(fd, pld->payload,
sizeof(pld->payload));
}
next:
/* CRCs are removed from both header and trailer
so that only 4 bytes of 1394 header remains */
pkt_size = pkt->mode.stream.len + 4;
ptr = (uint32_t *)((intptr_t)pkt + pkt_size);
tlen -= pkt_size;
} while (tlen > 0);
#if DEBUG
fprintf(stderr, "\nReading a data from firewire.\n");
#endif /* DEBUG */
}
if (fd != STDOUT_FILENO)
close(fd);
fprintf(stderr, "\n");
}