* FireWire DV media addon for Haiku
*
* Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn)
* Distributed under the terms of the MIT License.
*
*/
* 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.
*/
#include "FireWireCard.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <OS.h>
#include <stdint.h>
#include <errno.h>
#include "glue.h"
#define TAG (1<<6)
#define CHANNEL 63
#define FIX_FRAME 1
struct frac {
int n,d;
};
struct frac frame_cycle[2] = {
{8000*100, 2997},
{320, 1},
};
int npackets[] = {
250 ,
300
};
struct frac pad_rate[2] = {
{203, 2997},
{1, 15},
};
const char *system_name[] = {"NTSC", "PAL"};
int frame_rate[] = {30, 25};
#define DV_PSIZE 512
#define DV_DSIZE 480
#define DV_NCHUNK 64
#define DV_NPACKET_R 256
#define DV_NPACKET_T 255
#define DV_TNBUF 100 /* XXX too large value causes block noise */
#define DV_NEMPTY 10 /* depends on DV_TNBUF */
#define DV_RBUFSIZE (DV_PSIZE * DV_NPACKET_R)
#define DV_MAXBLOCKS (300)
#define DV_CYCLE_FRAC 0xc00
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
uint32_t c_offset:12,
c_count:13,
:7;
#endif
mpeg_ts_pld payload;
};
#define MPEG_NCHUNK 8
#define MPEG_PSIZE 596
#define MPEG_NPACKET_R 4096
#define MPEG_RBUFSIZE (MPEG_PSIZE * MPEG_NPACKET_R)
FireWireCard::FireWireCard(const char* path)
: fInitStatus(B_OK),
fDev(-1),
fBuf(NULL),
fPad(NULL)
{
printf("FireWireCard opening %s\n", path);
fDev = open(path, O_RDWR);
if (fDev < 0) {
printf("FireWireCard opening %s failed\n", path);
fInitStatus = B_ERROR;
return;
}
}
FireWireCard::~FireWireCard()
{
if (fDev > 0)
close(fDev);
}
status_t
FireWireCard::InitCheck()
{
return fInitStatus;
}
ssize_t
FireWireCard::Read(void** data)
{
if (fFormat == FMT_MPEGTS)
return MpegtsRead(data);
else
return DvRead(data);
}
status_t
FireWireCard::Extract(void* dest, void** src, ssize_t* sizeUsed)
{
if (fFormat == FMT_MPEGTS)
return MpegtsExtract(dest, src, sizeUsed);
else
return DvExtract(dest, src, sizeUsed);
}
void
FireWireCard::GetBufInfo(size_t* rbufsize, int* rcount)
{
*rbufsize = fRbufSize;
*rcount = fRcount;
}
status_t
FireWireCard::DetectRecvFn()
{
char* buf;
char ich = TAG | CHANNEL;
struct fw_isochreq isoreq;
struct fw_isobufreq bufreq;
int len;
u_int32_t* ptr;
struct ciphdr* ciph;
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(fDev, FW_SSTBUF, &bufreq) < 0)
return errno;
isoreq.ch = ich & 0x3f;
isoreq.tag = (ich >> 6) & 3;
if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
return errno;
buf = (char*)malloc(1024*16);
len = read(fDev, buf, 1024*16);
if (len < 0) {
free(buf);
return errno;
}
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");
fFormat = FMT_DV;
fBuf = malloc(DV_RBUFSIZE);
fRbufSize = DV_PSIZE;
fRcount = DV_NPACKET_R;
fPad = malloc(DV_DSIZE*DV_MAXBLOCKS);
memset(fPad, 0xff, DV_DSIZE*DV_MAXBLOCKS);
break;
case CIP_FMT_MPEG:
fprintf(stderr, "Detected MPEG TS format on input.\n");
fFormat = FMT_MPEGTS;
fBuf = malloc(MPEG_RBUFSIZE);
fRbufSize = MPEG_PSIZE;
fRcount = MPEG_NPACKET_R;
break;
default:
fprintf(stderr, "Unsupported format for receiving: fmt=0x%x", ciph->fmt);
}
free(buf);
return B_OK;
}
ssize_t
FireWireCard::DvRead(void** buffer)
{
struct fw_isochreq isoreq;
struct fw_isobufreq bufreq;
ssize_t len;
char ich = TAG|CHANNEL;
bufreq.rx.nchunk = DV_NCHUNK;
bufreq.rx.npacket = DV_NPACKET_R;
bufreq.rx.psize = DV_PSIZE;
bufreq.tx.nchunk = 0;
bufreq.tx.npacket = 0;
bufreq.tx.psize = 0;
if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0)
return errno;
isoreq.ch = ich & 0x3f;
isoreq.tag = (ich >> 6) & 3;
if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
return errno;
len = read(fDev, fBuf, DV_RBUFSIZE);
if (len < 0) {
if (errno == EAGAIN) {
fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
fflush(stderr);
} else
fprintf(stderr, "read failed");
return errno;
}
*buffer = fBuf;
return len;
}
status_t
FireWireCard::DvExtract(void* dest, void** src, ssize_t* sizeUsed)
{
struct dvdbc* dv;
struct ciphdr* ciph;
struct fw_pkt* pkt;
u_int32_t* ptr;
int nblocks[] = {250 , 300 };
int npad, k, m, system = -1, nb;
k = m = 0;
ptr = (u_int32_t*) (*src);
pkt = (struct fw_pkt*) ptr;
ciph = (struct ciphdr*)(ptr + 1);
if (ciph->fmt != CIP_FMT_DVCR) {
fprintf(stderr, "unknown format 0x%x", ciph->fmt);
return B_ERROR;
}
ptr = (u_int32_t*) (ciph + 1);
if (pkt->mode.stream.len <= sizeof(struct ciphdr))
return B_ERROR;
for (dv = (struct dvdbc*)ptr;
(char*)dv < (char *)(ptr + ciph->len);
dv+=6) {
if (dv->sct == DV_SCT_HEADER && dv->dseq == 0) {
if (system < 0) {
system = ciph->fdf.dv.fs;
fprintf(stderr, "%s\n", system_name[system]);
}
if (system == 1 &&
(dv->payload[0] & DV_DSF_12) == 0)
dv->payload[0] |= DV_DSF_12;
nb = nblocks[system];
fprintf(stderr, "%d", k%10);
#if FIX_FRAME
if (m > 0 && m != nb) {
npad = ((nb - m) % nb);
if (npad < 0)
npad += nb;
fprintf(stderr, "(%d blocks padded)",
npad);
npad *= DV_DSIZE;
memcpy(dest, fPad, npad);
dest = (char*)dest + npad;
}
#endif
k++;
if (k % frame_rate[system] == 0) {
fprintf(stderr, "\n");
}
fflush(stderr);
m = 0;
}
if (k == 0)
continue;
m++;
memcpy(dest, dv, DV_DSIZE);
dest = (char*)dest + DV_DSIZE;
}
ptr = (u_int32_t*)dv;
*src = ptr;
return B_OK;
}
ssize_t
FireWireCard::MpegtsRead(void** buffer)
{
struct fw_isochreq isoreq;
struct fw_isobufreq bufreq;
char ich = TAG|CHANNEL;
ssize_t len;
bufreq.rx.nchunk = MPEG_NCHUNK;
bufreq.rx.npacket = MPEG_NPACKET_R;
bufreq.rx.psize = MPEG_PSIZE;
bufreq.tx.nchunk = 0;
bufreq.tx.npacket = 0;
bufreq.tx.psize = 0;
if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0)
return errno;
isoreq.ch = ich & 0x3f;
isoreq.tag = (ich >> 6) & 3;
if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
return errno;
len = read(fDev, fBuf, MPEG_RBUFSIZE);
if (len < 0) {
if (errno == EAGAIN) {
fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
fflush(stderr);
} else
fprintf(stderr, "read failed");
return errno;
}
*buffer = fBuf;
return len;
}
status_t
FireWireCard::MpegtsExtract(void* dest, void** src, ssize_t* sizeUsed)
{
uint32_t* ptr;
struct fw_pkt* pkt;
struct ciphdr* ciph;
struct mpeg_pldt* pld;
int pkt_size, startwr;
ptr = (uint32_t *)(*src);
startwr = 0;
pkt = (struct fw_pkt*) ptr;
ciph = (struct ciphdr*)(ptr + 1);
if (ciph->fmt != CIP_FMT_MPEG) {
fprintf(stderr, "unknown format 0x%x", ciph->fmt);
return B_ERROR;
}
if (ciph->fn != 3) {
fprintf(stderr, "unsupported MPEG TS stream, fn=%d (only"
"fn=3 is supported)", ciph->fn);
return B_ERROR;
}
ptr = (uint32_t*) (ciph + 1);
if (pkt->mode.stream.len <= sizeof(struct ciphdr)) {
goto next;
}
writing the data */
if (ciph->dbc % (1<<ciph->fn) == 0)
startwr = 1;
for (pld = (struct mpeg_pldt *)ptr;
(intptr_t)pld < (intptr_t)((char*)ptr +
pkt->mode.stream.len - sizeof(struct ciphdr));
pld++) {
if (startwr == 1) {
memcpy(dest, pld->payload, sizeof(pld->payload));
dest = (char*)dest + sizeof(pld->payload);
}
}
next:
so that only 4 bytes of 1394 header remains */
pkt_size = pkt->mode.stream.len + 4;
ptr = (uint32_t*)((intptr_t)pkt + pkt_size);
*src = ptr;
return B_OK;
}