xref: /haiku/src/add-ons/media/media-add-ons/firewire_dv/FireWireCard.cpp (revision 4c9da6dc09e79654948b64ba600c4d4b1b12109e)
1 /*
2  * FireWire DV media addon for Haiku
3  *
4  * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn)
5  * Distributed under the terms of the MIT License.
6  *
7  */
8 /*
9  * Copyright (C) 2003
10  * 	Hidetoshi Shimokawa. All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *
23  *	This product includes software developed by Hidetoshi Shimokawa.
24  *
25  * 4. Neither the name of the author nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  */
41 
42 #include "FireWireCard.h"
43 
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <fcntl.h>
48 #include <unistd.h>
49 #include <sys/ioctl.h>
50 #include <OS.h>
51 #include <stdint.h>
52 #include <errno.h>
53 
54 #include "glue.h"
55 
56 #define TAG	(1<<6)
57 #define CHANNEL	63
58 
59 /* for DV format */
60 #define FIX_FRAME	1
61 
62 struct frac {
63 	int n,d;
64 };
65 
66 struct frac frame_cycle[2]  = {
67 	{8000*100, 2997},	/* NTSC 8000 cycle / 29.97 Hz */
68 	{320, 1},		/* PAL  8000 cycle / 25 Hz */
69 };
70 int npackets[] = {
71 	250		/* NTSC */,
72 	300		/* PAL */
73 };
74 struct frac pad_rate[2]  = {
75 	{203, 2997},	/* = (8000 - 29.97 * 250)/(29.97 * 250) */
76 	{1, 15},	/* = (8000 - 25 * 300)/(25 * 300) */
77 };
78 const char *system_name[] = {"NTSC", "PAL"};
79 int frame_rate[] = {30, 25};
80 
81 #define DV_PSIZE 512
82 #define DV_DSIZE 480
83 #define DV_NCHUNK 64
84 
85 #define DV_NPACKET_R 256
86 #define DV_NPACKET_T 255
87 #define DV_TNBUF 100	/* XXX too large value causes block noise */
88 #define DV_NEMPTY 10	/* depends on DV_TNBUF */
89 #define DV_RBUFSIZE (DV_PSIZE * DV_NPACKET_R)
90 #define DV_MAXBLOCKS (300)
91 #define DV_CYCLE_FRAC 0xc00
92 
93 /* for MPEGTS format */
94 typedef uint8_t mpeg_ts_pld[188];
95 
96 struct mpeg_pldt {
97 #if BYTE_ORDER == BIG_ENDIAN
98 	uint32_t	:7,
99 				c_count:13,
100 				c_offset:12;
101 #else /* BYTE_ORDER != BIG_ENDIAN */
102 	uint32_t	c_offset:12,
103 				c_count:13,
104 				:7;
105 #endif /* BYTE_ORDER == BIG_ENDIAN */
106 	mpeg_ts_pld payload;
107 };
108 
109 
110 #define	MPEG_NCHUNK 8
111 #define	MPEG_PSIZE 596
112 #define	MPEG_NPACKET_R 4096
113 #define	MPEG_RBUFSIZE (MPEG_PSIZE * MPEG_NPACKET_R)
114 
115 
FireWireCard(const char * path)116 FireWireCard::FireWireCard(const char* path)
117 	: fInitStatus(B_OK),
118 	fDev(-1),
119 	fBuf(NULL),
120 	fPad(NULL)
121 {
122 	printf("FireWireCard opening %s\n", path);
123 
124 	fDev = open(path, O_RDWR);
125 	if (fDev < 0) {
126 		printf("FireWireCard opening %s failed\n", path);
127 		fInitStatus = B_ERROR;
128 		return;
129 	}
130 }
131 
132 
~FireWireCard()133 FireWireCard::~FireWireCard()
134 {
135 	if (fDev > 0)
136 		close(fDev);
137 }
138 
139 
140 status_t
InitCheck()141 FireWireCard::InitCheck()
142 {
143 	return fInitStatus;
144 }
145 
146 
147 ssize_t
Read(void ** data)148 FireWireCard::Read(void** data)
149 {
150 	if (fFormat == FMT_MPEGTS)
151 		return MpegtsRead(data);
152 	else
153 		return DvRead(data);
154 }
155 
156 
157 status_t
Extract(void * dest,void ** src,ssize_t * sizeUsed)158 FireWireCard::Extract(void* dest, void** src, ssize_t* sizeUsed)
159 {
160 	if (fFormat == FMT_MPEGTS)
161 		return MpegtsExtract(dest, src, sizeUsed);
162 	else
163 		return DvExtract(dest, src, sizeUsed);
164 }
165 
166 
167 void
GetBufInfo(size_t * rbufsize,int * rcount)168 FireWireCard::GetBufInfo(size_t* rbufsize, int* rcount)
169 {
170 	*rbufsize = fRbufSize;
171 	*rcount = fRcount;
172 }
173 
174 
175 status_t
DetectRecvFn()176 FireWireCard::DetectRecvFn()
177 {
178 	char* buf;
179 	char ich = TAG | CHANNEL;
180 	struct fw_isochreq isoreq;
181 	struct fw_isobufreq bufreq;
182 	int len;
183 	u_int32_t* ptr;
184 	struct ciphdr* ciph;
185 
186 	bufreq.rx.nchunk = 8;
187 	bufreq.rx.npacket = 16;
188 	bufreq.rx.psize = 1024;
189 	bufreq.tx.nchunk = 0;
190 	bufreq.tx.npacket = 0;
191 	bufreq.tx.psize = 0;
192 
193 	if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0)
194 		return errno;
195 
196 	isoreq.ch = ich & 0x3f;
197 	isoreq.tag = (ich >> 6) & 3;
198 
199 	if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
200 		return errno;
201 
202 	buf = (char*)malloc(1024*16);
203 	len = read(fDev, buf, 1024*16);
204 	if (len < 0) {
205 		free(buf);
206 		return errno;
207 	}
208 	ptr = (u_int32_t*) buf;
209 	ciph = (struct ciphdr*)(ptr + 1);
210 
211 	switch(ciph->fmt) {
212 		case CIP_FMT_DVCR:
213 			fprintf(stderr, "Detected DV format on input.\n");
214 			fFormat = FMT_DV;
215 			fBuf = malloc(DV_RBUFSIZE);
216 			fRbufSize = DV_PSIZE;
217 			fRcount = DV_NPACKET_R;
218 			fPad = malloc(DV_DSIZE*DV_MAXBLOCKS);
219 			memset(fPad, 0xff, DV_DSIZE*DV_MAXBLOCKS);
220 			break;
221 		case CIP_FMT_MPEG:
222 			fprintf(stderr, "Detected MPEG TS format on input.\n");
223 			fFormat = FMT_MPEGTS;
224 			fBuf = malloc(MPEG_RBUFSIZE);
225 			fRbufSize = MPEG_PSIZE;
226 			fRcount = MPEG_NPACKET_R;
227 			break;
228 		default:
229 			fprintf(stderr, "Unsupported format for receiving: fmt=0x%x", ciph->fmt);
230 	}
231 	free(buf);
232 	return B_OK;
233 }
234 
235 
236 ssize_t
DvRead(void ** buffer)237 FireWireCard::DvRead(void** buffer)
238 {
239 	struct fw_isochreq isoreq;
240 	struct fw_isobufreq bufreq;
241 	ssize_t len;
242 	char ich = TAG|CHANNEL;
243 
244 	bufreq.rx.nchunk = DV_NCHUNK;
245 	bufreq.rx.npacket = DV_NPACKET_R;
246 	bufreq.rx.psize = DV_PSIZE;
247 	bufreq.tx.nchunk = 0;
248 	bufreq.tx.npacket = 0;
249 	bufreq.tx.psize = 0;
250 	if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0)
251 		return errno;
252 
253 	isoreq.ch = ich & 0x3f;
254 	isoreq.tag = (ich >> 6) & 3;
255 
256 	if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
257 		return errno;
258 
259 	len = read(fDev, fBuf, DV_RBUFSIZE);
260 	if (len < 0) {
261 		if (errno == EAGAIN) {
262 			fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
263 			fflush(stderr);
264 		} else
265 			fprintf(stderr, "read failed");
266 		return errno;
267 	}
268 	*buffer = fBuf;
269 	return len;
270 }
271 
272 
273 status_t
DvExtract(void * dest,void ** src,ssize_t * sizeUsed)274 FireWireCard::DvExtract(void* dest, void** src, ssize_t* sizeUsed)
275 {
276 	struct dvdbc* dv;
277 	struct ciphdr* ciph;
278 	struct fw_pkt* pkt;
279 	u_int32_t* ptr;
280 	int nblocks[] = {250 /* NTSC */, 300 /* PAL */};
281 	int npad, k, m, system = -1, nb;
282 
283 	k = m = 0;
284 	ptr = (u_int32_t*) (*src);
285 
286 	pkt = (struct fw_pkt*) ptr;
287 	ciph = (struct ciphdr*)(ptr + 1);	/* skip iso header */
288 	if (ciph->fmt != CIP_FMT_DVCR) {
289 		fprintf(stderr, "unknown format 0x%x", ciph->fmt);
290 		return B_ERROR;
291 	}
292 	ptr = (u_int32_t*) (ciph + 1);		/* skip cip header */
293 	if (pkt->mode.stream.len <= sizeof(struct ciphdr))
294 		/* no payload */
295 		return B_ERROR;
296 	for (dv = (struct dvdbc*)ptr;
297 			(char*)dv < (char *)(ptr + ciph->len);
298 			dv+=6) {
299 
300 		if  (dv->sct == DV_SCT_HEADER && dv->dseq == 0) {
301 			if (system < 0) {
302 				system = ciph->fdf.dv.fs;
303 				fprintf(stderr, "%s\n", system_name[system]);
304 			}
305 
306 			/* Fix DSF bit */
307 			if (system == 1 &&
308 				(dv->payload[0] & DV_DSF_12) == 0)
309 				dv->payload[0] |= DV_DSF_12;
310 			nb = nblocks[system];
311 			fprintf(stderr, "%d", k%10);
312 #if FIX_FRAME
313 			if (m > 0 && m != nb) {
314 				/* padding bad frame */
315 				npad = ((nb - m) % nb);
316 				if (npad < 0)
317 					npad += nb;
318 				fprintf(stderr, "(%d blocks padded)",
319 							npad);
320 				npad *= DV_DSIZE;
321 				memcpy(dest, fPad, npad);
322 				dest = (char*)dest + npad;
323 			}
324 #endif
325 			k++;
326 			if (k % frame_rate[system] == 0) {
327 				/* every second */
328 				fprintf(stderr, "\n");
329 			}
330 			fflush(stderr);
331 			m = 0;
332 		}
333 		if (k == 0)
334 			continue;
335 		m++;
336 		memcpy(dest, dv, DV_DSIZE);
337 		dest = (char*)dest + DV_DSIZE;
338 	}
339 	ptr = (u_int32_t*)dv;
340 	*src = ptr;
341 	return B_OK;
342 }
343 
344 
345 ssize_t
MpegtsRead(void ** buffer)346 FireWireCard::MpegtsRead(void** buffer)
347 {
348 	struct fw_isochreq isoreq;
349 	struct fw_isobufreq bufreq;
350 	char ich = TAG|CHANNEL;
351 	ssize_t len;
352 
353 
354 	bufreq.rx.nchunk = MPEG_NCHUNK;
355 	bufreq.rx.npacket = MPEG_NPACKET_R;
356 	bufreq.rx.psize = MPEG_PSIZE;
357 	bufreq.tx.nchunk = 0;
358 	bufreq.tx.npacket = 0;
359 	bufreq.tx.psize = 0;
360 	if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0)
361 		return errno;
362 
363 	isoreq.ch = ich & 0x3f;
364 	isoreq.tag = (ich >> 6) & 3;
365 
366 	if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
367 		return errno;
368 
369 	len = read(fDev, fBuf, MPEG_RBUFSIZE);
370 	if (len < 0) {
371 		if (errno == EAGAIN) {
372 			fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
373 			fflush(stderr);
374 		} else
375 			fprintf(stderr, "read failed");
376 		return errno;
377 	}
378 	*buffer = fBuf;
379 	return len;
380 }
381 
382 
383 status_t
MpegtsExtract(void * dest,void ** src,ssize_t * sizeUsed)384 FireWireCard::MpegtsExtract(void* dest, void** src, ssize_t* sizeUsed)
385 {
386 	uint32_t* ptr;
387 	struct fw_pkt* pkt;
388 	struct ciphdr* ciph;
389 	struct mpeg_pldt* pld;
390 	int pkt_size, startwr;
391 
392 	ptr = (uint32_t *)(*src);
393 	startwr = 0;
394 
395 	pkt = (struct fw_pkt*) ptr;
396 	/* there is no CRC in the 1394 header */
397 	ciph = (struct ciphdr*)(ptr + 1);	/* skip iso header */
398 	if (ciph->fmt != CIP_FMT_MPEG) {
399 		fprintf(stderr, "unknown format 0x%x", ciph->fmt);
400 		return B_ERROR;
401 	}
402 	if (ciph->fn != 3) {
403 		fprintf(stderr,	"unsupported MPEG TS stream, fn=%d (only"
404 			"fn=3 is supported)", ciph->fn);
405 		return B_ERROR;
406 	}
407 	ptr = (uint32_t*) (ciph + 1);		/* skip cip header */
408 
409 	if (pkt->mode.stream.len <= sizeof(struct ciphdr)) {
410 		/* no payload */
411 		/* tlen needs to be decremented before end of the loop */
412 		goto next;
413 	}
414 
415 	/* This is a condition that needs to be satisfied to start
416 	   writing the data */
417 	if (ciph->dbc % (1<<ciph->fn) == 0)
418 		startwr = 1;
419 	/* Read out all the MPEG TS data blocks from current packet */
420 	for (pld = (struct mpeg_pldt *)ptr;
421 	    (intptr_t)pld < (intptr_t)((char*)ptr +
422 	    pkt->mode.stream.len - sizeof(struct ciphdr));
423 	    pld++) {
424 		if (startwr == 1) {
425 			memcpy(dest, pld->payload, sizeof(pld->payload));
426 			dest = (char*)dest + sizeof(pld->payload);
427 		}
428 	}
429 
430 next:
431 	/* CRCs are removed from both header and trailer
432 	so that only 4 bytes of 1394 header remains */
433 	pkt_size = pkt->mode.stream.len + 4;
434 	ptr = (uint32_t*)((intptr_t)pkt + pkt_size);
435 	*src = ptr;
436 	return B_OK;
437 }
438 
439