xref: /haiku/src/add-ons/media/media-add-ons/firewire_dv/FireWireCard.cpp (revision 2c69b5b6c0e7b481a0c43366a1942a6055cbb864)
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 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 
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 
133 FireWireCard::~FireWireCard()
134 {
135 	if (fDev > 0)
136 		close(fDev);
137 }
138 
139 
140 status_t
141 FireWireCard::InitCheck()
142 {
143 	return fInitStatus;
144 }
145 
146 
147 ssize_t
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
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
168 FireWireCard::GetBufInfo(size_t* rbufsize, int* rcount)
169 {
170 	*rbufsize = fRbufSize;
171 	*rcount = fRcount;
172 }
173 
174 
175 status_t
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 		return errno;
206 	ptr = (u_int32_t*) buf;
207 	ciph = (struct ciphdr*)(ptr + 1);
208 
209 	switch(ciph->fmt) {
210 		case CIP_FMT_DVCR:
211 			fprintf(stderr, "Detected DV format on input.\n");
212 			fFormat = FMT_DV;
213 			fBuf = malloc(DV_RBUFSIZE);
214 			fRbufSize = DV_PSIZE;
215 			fRcount = DV_NPACKET_R;
216 			fPad = malloc(DV_DSIZE*DV_MAXBLOCKS);
217 			memset(fPad, 0xff, DV_DSIZE*DV_MAXBLOCKS);
218 			break;
219 		case CIP_FMT_MPEG:
220 			fprintf(stderr, "Detected MPEG TS format on input.\n");
221 			fFormat = FMT_MPEGTS;
222 			fBuf = malloc(MPEG_RBUFSIZE);
223 			fRbufSize = MPEG_PSIZE;
224 			fRcount = MPEG_NPACKET_R;
225 			break;
226 		default:
227 			fprintf(stderr, "Unsupported format for receiving: fmt=0x%x", ciph->fmt);
228 	}
229 	free(buf);
230 	return B_OK;
231 }
232 
233 
234 ssize_t
235 FireWireCard::DvRead(void** buffer)
236 {
237 	struct fw_isochreq isoreq;
238 	struct fw_isobufreq bufreq;
239 	ssize_t len;
240 	char ich = TAG|CHANNEL;
241 
242 	bufreq.rx.nchunk = DV_NCHUNK;
243 	bufreq.rx.npacket = DV_NPACKET_R;
244 	bufreq.rx.psize = DV_PSIZE;
245 	bufreq.tx.nchunk = 0;
246 	bufreq.tx.npacket = 0;
247 	bufreq.tx.psize = 0;
248 	if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0)
249 		return errno;
250 
251 	isoreq.ch = ich & 0x3f;
252 	isoreq.tag = (ich >> 6) & 3;
253 
254 	if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
255 		return errno;
256 
257 	len = read(fDev, fBuf, DV_RBUFSIZE);
258 	if (len < 0) {
259 		if (errno == EAGAIN) {
260 			fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
261 			fflush(stderr);
262 		} else
263 			fprintf(stderr, "read failed");
264 		return errno;
265 	}
266 	*buffer = fBuf;
267 	return len;
268 }
269 
270 
271 status_t
272 FireWireCard::DvExtract(void* dest, void** src, ssize_t* sizeUsed)
273 {
274 	struct dvdbc* dv;
275 	struct ciphdr* ciph;
276 	struct fw_pkt* pkt;
277 	u_int32_t* ptr;
278 	int nblocks[] = {250 /* NTSC */, 300 /* PAL */};
279 	int npad, k, m, system = -1, nb;
280 
281 	k = m = 0;
282 	ptr = (u_int32_t*) (*src);
283 
284 	pkt = (struct fw_pkt*) ptr;
285 	ciph = (struct ciphdr*)(ptr + 1);	/* skip iso header */
286 	if (ciph->fmt != CIP_FMT_DVCR) {
287 		fprintf(stderr, "unknown format 0x%x", ciph->fmt);
288 		return B_ERROR;
289 	}
290 	ptr = (u_int32_t*) (ciph + 1);		/* skip cip header */
291 	if (pkt->mode.stream.len <= sizeof(struct ciphdr))
292 		/* no payload */
293 		return B_ERROR;
294 	for (dv = (struct dvdbc*)ptr;
295 			(char*)dv < (char *)(ptr + ciph->len);
296 			dv+=6) {
297 
298 		if  (dv->sct == DV_SCT_HEADER && dv->dseq == 0) {
299 			if (system < 0) {
300 				system = ciph->fdf.dv.fs;
301 				fprintf(stderr, "%s\n", system_name[system]);
302 			}
303 
304 			/* Fix DSF bit */
305 			if (system == 1 &&
306 				(dv->payload[0] & DV_DSF_12) == 0)
307 				dv->payload[0] |= DV_DSF_12;
308 			nb = nblocks[system];
309 			fprintf(stderr, "%d", k%10);
310 #if FIX_FRAME
311 			if (m > 0 && m != nb) {
312 				/* padding bad frame */
313 				npad = ((nb - m) % nb);
314 				if (npad < 0)
315 					npad += nb;
316 				fprintf(stderr, "(%d blocks padded)",
317 							npad);
318 				npad *= DV_DSIZE;
319 				memcpy(dest, fPad, npad);
320 				dest = (char*)dest + npad;
321 			}
322 #endif
323 			k++;
324 			if (k % frame_rate[system] == 0) {
325 				/* every second */
326 				fprintf(stderr, "\n");
327 			}
328 			fflush(stderr);
329 			m = 0;
330 		}
331 		if (k == 0)
332 			continue;
333 		m++;
334 		memcpy(dest, dv, DV_DSIZE);
335 		dest = (char*)dest + DV_DSIZE;
336 	}
337 	ptr = (u_int32_t*)dv;
338 	*src = ptr;
339 	return B_OK;
340 }
341 
342 
343 ssize_t
344 FireWireCard::MpegtsRead(void** buffer)
345 {
346 	struct fw_isochreq isoreq;
347 	struct fw_isobufreq bufreq;
348 	char ich = TAG|CHANNEL;
349 	ssize_t len;
350 
351 
352 	bufreq.rx.nchunk = MPEG_NCHUNK;
353 	bufreq.rx.npacket = MPEG_NPACKET_R;
354 	bufreq.rx.psize = MPEG_PSIZE;
355 	bufreq.tx.nchunk = 0;
356 	bufreq.tx.npacket = 0;
357 	bufreq.tx.psize = 0;
358 	if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0)
359 		return errno;
360 
361 	isoreq.ch = ich & 0x3f;
362 	isoreq.tag = (ich >> 6) & 3;
363 
364 	if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
365 		return errno;
366 
367 	len = read(fDev, fBuf, MPEG_RBUFSIZE);
368 	if (len < 0) {
369 		if (errno == EAGAIN) {
370 			fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
371 			fflush(stderr);
372 		} else
373 			fprintf(stderr, "read failed");
374 		return errno;
375 	}
376 	*buffer = fBuf;
377 	return len;
378 }
379 
380 
381 status_t
382 FireWireCard::MpegtsExtract(void* dest, void** src, ssize_t* sizeUsed)
383 {
384 	uint32_t* ptr;
385 	struct fw_pkt* pkt;
386 	struct ciphdr* ciph;
387 	struct mpeg_pldt* pld;
388 	int pkt_size, startwr;
389 
390 	ptr = (uint32_t *)(*src);
391 	startwr = 0;
392 
393 	pkt = (struct fw_pkt*) ptr;
394 	/* there is no CRC in the 1394 header */
395 	ciph = (struct ciphdr*)(ptr + 1);	/* skip iso header */
396 	if (ciph->fmt != CIP_FMT_MPEG) {
397 		fprintf(stderr, "unknown format 0x%x", ciph->fmt);
398 		return B_ERROR;
399 	}
400 	if (ciph->fn != 3) {
401 		fprintf(stderr,	"unsupported MPEG TS stream, fn=%d (only"
402 			"fn=3 is supported)", ciph->fn);
403 		return B_ERROR;
404 	}
405 	ptr = (uint32_t*) (ciph + 1);		/* skip cip header */
406 
407 	if (pkt->mode.stream.len <= sizeof(struct ciphdr)) {
408 		/* no payload */
409 		/* tlen needs to be decremented before end of the loop */
410 		goto next;
411 	}
412 
413 	/* This is a condition that needs to be satisfied to start
414 	   writing the data */
415 	if (ciph->dbc % (1<<ciph->fn) == 0)
416 		startwr = 1;
417 	/* Read out all the MPEG TS data blocks from current packet */
418 	for (pld = (struct mpeg_pldt *)ptr;
419 	    (intptr_t)pld < (intptr_t)((char*)ptr +
420 	    pkt->mode.stream.len - sizeof(struct ciphdr));
421 	    pld++) {
422 		if (startwr == 1) {
423 			memcpy(dest, pld->payload, sizeof(pld->payload));
424 			dest = (char*)dest + sizeof(pld->payload);
425 		}
426 	}
427 
428 next:
429 	/* CRCs are removed from both header and trailer
430 	so that only 4 bytes of 1394 header remains */
431 	pkt_size = pkt->mode.stream.len + 4;
432 	ptr = (uint32_t*)((intptr_t)pkt + pkt_size);
433 	*src = ptr;
434 	return B_OK;
435 }
436 
437