From 67c0355c1e8c65cfedc497008572d77e7a58c949 Mon Sep 17 00:00:00 2001 From: simokawa Date: Thu, 23 Jan 2003 16:10:12 +0000 Subject: Merge DV(Digital Video) support. --- usr.sbin/fwcontrol/Makefile | 2 +- usr.sbin/fwcontrol/fwcontrol.8 | 34 ++++ usr.sbin/fwcontrol/fwcontrol.c | 17 +- usr.sbin/fwcontrol/fwdv.c | 399 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 449 insertions(+), 3 deletions(-) create mode 100644 usr.sbin/fwcontrol/fwdv.c (limited to 'usr.sbin') diff --git a/usr.sbin/fwcontrol/Makefile b/usr.sbin/fwcontrol/Makefile index 725ee18..f25ff95 100644 --- a/usr.sbin/fwcontrol/Makefile +++ b/usr.sbin/fwcontrol/Makefile @@ -1,7 +1,7 @@ # $FreeBSD$ PROG= fwcontrol -SRCS= fwcontrol.c fwcrom.c +SRCS= fwcontrol.c fwcrom.c fwdv.c MAN= fwcontrol.8 .include diff --git a/usr.sbin/fwcontrol/fwcontrol.8 b/usr.sbin/fwcontrol/fwcontrol.8 index d29d2dd..c4b3118 100644 --- a/usr.sbin/fwcontrol/fwcontrol.8 +++ b/usr.sbin/fwcontrol/fwcontrol.8 @@ -39,6 +39,8 @@ .Op Fl l Ar file .Op Fl g Ar gap_count .Op Fl b Ar pri_req +.Op Fl R Ar filename +.Op Fl S Ar filename .Sh DESCRIPTION The .Nm @@ -61,7 +63,39 @@ Load hex dump file of the configuration ROM and parse it. Broadcast gap_count by phy_config packet. .It Fl i Ar pri_req Set PRIORITY_BUDGET register on all supported nodes. +.It Fl R Ar filename +Receive DV stream and dump it to a file. Use Ctl-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. +.Bd -literal -offset indent +sysctl hw.firewire.try_bmr=0; fwcontrol -r +.Ed +.It Fl S Ar filename +Send a DV file as isochronus stream. .El +.Sh EXAMPLES +Each DV frame has fixed size and it is easy to edit those frame order. +.Bd -literal -offset indent +fwcontrol -R original.dv +.Ed +.Pp +Receive stream. +.Bd -literal -offset indent +dd if=original.dv of=first.dv bs=120000 count=30 +.Ed +.Pp +Get first 30 frames(NTSC). +.Bd -literal -offset indent +dd if=original.dv of=second.dv bs=120000 skip=30 count=30 +.Ed +.Pp +Get second 30 frames(NTSC). +.Bd -literal -offset indent +cat second.dv first.dv | fwcontrol -S /dev/stdin +.Ed +.Pp +Swap first and second 30 frames and send them to DV recorder. +For PAL, replace 'bs=120000' with 'bs=144000'. .Sh FILES .Bl -tag -width indent diff --git a/usr.sbin/fwcontrol/fwcontrol.c b/usr.sbin/fwcontrol/fwcontrol.c index c11ef4e..fe13a2c 100644 --- a/usr.sbin/fwcontrol/fwcontrol.c +++ b/usr.sbin/fwcontrol/fwcontrol.c @@ -50,11 +50,14 @@ #include #include +extern int dvrecv(int, char *, char, int); +extern int dvsend(int, char *, char, int); + static void usage(void) { printf("fwcontrol [-g gap_count] [-b pri_req] [-c node]" - " [-r] [-t] [-d node] [-l file]\n"); + " [-r] [-t] [-d node] [-l file] [-R file] [-S file]\n"); printf("\t-g: broadcast gap_count by phy_config packet\n"); printf("\t-b: set PRIORITY_BUDGET register on all supported nodes\n"); printf("\t-c: read configuration ROM\n"); @@ -62,6 +65,8 @@ usage(void) printf("\t-t: read topology map\n"); printf("\t-d: hex dump of configuration ROM\n"); printf("\t-l: load and parse hex dump file of configuration ROM\n"); + printf("\t-R: Receive DV stream\n"); + printf("\t-S: Send DV stream\n"); exit(0); } @@ -353,7 +358,7 @@ main(int argc, char **argv) usage(); } - while ((ch = getopt(argc, argv, "g:b:rtc:d:l:")) != -1) + while ((ch = getopt(argc, argv, "g:b:rtc:d:l:R:S:")) != -1) switch(ch) { case 'g': /* gap count */ @@ -385,6 +390,14 @@ main(int argc, char **argv) load_crom(optarg, crom_buf); show_crom(crom_buf); break; +#define TAG (1<<6) +#define CHANNEL 63 + case 'R': + dvrecv(fd, optarg, TAG | CHANNEL, -1); + break; + case 'S': + dvsend(fd, optarg, TAG | CHANNEL, -1); + break; default: usage(); } diff --git a/usr.sbin/fwcontrol/fwdv.c b/usr.sbin/fwcontrol/fwdv.c new file mode 100644 index 0000000..9ec7be6 --- /dev/null +++ b/usr.sbin/fwcontrol/fwdv.c @@ -0,0 +1,399 @@ +/* + * 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: + * 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 +#include +#include +#include +#include + +#if __FreeBSD_version >= 500000 +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +struct frac { + int n,d; +}; + +struct frac frame_cycle[2] = { + {8000*100, 2997}, /* NTSC 8000 cycle / 29.97 Hz */ + {320, 1}, /* PAL 8000 cycle / 25 Hz */ +}; +int npackets[] = { + 250 /* NTSC */, + 300 /* PAL */ +}; +struct frac pad_rate[2] = { + {203, 2997}, /* = (8000 - 29.97 * 250)/(29.97 * 250) */ + {1, 15}, /* = (8000 - 25 * 300)/(25 * 300) */ +}; + +#define PSIZE 512 +#define DSIZE 480 +#define NVEC 50 +#define BUFSIZE (PSIZE * 256) +#define BLOCKSIZE 80 +#define MAXBLOCKS (300*6) +#define CYCLE_FRAC 0xc00 + +int dvrecv(int d, char *filename, char ich, int count) +{ + struct fw_isochreq isoreq; + struct fw_isobufreq bufreq; + struct dvdbc *dv; + struct ciphdr *ciph; + struct fw_pkt *pkt; + char *pad, *buf; + u_int32_t *ptr; + int len, npad, fd, k, m, vec, pal, nb; + int nblocks[] = {250*6 /* NTSC */, 300*6 /* PAL */}; + struct iovec wbuf[NVEC]; + struct iovec *v; + + fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660); + buf = (char *)malloc(BUFSIZE); + pad = (char *)malloc(BLOCKSIZE*MAXBLOCKS); + bzero(pad, BLOCKSIZE*MAXBLOCKS); + bzero(wbuf, sizeof(wbuf)); + + bufreq.rx.nchunk = 16; + bufreq.rx.npacket = 256; + 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) { + vec = 0; + wbuf[0].iov_len = 0; + len = read(d, buf, BUFSIZE); + if (len < 0) { + if (errno == EAGAIN) + continue; + err(1, "read failed"); + } + ptr = (u_int32_t *) buf; +again: + pkt = (struct fw_pkt *) ptr; + if (ntohs(pkt->mode.stream.len) <= sizeof(struct ciphdr)) { + ptr ++; /* skip header */ + ptr += ntohs(pkt->mode.stream.len)/sizeof(u_int32_t); + goto next; + } +#if 0 + printf("%08x %08x %08x %08x\n", + htonl(ptr[0]), htonl(ptr[1]), + htonl(ptr[2]), htonl(ptr[3])); +#endif + ciph = (struct ciphdr *)(ptr + 1); /* skip iso header */ + if (ciph->fmt != CIP_FMT_DVCR) + err(1, "unknown format 0x%x", ciph->fmt); + ptr = (u_int32_t *) (ciph + 1); /* skip cip header */ +#if 0 + if (ciph->fdf.dv.cyc != 0xffff && k == 0) { + fprintf(stderr, "0x%04x\n", ntohs(ciph->fdf.dv.cyc)); + } +#endif + for (dv = (struct dvdbc *)ptr; + (char *)dv < (char *)(ptr + ciph->len); + dv++) { + +#if 0 + printf("(%d,%d) ", dv->sct, dv->dseq); +#endif + if (dv->sct == DV_SCT_HEADER && dv->dseq == 0) { +#if 0 + printf("%d(%d) ", k, m); +#else + printf("%d", k%10); +#endif + pal = ((dv->payload[0] & DV_DSF_12) != 0); + nb = nblocks[pal]; +#if 1 + if (m > 0 && m != nb) { + /* padding bad frame */ + npad = ((nb - m) % nb); + if (npad < 0) + npad += nb; + printf("(%d blocks padded)", npad); + npad *= BLOCKSIZE; + npad = write(fd, pad, npad); + } +#endif + k++; + if (k % 30 == 0) { /* every second */ + printf("\n"); + } + fflush(stdout); + m = 0; + } + if (k == 0 || (count > 0 && k > count)) + continue; + m++; + v = &wbuf[vec]; + if ((v->iov_base + v->iov_len) == (char *) dv) { + v->iov_len += sizeof(struct dvdbc); + } else { + if (v->iov_len != 0) + vec++; + if (vec >= NVEC) { + writev(fd, wbuf, vec); + vec = 0; + } + v = &wbuf[vec]; + v->iov_base = (char *) dv; + v->iov_len = sizeof(struct dvdbc); + } + } + ptr = (u_int32_t *)dv; +next: + if ((char *)ptr < buf + len) { + goto again; + } + if (wbuf[0].iov_len > 0) { + writev(fd, wbuf, vec+1); + } + } + close(fd); + printf("\n"); + return 0; +} + +int dvsend(int d, char *filename, char ich, int count) +{ + struct fw_isochreq isoreq; + struct fw_isobufreq bufreq; + struct dvdbc *dv; + struct ciphdr *ciph; + struct fw_pkt *pkt; + int len, dlen, header, fd, frames, packets, vec; + int system=0, pad_acc, cycle_acc, cycle, f_cycle, f_frac; + struct iovec wbuf[NVEC]; + char *pbuf; + u_int32_t hdr[3]; + struct timeval start, end; + double rtime; + + fd = open(filename, O_RDONLY); + pbuf = (char *)malloc(BUFSIZE); + bzero(wbuf, sizeof(wbuf)); + + bufreq.rx.nchunk = 0; + bufreq.rx.npacket = 0; + bufreq.rx.psize = 0; + bufreq.tx.nchunk = 10; + bufreq.tx.npacket = 255; + bufreq.tx.psize = PSIZE; + if (ioctl(d, FW_SSTBUF, &bufreq) < 0) { + err(1, "ioctl"); + } + + isoreq.ch = ich & 0x3f; + isoreq.tag = (ich >> 6) & 3; + + if( ioctl(d, FW_STSTREAM, &isoreq) < 0) + err(1, "ioctl"); + + bzero(hdr, sizeof(hdr)); + pkt = (struct fw_pkt *) &hdr[0]; + pkt->mode.stream.len = htons(DSIZE + sizeof(struct ciphdr)); + pkt->mode.stream.sy = 0; + pkt->mode.stream.tcode = FWTCODE_STREAM; + pkt->mode.stream.chtag = ich; + + ciph = (struct ciphdr *) &hdr[1]; + ciph->src = 0; /* XXX */ + ciph->len = 120; + ciph->dbc = 0; + ciph->eoh1 = 1; + ciph->fdf.dv.cyc = 0xffff; + + gettimeofday(&start, NULL); +#if 0 + printf("%08x %08x %08x\n", htonl(hdr[0]), htonl(hdr[1]), htonl(hdr[2])); +#endif + frames = 0; + packets = 0; + pad_acc = 0; + cycle_acc = frame_cycle[system].d * 1; + cycle = 1; + while (1) { + vec = 0; + wbuf[0].iov_len = 0; + dlen = 0; + while (dlen < DSIZE) { + len = read(fd, pbuf + dlen, DSIZE - dlen); + if (len <= 0) { + printf("\nend of file(len=%d)\n", len); + goto send_end; + } + dlen += len; + } + dv = (struct dvdbc *)pbuf; +#if 0 + header = (dv->sct == 0 && dv->dseq == 0); +#else + header = (packets % npackets[system] == 0); +#endif + + if (header) { + printf("%d", frames % 10); + frames ++; + if (count > 0 && frames > count) + break; + if (frames % 30 == 0) + printf("\n"); + fflush(stdout); + system = ((dv->payload[0] & DV_DSF_12) != 0); + f_cycle = (cycle_acc / frame_cycle[system].d) & 0xf; + f_frac = (cycle_acc % frame_cycle[system].d + * CYCLE_FRAC) / frame_cycle[system].d; +#if 0 + ciph->fdf.dv.cyc = htons(f_cycle << 12 | f_frac); +#else + ciph->fdf.dv.cyc = htons(cycle << 12 | f_frac); +#endif + cycle_acc += frame_cycle[system].n; + cycle_acc %= frame_cycle[system].d * 0x10; + + } else { + ciph->fdf.dv.cyc = 0xffff; /* XXX */ + } + ciph->dbc = packets++ % 256; + wbuf[0].iov_base = (char *)&hdr[0]; + wbuf[0].iov_len = 3 * sizeof(u_int32_t); + wbuf[1].iov_base = pbuf; + wbuf[1].iov_len = 480; + + pad_acc += pad_rate[system].n; + if (pad_acc >= pad_rate[system].d) { + pad_acc -= pad_rate[system].d; + pkt->mode.stream.len = htons(sizeof(struct ciphdr)); + cycle ++; +again1: + len = writev(d, wbuf, 1); + if (len < 0) { + if (errno == EAGAIN) { + printf("again"); fflush(stdout); + goto again1; + } + err(1, "write failed"); + } + } + + pkt->mode.stream.len = htons(480 + sizeof(struct ciphdr)); + cycle ++; +again2: + len = writev(d, wbuf, 2); + if (len < 0) { + if (errno == EAGAIN) { + printf("again"); fflush(stdout); + goto again2; + } + err(1, "write failed"); + } +#if 0 + printf("."); fflush(stdout); +#endif + } + close(fd); + printf("\n"); +send_end: + gettimeofday(&end, NULL); + rtime = end.tv_sec - start.tv_sec + + (end.tv_usec - start.tv_usec) * 1e-6; + printf("%d frames, %.2f secs, %.2f frames/sec\n", + frames, rtime, frames/rtime); + return 0; +} + + +#if 0 +int +main(int argc, char *argv[]) +{ + extern char *optarg; + extern int optind; + int ch; + int fd; + char devname[] = "/dev/fw0"; + + while ((ch = getopt(argc, argv, "I:")) != -1){ + switch(ch) { + case 'I': + strcpy(devname, optarg); + break; + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (argc < 2) + usage(); + + fd = open(devname, O_RDWR); + if (fd < 0) + err(1, "open"); + dvrec(fd, argv[0], (TAG<<6) | CHANNEL, atoi(argv[1])); + close(fd); + exit(0); +} + +#endif -- cgit v1.1