diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index 6749171420..b7bbd9517a 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -106,6 +106,7 @@ BEOS_ADD_ONS_MEDIA = cortex_audioadapter.media_addon cortex_flanger.media_addon usb_webcam.media_addon video_producer_demo.media_addon video_window_demo.media_addon + firewire_dv.media_addon #legacy.media_addon ; BEOS_ADD_ONS_MEDIA_PLUGINS = $(GPL_ONLY)ac3_decoder diff --git a/src/add-ons/media/media-add-ons/Jamfile b/src/add-ons/media/media-add-ons/Jamfile index 6d24f31ebf..fc7089ebf8 100644 --- a/src/add-ons/media/media-add-ons/Jamfile +++ b/src/add-ons/media/media-add-ons/Jamfile @@ -15,4 +15,5 @@ SubInclude HAIKU_TOP src add-ons media media-add-ons usb_vision ; SubInclude HAIKU_TOP src add-ons media media-add-ons usb_webcam ; SubInclude HAIKU_TOP src add-ons media media-add-ons video_producer_demo ; SubInclude HAIKU_TOP src add-ons media media-add-ons videowindow ; +SubInclude HAIKU_TOP src add-ons media media-add-ons firewire_dv ; diff --git a/src/add-ons/media/media-add-ons/firewire_dv/FireWireCard.cpp b/src/add-ons/media/media-add-ons/firewire_dv/FireWireCard.cpp new file mode 100644 index 0000000000..a0776fe740 --- /dev/null +++ b/src/add-ons/media/media-add-ons/firewire_dv/FireWireCard.cpp @@ -0,0 +1,400 @@ +/* + * FireWire DV media addon for Haiku + * + * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn) + * Distributed under the terms of the MIT License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FireWireCard.h" +#include "glue.h" + +#define TAG (1<<6) +#define CHANNEL 63 + +/* for DV format */ +#define FIX_FRAME 1 + +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) */ +}; +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 + +/* for MPEGTS format */ +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 /* BYTE_ORDER != BIG_ENDIAN */ + uint32_t c_offset:12, + c_count:13, + :7; +#endif /* BYTE_ORDER == BIG_ENDIAN */ + 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, 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) + 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 /* NTSC */, 300 /* PAL */}; + 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); /* skip iso header */ + if (ciph->fmt != CIP_FMT_DVCR) { + fprintf(stderr, "unknown format 0x%x", ciph->fmt); + return B_ERROR; + } + ptr = (u_int32_t *) (ciph + 1); /* skip cip header */ + if (pkt->mode.stream.len <= sizeof(struct ciphdr)) + /* no payload */ + 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]); + } + + /* Fix DSF bit */ + 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) { + /* padding bad frame */ + 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) { + /* every second */ + 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; + /* there is no CRC in the 1394 header */ + ciph = (struct ciphdr *)(ptr + 1); /* skip iso header */ + 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); /* skip cip header */ + + if (pkt->mode.stream.len <= sizeof(struct ciphdr)) { + /* no payload */ + /* tlen needs to be decremented before end of the loop */ + goto next; + } + + /* This is a condition that needs to be satisfied to start + writing the data */ + if (ciph->dbc % (1<fn) == 0) + startwr = 1; + /* Read out all the MPEG TS data blocks from current packet */ + 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: + /* CRCs are removed from both header and trailer + 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; +} diff --git a/src/add-ons/media/media-add-ons/firewire_dv/FireWireCard.h b/src/add-ons/media/media-add-ons/firewire_dv/FireWireCard.h new file mode 100644 index 0000000000..821109db3e --- /dev/null +++ b/src/add-ons/media/media-add-ons/firewire_dv/FireWireCard.h @@ -0,0 +1,47 @@ +/* + * FireWire DV media addon for Haiku + * + * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn) + * Distributed under the terms of the MIT License. + * + */ + +#ifndef __FIREWIRE_CARD_H +#define __FIREWIRE_CARD_H + +class FireWireCard +{ +public: + enum fmt_type { + FMT_MPEGTS = 0, + FMT_DV = 1, + }; + FireWireCard(const char *path); + ~FireWireCard(); + + status_t InitCheck(); + + status_t DetectRecvFn(); + ssize_t Read(void **buffer); + status_t Extract(void *dest, void **src, ssize_t *sizeUsed); + + void GetBufInfo(size_t *rbufsize, int *rcount); + +private: + ssize_t DvRead(void **buffer); + status_t DvExtract(void *dest, void **src, ssize_t *sizeUsed); + ssize_t MpegtsRead(void **buffer); + status_t MpegtsExtract(void *dest, void **src, ssize_t *sizeUsed); + + + status_t fInitStatus; + int fDev; + void *fBuf; + void *fPad; + size_t fRbufSize; + int fRcount; + fmt_type fFormat; + +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVAddOn.cpp b/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVAddOn.cpp new file mode 100644 index 0000000000..2e2f991abd --- /dev/null +++ b/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVAddOn.cpp @@ -0,0 +1,183 @@ +/* + * FireWire DV media addon for Haiku + * + * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn) + * Distributed under the terms of the MIT License. + * + * Based on DVB media addon + * Copyright (c) 2004-2007 Marcus Overhagen + */ + +#include "FireWireDVAddOn.h" +#include "FireWireCard.h" +#include "FireWireDVNode.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" + +struct device_info +{ + FireWireCard * card; + char name[16]; + media_format in_formats[1]; + media_format out_formats[1]; + flavor_info flavor; +}; + +extern "C" BMediaAddOn * +make_media_addon(image_id id) +{ + CALLED(); + return new FireWireDVAddOn(id); +} + +FireWireDVAddOn::~FireWireDVAddOn() +{ + CALLED(); + FreeDeviceList(); +} + +FireWireDVAddOn::FireWireDVAddOn(image_id id) + : BMediaAddOn(id) +{ + CALLED(); + ScanFolder("/dev/bus/fw"); +} + +status_t +FireWireDVAddOn::InitCheck(const char **out_failure_text) +{ + CALLED(); + if (!fDeviceList.CountItems()) { + *out_failure_text = "No supported device found"; + return B_ERROR; + } + return B_OK; +} + +int32 +FireWireDVAddOn::CountFlavors() +{ + CALLED(); + return fDeviceList.CountItems(); +} + +status_t +FireWireDVAddOn::GetFlavorAt(int32 n, const flavor_info **out_info) +{ + CALLED(); + device_info *dev = (device_info *)fDeviceList.ItemAt(n); + if (!dev) + return B_ERROR; + *out_info = &dev->flavor; + return B_OK; +} + +BMediaNode * +FireWireDVAddOn::InstantiateNodeFor(const flavor_info *info, BMessage *config, status_t *out_error) +{ + CALLED(); + device_info *dev = (device_info *)fDeviceList.ItemAt(info->internal_id); + if (!dev || dev->flavor.internal_id != info->internal_id) { + *out_error = B_ERROR; + return NULL; + } + *out_error = B_OK; + + return new FireWireDVNode(this, dev->name, dev->flavor.internal_id, dev->card); +} + +bool +FireWireDVAddOn::WantsAutoStart() +{ + CALLED(); + return false; +} + +status_t +FireWireDVAddOn::AutoStart(int index, BMediaNode **outNode, int32 *outInternalID, bool *outHasMore) +{ + CALLED(); + return B_ERROR; +} + +void +FireWireDVAddOn::ScanFolder(const char *path) +{ + CALLED(); + BDirectory dir(path); + if (dir.InitCheck() != B_OK) + return; + + BEntry ent; + + while (dir.GetNextEntry(&ent) == B_OK) { + BPath path(&ent); + if (!ent.IsDirectory() && !ent.IsSymLink()) { + FireWireCard *card = new FireWireCard(path.Path()); + if (card->InitCheck() == B_OK) + AddDevice(card, path.Path()); + else + delete card; + } + } +} + +void +FireWireDVAddOn::AddDevice(FireWireCard *card, const char *path) +{ + const char *fwnumber; + + // get card name, info and type + + // get interface number + const char *p = strrchr(path, '/'); + fwnumber = p ? p + 1 : ""; + + // create device_info + device_info *dev = new device_info; + fDeviceList.AddItem(dev); + + dev->card = card; + + sprintf(dev->name, "FireWire-%s", fwnumber); + + // setup supported formats (the lazy way) + memset(dev->in_formats, 0, sizeof(dev->in_formats)); + memset(dev->out_formats, 0, sizeof(dev->out_formats)); + dev->in_formats[1].type = B_MEDIA_ENCODED_VIDEO; + dev->out_formats[1].type = B_MEDIA_ENCODED_VIDEO; + + // setup flavor info + dev->flavor.name = dev->name; + dev->flavor.info = "The FireWireDVNode outputs to fw_raw drivers."; + dev->flavor.kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER + | B_CONTROLLABLE | B_PHYSICAL_OUTPUT | B_PHYSICAL_INPUT; + dev->flavor.flavor_flags = B_FLAVOR_IS_GLOBAL; + dev->flavor.internal_id = fDeviceList.CountItems() - 1; + dev->flavor.possible_count = 1; + dev->flavor.in_format_count = 1; + dev->flavor.in_format_flags = 0; + dev->flavor.in_formats = dev->in_formats; + dev->flavor.out_format_count = 1; + dev->flavor.out_format_flags = 0; + dev->flavor.out_formats = dev->out_formats; +} + + +void +FireWireDVAddOn::FreeDeviceList() +{ + device_info *dev; + while ((dev = (device_info *)fDeviceList.RemoveItem((int32)0))) { + delete dev->card; + delete dev; + } +} diff --git a/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVAddOn.h b/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVAddOn.h new file mode 100644 index 0000000000..5bb39bb28a --- /dev/null +++ b/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVAddOn.h @@ -0,0 +1,45 @@ +/* + * FireWire DV media addon for Haiku + * + * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn) + * Distributed under the terms of the MIT License. + * + * Based on DVB media addon + * Copyright (c) 2004-2007 Marcus Overhagen + */ + +#ifndef _FIREWIRE_DV_ADDON_H_ +#define _FIREWIRE_DV_ADDON_H_ + +#include + +class FireWireCard; + +class FireWireDVAddOn : public BMediaAddOn +{ +public: + FireWireDVAddOn(image_id id); + ~FireWireDVAddOn(); + + status_t InitCheck(const char **out_failure_text); + + int32 CountFlavors(); + + status_t GetFlavorAt(int32 n, const flavor_info **out_info); + + BMediaNode *InstantiateNodeFor(const flavor_info *info, BMessage *config, status_t *out_error); + + bool WantsAutoStart(); + status_t AutoStart(int index, BMediaNode **outNode, int32 *outInternalID, bool *outHasMore); + +protected: + void ScanFolder(const char *path); + + void AddDevice(FireWireCard *card, const char *path); + void FreeDeviceList(); + +protected: + BList fDeviceList; +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVNode.cpp b/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVNode.cpp new file mode 100644 index 0000000000..8c140f0d4d --- /dev/null +++ b/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVNode.cpp @@ -0,0 +1,692 @@ +/* + * FireWire DV media addon for Haiku + * + * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn) + * Distributed under the terms of the MIT License. + * + * Based on DVB media addon + * Copyright (c) 2004-2007 Marcus Overhagen + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "FireWireDVNode.h" +#include "FireWireCard.h" +#include "debug.h" + +#define REVISION "unknown" +#define VERSION "1.0" +#define BUILD __DATE__ " "__TIME__ + +// debugging +#ifdef TRACE +# undef TRACE +#endif +#define TRACE_FIREWIRE_NODE +#ifdef TRACE_FIREWIRE_NODE +# define TRACE(x...) printf(x) +#else +# define TRACE(x...) +#endif + +#define RETURN_IF_ERROR(expr) { status_t e = (expr); if (e != B_OK) return e; } + +#define M_REFRESH_PARAMETER_WEB (BTimedEventQueue::B_USER_EVENT + 1) + +FireWireDVNode::FireWireDVNode( + BMediaAddOn *addon, const char *name, + int32 internal_id, FireWireCard *card) + : BMediaNode(name) + , BBufferProducer(B_MEDIA_ENCODED_VIDEO) + , BControllable() + , BMediaEventLooper() + , fOutputEnabledEncVideo(false) + , fCard(card) + , fCaptureThreadsActive(false) + , fThreadIdCardReader(-1) + , fTerminateThreads(false) + , fBufferGroupEncVideo(0) + , fCaptureActive(false) +{ + CALLED(); + + AddNodeKind(B_PHYSICAL_INPUT); +// AddNodeKind(B_PHYSICAL_OUTPUT); + + fInternalID = internal_id; + fAddOn = addon; + + fInitStatus = B_OK; + + fDefaultFormatEncVideo.type = B_MEDIA_ENCODED_VIDEO; +} + + +FireWireDVNode::~FireWireDVNode() +{ + CALLED(); + + StopCapture(); + fWeb = NULL; +} + + +/* BMediaNode */ + + +BMediaAddOn * +FireWireDVNode::AddOn(int32 *internal_id) const +{ + if (internal_id) + *internal_id = fInternalID; + return fAddOn; +} + + +status_t +FireWireDVNode::HandleMessage(int32 message, const void *data, size_t size) +{ + return B_ERROR; +} + + +void +FireWireDVNode::Preroll() +{ + /* This hook may be called before the node is started to give the hardware + * a chance to start. */ +} + + +void +FireWireDVNode::SetTimeSource(BTimeSource *time_source) +{ + CALLED(); +} + + +void +FireWireDVNode::SetRunMode(run_mode mode) +{ + CALLED(); + TRACE("FireWireDVNode::SetRunMode(%d)\n", mode); +} + + +/* BMediaEventLooper */ + + +void +FireWireDVNode::NodeRegistered() +{ + CALLED(); + + SetPriority(B_REAL_TIME_PRIORITY); + Run(); + + fOutputEncVideo.node = Node(); + fOutputEncVideo.source.port = ControlPort(); + fOutputEncVideo.source.id = 0; + fOutputEncVideo.destination = media_destination::null; + fOutputEncVideo.format = fDefaultFormatEncVideo; + strcpy(fOutputEncVideo.name, "encoded video"); + + RefreshParameterWeb(); +} + + +void +FireWireDVNode::HandleEvent(const media_timed_event *event, + bigtime_t lateness, bool realTimeEvent) +{ + + switch(event->type) + { + case M_REFRESH_PARAMETER_WEB: + RefreshParameterWeb(); + break; + case BTimedEventQueue::B_START: + HandleStart(event->event_time); + break; + case BTimedEventQueue::B_STOP: + HandleStop(); + break; + case BTimedEventQueue::B_WARP: + HandleTimeWarp(event->bigdata); + break; + case BTimedEventQueue::B_SEEK: + HandleSeek(event->bigdata); + break; + case BTimedEventQueue::B_HANDLE_BUFFER: + case BTimedEventQueue::B_DATA_STATUS: + case BTimedEventQueue::B_PARAMETER: + default: + TRACE("FireWireDVNode::HandleEvent: Unhandled event -- %lx\n", event->type); + break; + } +} + + +/* BBufferProducer */ + + +status_t +FireWireDVNode::FormatChangeRequested(const media_source &source, + const media_destination &destination, media_format *io_format, + int32 *_deprecated_) +{ + CALLED(); + + // we don't support any other formats, so we just reject any format changes. + return B_ERROR; +} + + +status_t +FireWireDVNode::GetNextOutput(int32 *cookie, media_output *out_output) +{ + CALLED(); + + if (*cookie == 0) { + *out_output = fOutputEncVideo; + *cookie += 1; + return B_OK; + } else { + return B_BAD_INDEX; + } +} + + +status_t +FireWireDVNode::DisposeOutputCookie(int32 cookie) +{ + CALLED(); + // do nothing because we don't use the cookie for anything special + return B_OK; +} + + +status_t +FireWireDVNode::SetBufferGroup(const media_source &source, BBufferGroup *group) +{ + CALLED(); + return B_ERROR; +} + + +status_t +FireWireDVNode::VideoClippingChanged(const media_source &for_source, + int16 num_shorts, int16 *clip_data, + const media_video_display_info &display, int32 *_deprecated_) +{ + CALLED(); + return B_ERROR; +} + + +status_t +FireWireDVNode::GetLatency(bigtime_t *out_latency) +{ + CALLED(); + + *out_latency = EventLatency() + SchedulingLatency(); + return B_OK; +} + + +status_t +FireWireDVNode::FormatSuggestionRequested( + media_type type, int32 quality, media_format *format) +{ + CALLED(); + + if (format == NULL) { + fprintf(stderr, "\tERROR - NULL format pointer passed in!\n"); + return B_BAD_VALUE; + } + + // this is the format we'll be returning (our preferred format) + *format = fDefaultFormatEncVideo; + + // a wildcard type is okay; we can specialize it + if (type == B_MEDIA_UNKNOWN_TYPE) + type = B_MEDIA_ENCODED_VIDEO; + + if (type != B_MEDIA_ENCODED_VIDEO) + return B_MEDIA_BAD_FORMAT; + + return B_OK; +} + + +status_t +FireWireDVNode::FormatProposal(const media_source &source, media_format *format) +{ + CALLED(); + /* The connection process: + * we are here => BBufferProducer::FormatProposal + * BBufferConsumer::AcceptFormat + * BBufferProducer::PrepareToConnect + * BBufferConsumer::Connected + * BBufferProducer::Connect + * + * What we need to do: + * - if the format contains a wildcard AND we have a requirement for that + * field, set it to the value we need. + * - if a field has a value that is not wildcard and not supported by us, + * we don't change it, and return B_MEDIA_BAD_FORMAT + * - after we are done, the format may still contain wildcards. + */ + + if (source.port != ControlPort()) { + fprintf(stderr, "FireWireDVNode::FormatProposal returning " + "B_MEDIA_BAD_SOURCE\n"); + return B_MEDIA_BAD_SOURCE; + } + + media_type requestedType = format->type; + *format = fDefaultFormatEncVideo; + + if (requestedType != B_MEDIA_UNKNOWN_TYPE + && requestedType != B_MEDIA_ENCODED_VIDEO) { + fprintf(stderr, "FireWireDVNode::FormatProposal returning " + "B_MEDIA_BAD_FORMAT\n"); + return B_MEDIA_BAD_FORMAT; + } + + // encoded video or wildcard type, either is okay by us + return B_OK; +} + + +status_t +FireWireDVNode::PrepareToConnect(const media_source &source, + const media_destination &destination, media_format *format, + media_source *out_source, char *out_name) +{ + /* The connection process: + * BBufferProducer::FormatProposal + * BBufferConsumer::AcceptFormat + * we are here => BBufferProducer::PrepareToConnect + * BBufferConsumer::Connected + * BBufferProducer::Connect + * + * At this point, the consumer's AcceptFormat() method has been called, + * and that node has potentially changed the proposed format. It may + * also have left wildcards in the format. PrepareToConnect() + * *must* fully specialize the format before returning! + */ + + CALLED(); + // is the source valid? + if (source.port != ControlPort() && + fCard->DetectRecvFn() != B_OK) { + fprintf(stderr, "FireWireDVNode::PrepareToConnect returning " + "B_MEDIA_BAD_SOURCE\n"); + return B_MEDIA_BAD_SOURCE; + } + + // are we already connected? + if (fOutputEncVideo.destination != media_destination::null) + return B_MEDIA_ALREADY_CONNECTED; + + // the format may not yet be fully specialized (the consumer might have + // passed back some wildcards). Finish specializing it now, and return an + // error if we don't support the requested format. + if (format->type != B_MEDIA_RAW_AUDIO) { + fprintf(stderr, "\tnon-raw-audio format?!\n"); + return B_MEDIA_BAD_FORMAT; + } + + // reserve the connection by setting destination + // set the output's format to the new format + fOutputEncVideo.destination = destination; + fOutputEncVideo.format = *format; + + // set source and suggest a name + *out_source = source; + strcpy(out_name, "encoded video"); + + return B_OK; +} + + +void +FireWireDVNode::Connect(status_t error, const media_source &source, + const media_destination &destination, const media_format &format, + char *io_name) +{ + /* The connection process: + * BBufferProducer::FormatProposal + * BBufferConsumer::AcceptFormat + * BBufferProducer::PrepareToConnect + * BBufferConsumer::Connected + * we are here => BBufferProducer::Connect + */ + + CALLED(); + + if (error != B_OK) { + TRACE("Error during connecting\n"); + // if an error occured, unreserve the connection + fOutputEncVideo.destination = media_destination::null; + fOutputEncVideo.format = fDefaultFormatEncVideo; + return; + } + + // Since the destination is allowed to be changed by the + // consumer, the one we got in PrepareToConnect() is no + // longer correct, and must be updated here. + fOutputEncVideo.destination = destination; + fOutputEncVideo.format = format; + + // if the connection has no name, we set it now + if (strlen(io_name) == 0) + strcpy(io_name, "encoded video"); + + #ifdef DEBUG + bigtime_t latency; + media_node_id ts; + if (B_OK != FindLatencyFor(destination, &latency, &ts)) + TRACE("FindLatencyFor failed\n"); + else + TRACE("downstream latency %Ld\n", latency); + #endif +} + + +void +FireWireDVNode::Disconnect(const media_source &source, + const media_destination &destination) +{ + CALLED(); + + // unreserve the connection + fOutputEncVideo.destination = media_destination::null; + fOutputEncVideo.format = fDefaultFormatEncVideo; +} + + +void +FireWireDVNode::LateNoticeReceived(const media_source &source, + bigtime_t how_much, bigtime_t performance_time) +{ + TRACE("FireWireDVNode::LateNoticeReceived %Ld late at %Ld\n", how_much, performance_time); +} + + +void +FireWireDVNode::EnableOutput(const media_source &source, bool enabled, + int32 *_deprecated_) +{ + CALLED(); + fOutputEnabledEncVideo = enabled; +} + + +void +FireWireDVNode::AdditionalBufferRequested(const media_source &source, + media_buffer_id prev_buffer, bigtime_t prev_time, + const media_seek_tag *prev_tag) +{ + CALLED(); + // we don't support offline mode + return; +} + + +/* FireWireDVNode */ + + +void +FireWireDVNode::HandleTimeWarp(bigtime_t performance_time) +{ + TRACE("FireWireDVNode::HandleTimeWarp at %Ld\n", performance_time); +} + + +void +FireWireDVNode::HandleSeek(bigtime_t performance_time) +{ + TRACE("FireWireDVNode::HandleSeek at %Ld\n", performance_time); +} + + +void +FireWireDVNode::HandleStart(bigtime_t performance_time) +{ + CALLED(); + StartCapture(); +} + + +void +FireWireDVNode::HandleStop(void) +{ + CALLED(); + StopCapture(); +} + + +status_t +FireWireDVNode::StartCapture() +{ + CALLED(); + + if (fCaptureActive) + return B_OK; + + RETURN_IF_ERROR(StopCaptureThreads()); + + RETURN_IF_ERROR(StartCaptureThreads()); + + fCaptureActive = true; + + RefreshParameterWeb(); + + return B_OK; +} + + +status_t +FireWireDVNode::StopCapture() +{ + CALLED(); + if (!fCaptureActive) + return B_OK; + + StopCaptureThreads(); + + fCaptureActive = false; + return B_OK; +} + + +status_t +FireWireDVNode::StartCaptureThreads() +{ + CALLED(); + + if (fCaptureThreadsActive) + return B_OK; + + fTerminateThreads = false; + + fThreadIdCardReader = spawn_thread(_card_reader_thread_, "FireWire DV reader", 120, this); + resume_thread(fThreadIdCardReader); + + fCaptureThreadsActive = true; + return B_OK; +} + + +status_t +FireWireDVNode::StopCaptureThreads() +{ + CALLED(); + + if (!fCaptureThreadsActive) + return B_OK; + + fTerminateThreads = true; + + status_t dummy; // NULL as parameter does not work + wait_for_thread(fThreadIdCardReader, &dummy); + + fCaptureThreadsActive = false; + return B_OK; +} + + +int32 +FireWireDVNode::_card_reader_thread_(void *arg) +{ + static_cast(arg)->card_reader_thread(); + return 0; +} + + +void +FireWireDVNode::card_reader_thread() +{ + status_t err; + size_t rbufsize; + int rcount; + + fCard->GetBufInfo(&rbufsize, &rcount); + delete fBufferGroupEncVideo; + fBufferGroupEncVideo = new BBufferGroup(rbufsize, rcount); + while (!fTerminateThreads) { + void *data, *end; + ssize_t sizeUsed = fCard->Read(&data); + if (sizeUsed < 0) { + TRACE("FireWireDVNode::%s: %s\n", __FUNCTION__, + strerror(sizeUsed)); + continue; + } + + end = (char *)data + sizeUsed; + + while (data < end) { + BBuffer *buf = fBufferGroupEncVideo->RequestBuffer(rbufsize, 10000); + if (!buf) { + TRACE("OutVideo: request buffer timout\n"); + continue; + } + + err = fCard->Extract(buf->Data(), &data, &sizeUsed); + if (err) { + buf->Recycle(); + printf("OutVideo Extract error %s\n", strerror(err)); + continue; + } + + media_header* hdr = buf->Header(); + hdr->type = B_MEDIA_ENCODED_VIDEO; + hdr->size_used = sizeUsed; + hdr->time_source = TimeSource()->ID(); // set time source id + //what should the start_time be? + hdr->start_time = TimeSource()->PerformanceTimeFor(system_time()); + + lock.Lock(); + if (B_OK != SendBuffer(buf, fOutputEncVideo.destination)) { + TRACE("OutVideo: sending buffer failed\n"); + buf->Recycle(); + } + lock.Unlock(); + } + + } +} + + +void +FireWireDVNode::RefreshParameterWeb() +{ + TRACE("FireWireDVNode::RefreshParameterWeb enter\n"); + fWeb = CreateParameterWeb(); + SetParameterWeb(fWeb); + TRACE("FireWireDVNode::RefreshParameterWeb finished\n"); +} + + +void +FireWireDVNode::SetAboutInfo(BParameterGroup *about) +{ + //May need more useful infomation? + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "FireWireDV media_addon info:", B_GENERIC); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Version " VERSION, B_GENERIC); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Revision " REVISION, B_GENERIC); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Build " BUILD, B_GENERIC); + + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "", B_GENERIC); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Driver info:", B_GENERIC); +} + + +BParameterWeb * +FireWireDVNode::CreateParameterWeb() +{ + /* Set up the parameter web */ + BParameterWeb *web = new BParameterWeb(); + + BString name; + name << Name(); + + BParameterGroup *main = web->MakeGroup(name.String()); + + if (!fCaptureActive) { + BParameterGroup *info = main->MakeGroup("Info"); + info->MakeNullParameter(0, B_MEDIA_NO_TYPE, info->Name(), B_GENERIC); + BParameterGroup *about = main->MakeGroup("About"); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, about->Name(), B_GENERIC); + SetAboutInfo(about); + info->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Node is stopped", B_GENERIC); + info->MakeNullParameter(0, B_MEDIA_NO_TYPE, "or tuning failed.", B_GENERIC); + return web; + } + + BParameterGroup *about = main->MakeGroup("About"); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, about->Name(), B_GENERIC); + SetAboutInfo(about); + + return web; +} + + +status_t +FireWireDVNode::GetParameterValue(int32 id, bigtime_t *last_change, void *value, size_t *size) +{ + TRACE("FireWireDVNode::GetParameterValue, id 0x%lx\n", id); + //do we need Parameter for firewire dv? + return B_OK; +} + + +void +FireWireDVNode::SetParameterValue(int32 id, bigtime_t when, const void *value, size_t size) +{ + TRACE("FireWireDVNode::SetParameterValue, id 0x%lx, size %ld, value 0x%lx\n", id, size, *(const int32 *)value); + //do we need parameter for firewire dv? + TRACE("FireWireDVNode::SetParameterValue finished\n"); +} diff --git a/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVNode.h b/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVNode.h new file mode 100644 index 0000000000..47f9004c24 --- /dev/null +++ b/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVNode.h @@ -0,0 +1,145 @@ +/* + * FireWire DV media addon for Haiku + * + * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn) + * Distributed under the terms of the MIT License. + * + * Based on DVB media addon + * Copyright (c) 2004-2007 Marcus Overhagen + */ + +#ifndef __FIREWIRE_DV_NODE_H +#define __FIREWIRE_DV_NODE_H + + +#include +#include +//#include +#include +#include +#include +#include +#include + +class FireWireCard; +class BParameterGroup; + +class FireWireDVNode : public virtual BBufferProducer, + public virtual BControllable, public virtual BMediaEventLooper { +public: + FireWireDVNode(BMediaAddOn *addon, + const char *name, int32 internal_id, FireWireCard *card); +virtual ~FireWireDVNode(); + +virtual status_t InitCheck() const { return fInitStatus; } + +/* BMediaNode */ +public: +virtual BMediaAddOn *AddOn(int32 * internal_id) const; +virtual status_t HandleMessage(int32 message, const void *data, + size_t size); +protected: +virtual void Preroll(); +virtual void SetTimeSource(BTimeSource * time_source); +virtual void SetRunMode(run_mode mode); + +/* BMediaEventLooper */ +protected: +virtual void NodeRegistered(); + +virtual void HandleEvent(const media_timed_event *event, + bigtime_t lateness, bool realTimeEvent = false); + +/* BBufferProducer */ +protected: +virtual status_t FormatSuggestionRequested(media_type type, int32 quality, + media_format * format); +virtual status_t FormatProposal(const media_source &source, + media_format *format); +virtual status_t FormatChangeRequested(const media_source &source, + const media_destination &destination, + media_format *io_format, int32 *_deprecated_); +virtual status_t GetNextOutput(int32 * cookie, media_output * out_output); +virtual status_t DisposeOutputCookie(int32 cookie); +virtual status_t SetBufferGroup(const media_source &for_source, + BBufferGroup * group); +virtual status_t VideoClippingChanged(const media_source &for_source, + int16 num_shorts, int16 *clip_data, + const media_video_display_info &display, + int32 * _deprecated_); +virtual status_t GetLatency(bigtime_t * out_latency); +virtual status_t PrepareToConnect(const media_source &what, + const media_destination &where, + media_format *format, + media_source *out_source, char *out_name); +virtual void Connect(status_t error, const media_source &source, + const media_destination &destination, + const media_format & format, char *io_name); +virtual void Disconnect(const media_source & what, + const media_destination & where); +virtual void LateNoticeReceived(const media_source & what, + bigtime_t how_much, bigtime_t performance_time); +virtual void EnableOutput(const media_source & what, bool enabled, + int32 * _deprecated_); +virtual void AdditionalBufferRequested(const media_source & source, + media_buffer_id prev_buffer, bigtime_t prev_time, + const media_seek_tag * prev_tag); + +/* BControllable */ +protected: +virtual status_t GetParameterValue(int32 id, bigtime_t *last_change, + void *value, size_t *size); +virtual void SetParameterValue(int32 id, bigtime_t when, + const void *value, size_t size); + +/* state */ +private: + void HandleStart(bigtime_t performance_time); + void HandleStop(); + void HandleTimeWarp(bigtime_t performance_time); + void HandleSeek(bigtime_t performance_time); + + + BParameterWeb * CreateParameterWeb(); + void SetAboutInfo(BParameterGroup *about); + + void RefreshParameterWeb(); + + status_t StartCapture(); + status_t StopCapture(); + status_t StartCaptureThreads(); + status_t StopCaptureThreads(); + + static int32 _card_reader_thread_(void *arg); + + void card_reader_thread(); + + status_t fInitStatus; + + int32 fInternalID; + BMediaAddOn *fAddOn; + + bool fOutputEnabledEncVideo; + + media_output fOutputEncVideo; + + media_format fDefaultFormatEncVideo; + + FireWireCard * fCard; + + bool fCaptureThreadsActive; + + thread_id fThreadIdCardReader; + + volatile bool fTerminateThreads; + + BBufferGroup * fBufferGroupEncVideo; + + BParameterWeb * fWeb; + + bool fCaptureActive; + BLocker lock; + +}; + +#endif // __FIREWIRE_DV_NODE_H diff --git a/src/add-ons/media/media-add-ons/firewire_dv/Jamfile b/src/add-ons/media/media-add-ons/firewire_dv/Jamfile new file mode 100644 index 0000000000..4c01822d15 --- /dev/null +++ b/src/add-ons/media/media-add-ons/firewire_dv/Jamfile @@ -0,0 +1,12 @@ +SubDir HAIKU_TOP src add-ons media media-add-ons firewire_dv ; + +UseHeaders [ FDirName $(HAIKU_TOP) headers compatibility bsd ] : true ; +UsePrivateHeaders media ; +UsePrivateHeaders firewire ; + +Addon firewire_dv.media_addon : + FireWireCard.cpp + FireWireDVAddOn.cpp + FireWireDVNode.cpp + : be media +; diff --git a/src/add-ons/media/media-add-ons/firewire_dv/debug.h b/src/add-ons/media/media-add-ons/firewire_dv/debug.h new file mode 100644 index 0000000000..835882f2e8 --- /dev/null +++ b/src/add-ons/media/media-add-ons/firewire_dv/debug.h @@ -0,0 +1,34 @@ + +#include + +#ifndef NDEBUG + + #ifndef DEBUG + #define DEBUG 2 + #endif + + #if DEBUG >= 1 + #define UNIMPLEMENTED() printf("UNIMPLEMENTED %s\n",__PRETTY_FUNCTION__) + #else + #define UNIMPLEMENTED() ((void)0) + #endif + + #if DEBUG >= 2 + #define BROKEN() printf("BROKEN %s\n",__PRETTY_FUNCTION__) + #else + #define BROKEN() ((void)0) + #endif + + #if DEBUG >= 3 + #define CALLED() printf("CALLED %s\n",__PRETTY_FUNCTION__) + #else + #define CALLED() ((void)0) + #endif + +#else + + #define UNIMPLEMENTED() ((void)0) + #define BROKEN() ((void)0) + #define CALLED() ((void)0) + +#endif diff --git a/src/add-ons/media/media-add-ons/firewire_dv/glue.h b/src/add-ons/media/media-add-ons/firewire_dv/glue.h new file mode 100644 index 0000000000..9f91046eb5 --- /dev/null +++ b/src/add-ons/media/media-add-ons/firewire_dv/glue.h @@ -0,0 +1,27 @@ +/* + * FireWire DV media addon for Haiku + * + * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn) + * Distributed under the terms of the MIT License. + * + */ +#ifndef __FIREWIRE_CARD_GLUE_H +#define __FIREWIRE_CRAD_GLUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "eui64.h" +#include "firewire.h" +#include "iec13213.h" +#include "fwphyreg.h" +#include "iec68113.h" + +#ifdef __cplusplus +} +#endif + +#endif