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 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 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 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 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 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 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