2 * This file is essentially a rewrite of video4dc1394.cxx
3 * Check that one for more explanations
5 * A lot of code "borrowed" from
6 * - dvgrab.c from libdv (http://libdv.sf.net/)
7 * - kino (http://kino.schirmacher.de/)
8 * - video4dc1394.cxx from ptlib
9 * - ... probably others too
11 * The code is highly experimental.
12 * You should expect any of :
13 * - plenty of segfaults
14 * - loss of performance
15 * - not working at all for you
17 * Known Bugs / Limitations / Room for improvement (feel free to patch/suggest)
18 * - Colors are no good after a Convert
19 * Can someone look at the code and tell me what I have to tell the convert function
20 * that my source colors are? I thought it is pure RGB24, but obviously not.
21 * Dumping the binary data directly to a PPM file works like a charm, though :-/
22 * - Occasional segfaults (I think these are fixed, but don't be surprised if it works not)
23 * - grabs first camera by default (not sure how to go about selection of cameras/ports)
24 * - still haven't figured what the channel parameter in start_iso_rcv(handle,channel) means,
25 * but it seems that we are in for a long wait if channel != 63
26 * - code depends on libavc1394 to figure out if a device is a camera
27 * I am not really sure there isn't a smarter way of going about it
28 * since capturing video only requires libraw1394 (for the moment)
29 * Maybe we can drop that dependency?
30 * - Still not sure how to go about setting frame size.
31 * Resizing manually at the moment, since the frame size of a captured frame
32 * from an AVC camera is not settable.
33 * An AVC camera supports either NTSC (720x480) or PAL (720x576) resolution
34 * and the only way to check which one it is, seems to be to parse the header
35 * of a captured frame. Will wait for a suggestion on the proper way to handle it.
36 * - bus resets not handled (yet?)
37 * - Still not sure what to use the device name for (beats me :( )
38 * - not sure if TRY_1394AVC and TRY_1394DC will not break something if used at the same time
39 * - Overuse of PTRACE?
40 * - I am not sure how most of the stuff works
41 * - ... everything else
44 * ------------------------------------------------------------
47 * This module was tested against:
51 * Sony DCR-TRV20 NTSC (http://www.sony.jp/products/Consumer/VD/DCR-TRV20/)
52 * Texas Instruments TSB12LV26 IEEE-1394 Controller
54 * Linux vanilla kernel 2.4.20
59 * Author: Georgi Georgiev <chutz@gg3.net>
63 #pragma implementation "videoio1394avc.h"
66 #include <ptlib/videoio.h>
67 #include <ptlib/videoio1394avc.h>
68 #include <ptlib/vconvert.h>
69 #include <ptlib/file.h>
70 #include <sys/utsname.h>
72 //#define ESTIMATE_CAPTURE_PERFORMANCE
74 #ifdef ESTIMATE_CAPTURE_PERFORMANCE
76 static PInt64 start_time
;
77 static int num_captured
;
80 #ifndef RAW_BUFFER_SIZE
81 #define RAW_BUFFER_SIZE 512
84 static u_int8_t raw_buffer
[RAW_BUFFER_SIZE
];
87 ///////////////////////////////////////////////////////////////////////////////
90 PVideoInput1394AvcDevice::PVideoInput1394AvcDevice()
92 PTRACE(3, "PVideo1394AvcDevice::PVideo1394AvcDevice()");
95 capturing_duration
= 10000; // arbitrary large value suffices
99 PVideoInput1394AvcDevice::~PVideoInput1394AvcDevice()
104 static int kernel_version_ok(void)
106 PTRACE(3, "kernel_version_ok()");
107 raw1394handle_t handle
;
108 handle
= raw1394_new_handle();
109 if (handle
== NULL
&& errno
== 0) {
112 raw1394_destroy_handle(handle
);
116 BOOL
PVideoInput1394AvcDevice::Open(const PString
& devName
, BOOL startImmediate
)
118 PTRACE(3, "PVideoInput1394AvcDevice::Open");
119 if (!kernel_version_ok()) {
120 PTRACE(0, "The Linux kernel version is not compatible");
125 PTRACE(0, "You cannot open PVideoInput1394AvcDevice twice.");
129 // Haven't figure how to use DMA, but let's set anyways
130 // Not sure what we need device name for anyway
131 if (devName
== "/dev/raw1394")
133 // Don't forget /dev/video1394/0
134 else if (strncmp(devName
, "/dev/video1394", 14) == 0)
137 PTRACE(0, "devName must be /dev/raw1394 or /dev/video1394*");
141 // See if devName is accessible.
142 if (!PFile::Exists(devName
)) {
143 PTRACE(1, devName
<< " is not accessible.");
147 /*-----------------------------------------------------------------------
148 * Open ohci and asign handle to it
149 *-----------------------------------------------------------------------*/
150 handle
= raw1394_new_handle();
153 PTRACE(0, "Unable to aquire a raw1394 handle"
154 "did you insmod the drivers?\n");
158 /*-----------------------------------------------------------------------
159 * get the camera nodes and describe them as we find them
160 *-----------------------------------------------------------------------*/
161 // Still not letting the user choose a camera though
162 struct raw1394_portinfo g_pinf
[16];
163 int num_cards
= raw1394_get_port_info(handle
, g_pinf
, 16);
165 PTRACE(0,"Couldn't get card info");
166 raw1394_destroy_handle(handle
);
170 for (int i
=0;i
<num_cards
;i
++) {
171 PTRACE(3, "Found card "<< g_pinf
[i
].name
<< " with " << g_pinf
[i
].nodes
<< " nodes");
175 if (numCameras
< 1) {
176 PTRACE(0, "no cameras found");
177 raw1394_destroy_handle(handle
);
182 // SetFrameSize(CIFHeight, CIFWidth);
184 frameWidth = CIFHeight;
185 frameHeight = CIFWidth;
186 // colourFormat = "UYVY422";
187 colourFormat = "RGB24";
189 frameWidth
= CIFWidth
;
190 frameHeight
= CIFHeight
;
191 colourFormat
= "RGB24F";
192 desiredFrameHeight
= CIFHeight
;
193 desiredFrameWidth
= CIFWidth
;
194 desiredColourFormat
= "RGB24F";
196 capturing_duration
= 10000; // arbitrary large value suffices
197 deviceName
= devName
;
199 // select the specified input and video format
201 if (!SetChannel(channelNumber
) ||
202 !SetVideoFormat(videoFormat
)) {
203 PTRACE(1, "SetChannel() or SetVideoFormat() failed");
208 if (startImmediate
&& !Start()) {
212 PTRACE(3, "Successfully opened avc1394");
217 BOOL
PVideoInput1394AvcDevice::IsOpen()
219 return handle
!= NULL
;
223 BOOL
PVideoInput1394AvcDevice::Close()
225 PTRACE(3, "Close()");
229 raw1394_destroy_handle(handle
);
236 BOOL
PVideoInput1394AvcDevice::Start()
238 PTRACE(3, "Start()");
239 if (!IsOpen()) return FALSE
;
240 if (IsCapturing()) return TRUE
;
242 if (frameWidth == 320 && frameHeight == 240)
244 else if (frameWidth == 160 && frameHeight == 120)
247 PTRACE(1, "Frame size is neither 320x240 or 160x120" << frameWidth <<
252 PTRACE(1, "Using " << deviceName
<< " at channel " << channelNumber
);
254 /*-----------------------------------------------------------------------
255 * have the camera start sending us data
256 *-----------------------------------------------------------------------*/
257 // will figure channel numbers later // channelNumber = 63??
258 if (raw1394_set_iso_handler(handle
, 63, & RawISOHandler
)!= NULL
) {
259 PTRACE (0, "Cannot set_iso_handler");
264 if (raw1394_start_iso_rcv(handle
, 63) < 0) {
265 PTRACE (0, "Cannot start_iso_rcv");
270 #ifdef ESTIMATE_CAPTURE_PERFORMANCE
272 start_time
= now
.GetTimestamp();
279 BOOL
PVideoInput1394AvcDevice::Stop()
285 raw1394_stop_iso_rcv(handle
, 63);
287 is_capturing
= FALSE
;
294 BOOL
PVideoInput1394AvcDevice::IsCapturing()
299 PStringList
PVideoInput1394AvcDevice::GetInputDeviceNames()
301 PTRACE(3, "GetInputDeviceNames()");
304 // The control of the device does not require the device name anywhere, but still...
305 if (PFile::Exists("/dev/raw1394"))
306 list
.AppendString("/dev/raw1394");
307 if (PFile::Exists("/dev/video1394/0"))
308 // DEVFS naming scheme
309 for (int i
=0; ; i
++) {
310 PString devname
= PString("/dev/video1394/") + PString(i
);
311 if (PFile::Exists(devname
))
312 list
.AppendString(devname
);
316 else if (PFile::Exists("/dev/video1394"))
317 /* traditional naming */
318 list
.AppendString("/dev/video1394");
323 BOOL
PVideoInput1394AvcDevice::SetVideoFormat(VideoFormat newFormat
)
325 PTRACE(3, "SetVideoFormat("<<newFormat
<<")");
326 if (!PVideoDevice::SetVideoFormat(newFormat
)) {
327 PTRACE(3,"PVideoDevice::SetVideoFormat\t failed for format "<<newFormat
);
333 int PVideoInput1394AvcDevice::GetBrightness()
339 BOOL
PVideoInput1394AvcDevice::SetBrightness(unsigned newBrightness
)
345 int PVideoInput1394AvcDevice::GetHue()
351 BOOL
PVideoInput1394AvcDevice::SetHue(unsigned newHue
)
357 int PVideoInput1394AvcDevice::GetContrast()
363 BOOL
PVideoInput1394AvcDevice::SetContrast(unsigned newContrast
)
368 BOOL
PVideoInput1394AvcDevice::SetColour(unsigned newColour
)
373 int PVideoInput1394AvcDevice::GetColour()
379 BOOL
PVideoInput1394AvcDevice::SetWhiteness(unsigned newWhiteness
)
384 int PVideoInput1394AvcDevice::GetWhiteness()
390 BOOL
PVideoInput1394AvcDevice::GetParameters (int *whiteness
, int *brightness
,
391 int *colour
, int *contrast
, int *hue
)
401 int PVideoInput1394AvcDevice::GetNumChannels()
407 BOOL
PVideoInput1394AvcDevice::SetChannel(int newChannel
)
409 PTRACE(3, "SetChannel("<<newChannel
<<")");
410 if (PVideoDevice::SetChannel(newChannel
) == FALSE
)
421 BOOL
PVideoInput1394AvcDevice::SetFrameRate(unsigned rate
)
423 return PVideoDevice::SetFrameRate(rate
);
427 BOOL
PVideoInput1394AvcDevice::GetFrameSizeLimits(unsigned & minWidth
,
428 unsigned & minHeight
,
430 unsigned & maxHeight
)
432 PTRACE(3, "GetFrameSizeLimits()");
442 PINDEX
PVideoInput1394AvcDevice::GetMaxFrameBytes()
444 PTRACE(3, "GetMaxFrameBytes()");
445 if (converter
!= NULL
) {
446 PINDEX bytes
= converter
->GetMaxDstFrameBytes();
447 if (bytes
> frameBytes
)
454 BOOL
dump_ppm(PString name
, int w
, int h
, BYTE
* data
)
456 PFile
file(name
, PFile::WriteOnly
);
457 if (!file
.IsOpen()) return FALSE
;
458 file
.WriteString("P6\n");
460 file
.WriteString("\n");
462 file
.WriteString("\n255\n");
463 file
.Write(data
, w
*h
*3);
468 BOOL
PVideoInput1394AvcDevice::GetFrameDataNoDelay(BYTE
* buffer
, PINDEX
* bytesReturned
)
470 PTRACE(3, "GetFrameDataNoDelay()");
471 if (!IsCapturing()) return FALSE
;
473 // get a DV frame first
475 BOOL frame_complete
= FALSE
;
476 BOOL found_first_frame
= FALSE
;
478 int broken_frames
= 0;
479 BYTE capture_buffer
[150000];
480 BYTE
* capture_buffer_end
= capture_buffer
;
482 if (raw1394_start_iso_rcv(handle
, 63) < 0) {
483 PTRACE(1, "Cannot receive data on channel 63");
486 while (! frame_complete
) {
487 raw1394_loop_iterate(handle
);
488 if (*(uint32_t *)raw_buffer
>= 492) {
489 if (!found_first_frame
) {
490 if (raw_buffer
[16] == 0x1f && raw_buffer
[17] == 0x07)
491 found_first_frame
= TRUE
;
495 PTRACE (0, "Skipped too many frames");
498 if (found_first_frame
) {
499 if (raw_buffer
[16] == 0x1f && raw_buffer
[17] == 0x07 && (capture_buffer_end
- capture_buffer
> 480)) {
500 // check for a short read. check if we read less than a NTSC frame
501 // because it is the smaller one. still not sure how to handle NTSC vs. PAL
502 if (capture_buffer_end
- capture_buffer
< 120000) {
504 capture_buffer_end
= capture_buffer
;
506 frame_complete
= TRUE
;
509 if (!frame_complete
) {
510 memcpy (capture_buffer_end
, raw_buffer
+16, 480);
511 capture_buffer_end
+= 480;
514 if (broken_frames
> 30) {
515 PTRACE(1, "Received too many broken frames from the camera. Giving up for now");
520 raw1394_stop_iso_rcv(handle
, 63);
521 PTRACE(3, "got "<< capture_buffer_end
- capture_buffer
<< " bytes... now convert DV -> RGB");
524 dv
= dv_decoder_new(TRUE
, FALSE
, FALSE
);
525 dv
->quality
= DV_QUALITY_BEST
;
526 if (dv_parse_header(dv
, capture_buffer
) < 0) {
527 PTRACE(0, "cannot parse header");
531 dv_color_space_t color_space
;
535 PTRACE(3, "Captured image from camera is w"<<dv
->width
<<"x"<<dv
->height
);
536 pitches
[0] = dv
->width
* 3;
537 pitches
[1] = pitches
[2] = 0;
538 pixels
[0] = (uint8_t *) malloc (dv
->width
* dv
->height
* 3);
539 pixels
[1] = pixels
[2] = NULL
;
540 color_space
= e_dv_color_rgb
;
542 dv_decode_full_frame(dv
, capture_buffer
, color_space
, pixels
, pitches
);
543 // dump_ppm("/tmp/ohphone-before.ppm", dv->width, dv->height, pixels[0]);
545 // gotta resize manually :( ... do I?
546 PTRACE(3, "Manual resize "<<dv
->width
<<"x"<<dv
->height
<<" -> "<<frameWidth
<<"x"<<frameHeight
);
547 float xRatio
= dv
->width
/ (float)frameWidth
;
548 float yRatio
= dv
->height
/ (float)frameHeight
;
549 for (uint y
=0;y
<frameHeight
;y
++)
550 for (uint x
=0;x
<frameWidth
;x
++) {
551 uint16_t sourceX
= (uint16_t) (x
* xRatio
);
552 uint16_t sourceY
= (uint16_t) (y
* yRatio
);
553 memcpy (pixels
[0]+3*(y
*frameWidth
+x
), pixels
[0]+3*(sourceY
*dv
->width
+sourceX
), 3);
554 // Temporary workaround for RGB -> BGR
557 int offset
= (y
*frameWidth
+x
)*3;
558 temp
= pixels
[0][offset
+0];
559 pixels
[0][offset
+0] = pixels
[0][offset
+2];
560 pixels
[0][offset
+2] = temp
;
564 // dump_ppm("/tmp/ohphone-after.ppm", frameWidth, frameHeight,pixels[0]);
565 if (converter
!= NULL
) {
566 converter
->Convert((const BYTE
*)pixels
[0], buffer
, bytesReturned
);
567 if (pixels
[0] != NULL
) {
571 PTRACE(1, "Converter must exist. Something goes wrong.");
574 PTRACE(3, "1394avc: return frame: "<< frameWidth
<< "x"<<frameHeight
);
575 #ifdef ESTIMATE_CAPTURE_PERFORMANCE
578 double capturing_time
= (double)((now
.GetTimestamp()-start_time
))/1000000;
579 ::fprintf(stderr
, "time %f, num_captured=%d, fps=%f\n",
580 capturing_time
, num_captured
, num_captured
/capturing_time
);
586 BOOL
PVideoInput1394AvcDevice::GetFrameData(BYTE
* buffer
, PINDEX
* bytesReturned
)
588 PTRACE(2, "1394avc::getframedata");
590 if (msBetweenFrames
> capturing_duration
)
591 PThread::Current()->Sleep(msBetweenFrames
- capturing_duration
);
593 if ( !GetFrameDataNoDelay(buffer
, bytesReturned
))
596 capturing_duration
= (int)((end
-start
).GetMilliSeconds());
599 return GetFrameDataNoDelay(buffer
,bytesReturned
);
603 void PVideoInput1394AvcDevice::ClearMapping()
605 PTRACE(3, "ClearMapping()");
609 BOOL
PVideoInput1394AvcDevice::TestAllFormats()
614 BOOL
PVideoInput1394AvcDevice::SetColourFormat(const PString
& newFormat
)
616 PTRACE(3, "SetColourFormat("<<newFormat
<<")");
617 if (newFormat
!= colourFormat
) {
624 BOOL
PVideoInput1394AvcDevice::SetFrameSize(unsigned width
, unsigned height
)
626 PTRACE(3, "SetFrameSize("<<width
<<","<<height
<<")");
628 if ((!(width == 320 && height == 240)) &&
629 (!(width == 160 && height == 120)))
633 if ((width
!= 720) || (height
!= 480))
637 frameHeight
= height
;
639 if (frameWidth == 320 && frameHeight == 240)
640 colourFormat = "UYVY422";
641 else if (frameWidth == 160 && frameHeight == 120)
642 colourFormat = "UYV444";
644 colourFormat
= "RGB24F";
645 frameBytes
= PVideoDevice::CalculateFrameBytes(frameWidth
, frameHeight
, colourFormat
);
647 // Don't really need the next lines - AVC cameras don't support change in resolution
657 BOOL
PVideoInput1394AvcDevice::SetFrameSizeConverter(unsigned width
, unsigned height
,
660 PTRACE(3, "SetFrameSizeConverter("<<width
<<","<<height
<<","<<bScaleNotCrop
<<")");
662 if (width == CIFWidth && height == CIFHeight)
663 SetFrameSize(320, 240);
664 else if (width == QCIFWidth && height == QCIFHeight)
665 SetFrameSize(160, 120);
667 PTRACE(1, width << "x" << height << " is not supported.");
671 SetFrameSize(width
,height
);
672 if (converter
!= NULL
)
675 desiredFrameWidth
= width
;
676 desiredFrameHeight
= height
;
678 PTRACE(3, "Set Converter, colourFormat="<<colourFormat
<<" desiredformat="<<desiredColourFormat
<<" width="<<width
<<" height="<<height
<<" frameWidth="<<frameWidth
<<" frameHeight="<<frameHeight
);
679 converter
= PColourConverter::Create(colourFormat
, desiredColourFormat
, width
, height
);
680 if (converter
== NULL
) {
681 PTRACE(1, "Failed to make a converter.");
684 if (converter
->SetSrcFrameSize(width
,height
) == FALSE
) {
685 PTRACE(1, "Failed to set source frame size of a converter.");
688 if (converter
->SetDstFrameSize(desiredFrameWidth
, desiredFrameHeight
, FALSE
) == FALSE
) {
689 PTRACE(1, "Failed to set destination frame size (+scaling) of a converter.");
695 BOOL
PVideoInput1394AvcDevice::SetColourFormatConverter(const PString
& colourFmt
)
697 PTRACE(3, "SetColourFormatConverter("<<colourFmt
<<")");
699 if (colourFmt != "YUV420P") {
700 PTRACE(1, colourFmt << " is unsupported.");
704 desiredColourFormat
= colourFmt
;
705 return SetFrameSizeConverter(desiredFrameWidth
, desiredFrameHeight
, FALSE
);
708 void PVideoInput1394AvcDevice::GetAvcCameras()
710 PTRACE(3, "1394AVC :: GetAvcCameras");
711 camera_nodes
= (nodeid_t
*) malloc (sizeof(nodeid_t
)*16);
712 if (camera_nodes
== NULL
) {
713 PTRACE(0, "getavccameras :: out of memory");
717 rom1394_directory rom_dir
;
718 if (handle
== NULL
) {
719 PTRACE(0, "Weird, handle not created yet :-/");
722 if (raw1394_set_port(handle
, 0) < 0) {
723 PTRACE(0, "Cannot set port");
726 int nodecount
= raw1394_get_nodecount(handle
);
727 for (int i
=0; i
<nodecount
;i
++) {
728 PTRACE(3, "check node " << i
);
729 if (rom1394_get_directory(handle
, i
, &rom_dir
) < 0) {
730 PTRACE(0, "Cannot read ROM data for node" << i
);
733 if ((rom1394_get_node_type(&rom_dir
) == ROM1394_NODE_TYPE_AVC
)) {
734 PTRACE(3, "Found a camera at node " << i
);
735 camera_nodes
[numCameras
++] = i
;
740 int RawISOHandler (raw1394handle_t handle
, int channel
, size_t length
, u_int32_t
* data
)
742 PTRACE(4, "1394avc :: rawisohandler with length " << length
);
743 if (length
< RAW_BUFFER_SIZE
) {
744 *(u_int32_t
*) raw_buffer
= length
;
745 memcpy (raw_buffer
+ 4, data
, length
);
749 // End Of File ///////////////////////////////////////////////////////////////