Add the current version of JiSheng Zhang's firewire_dv media add-on to the

build. This is mostly complete, excepting BBufferConsumer's virtuals are not
yet implemented, and needs more testing to ensure that the encoded stream is
correctly read from the camera, though based off preliminary tests by Francois,
detection/publishing at least seems to work.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27965 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Rene Gollent 2008-10-11 04:52:20 +00:00
parent 00acef29d6
commit b99c736b83
11 changed files with 1587 additions and 0 deletions

View File

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

View File

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

View File

@ -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 <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 "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<<ciph->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;
}

View File

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

View File

@ -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 <marcus@overhagen.de>
*/
#include "FireWireDVAddOn.h"
#include "FireWireCard.h"
#include "FireWireDVNode.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <Alert.h>
#include <Directory.h>
#include <Entry.h>
#include <Path.h>
#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;
}
}

View File

@ -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 <marcus@overhagen.de>
*/
#ifndef _FIREWIRE_DV_ADDON_H_
#define _FIREWIRE_DV_ADDON_H_
#include <MediaAddOn.h>
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

View File

@ -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 <marcus@overhagen.de>
*/
#include <fcntl.h>
#include <malloc.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>
#include <MediaRoster.h>
#include <Buffer.h>
#include <BufferGroup.h>
#include <ParameterWeb.h>
#include <TimeSource.h>
#include <String.h>
#include <Autolock.h>
#include <Debug.h>
#include <Directory.h>
#include <Entry.h>
#include <Path.h>
#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<FireWireDVNode *>(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");
}

View File

@ -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 <marcus@overhagen.de>
*/
#ifndef __FIREWIRE_DV_NODE_H
#define __FIREWIRE_DV_NODE_H
#include <OS.h>
#include <media/BufferProducer.h>
//#include <media/BufferConsumer.h>
#include <media/Controllable.h>
#include <media/MediaDefs.h>
#include <media/MediaEventLooper.h>
#include <media/MediaNode.h>
#include <support/Locker.h>
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

View File

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

View File

@ -0,0 +1,34 @@
#include <stdio.h>
#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

View File

@ -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 <stdint.h>
#include <endian.h>
#include "eui64.h"
#include "firewire.h"
#include "iec13213.h"
#include "fwphyreg.h"
#include "iec68113.h"
#ifdef __cplusplus
}
#endif
#endif