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 "vidinput_avc.h"
65 #include "vidinput_avc.h"
67 #ifndef RAW_BUFFER_SIZE
68 #define RAW_BUFFER_SIZE 512
71 PCREATE_VIDINPUT_PLUGIN(AVC
, PVideoInput1394AvcDevice
);
74 static PDictionary
<PString
, PString
> *dico
;
75 static u_int8_t raw_buffer
[RAW_BUFFER_SIZE
];
77 ///////////////////////////////////////////////////////////////////////////////
80 PVideoInput1394AvcDevice::PVideoInput1394AvcDevice()
87 PVideoInput1394AvcDevice::~PVideoInput1394AvcDevice()
92 BOOL
PVideoInput1394AvcDevice::Open(const PString
& devName
, BOOL startImmediate
)
94 PTRACE(3, "trying to open " << devName
);
99 UseDMA
= TRUE
; // FIXME: useful?
101 handle
= raw1394_new_handle();
102 if (handle
== NULL
) {
103 PTRACE(3, "No handle.");
108 if(dico
!= NULL
&& sscanf((char *)dico
->GetAt(devName
), "%d", &port
) == 1)
114 if(raw1394_set_port(handle
, port
) != 0) {
115 PTRACE(3, "couldn't set the port");
120 frameWidth
= CIFWidth
;
121 frameHeight
= CIFHeight
;
122 colourFormat
= "RGB24F";
123 desiredFrameHeight
= CIFHeight
;
124 desiredFrameWidth
= CIFWidth
;
125 desiredColourFormat
= "RGB24F";
127 deviceName
= devName
; // FIXME: looks useless
129 if (!SetChannel(channelNumber
)
130 || !SetVideoFormat(videoFormat
)) {
131 PTRACE(3, "SetChannel() or SetVideoFormat() failed");
136 if (startImmediate
&& !Start()) {
141 PTRACE(3, "Successfully opened avc1394");
145 BOOL
PVideoInput1394AvcDevice::IsOpen()
147 return handle
!= NULL
;
150 BOOL
PVideoInput1394AvcDevice::Close()
152 PTRACE(3, "Close()");
156 raw1394_destroy_handle(handle
);
164 BOOL
PVideoInput1394AvcDevice::Start()
166 if (!IsOpen()) return FALSE
;
167 if (IsCapturing()) return TRUE
;
169 if (raw1394_set_iso_handler(handle
, 63, &RawISOHandler
)!= NULL
) {
170 PTRACE (3, "Cannot set_iso_handler");
178 BOOL
PVideoInput1394AvcDevice::Stop()
181 is_capturing
= FALSE
;
188 BOOL
PVideoInput1394AvcDevice::IsCapturing()
193 PStringList
PVideoInput1394AvcDevice::GetInputDeviceNames()
196 raw1394handle_t hdl
= NULL
;
198 hdl
= raw1394_new_handle();
203 // scan all nodes of all ports, check the real name of the device
204 int nb_ports
= raw1394_get_port_info(hdl
, NULL
, 0);
205 for(int pt
= 0; pt
< nb_ports
; pt
++) {
206 if(raw1394_set_port(hdl
, pt
) >= 0) {
207 int nb_nodes
= raw1394_get_nodecount(hdl
);
208 for(int nd
= 0; nd
< nb_nodes
; nd
++) {
209 rom1394_directory dir
;
210 rom1394_get_directory(hdl
, nd
, &dir
);
211 if(rom1394_get_node_type(&dir
) == ROM1394_NODE_TYPE_AVC
) {
212 PString ufname
= (PString
)dir
.label
;
213 PString
*devname
= new PString(pt
);
216 dico
= new PDictionary
<PString
, PString
>;
217 if(dico
->Contains(ufname
)
218 && *dico
->GetAt(ufname
) != *devname
) {
219 PString altname
= ufname
+ " (2)";
221 while(dico
->Contains(altname
) && *dico
->GetAt(altname
) != *devname
) {
223 altname
= ufname
+ " ("+(PString
)i
+")";
225 dico
->SetAt(altname
, devname
);
226 Result
.AppendString(altname
);
229 dico
->SetAt(ufname
, devname
);
230 Result
.AppendString(ufname
);
239 raw1394_destroy_handle(hdl
);
243 BOOL
PVideoInput1394AvcDevice::SetVideoFormat(VideoFormat newFormat
)
245 // FIXME: isn't it inherited from PVideoDevice anyway?
246 if (!PVideoDevice::SetVideoFormat(newFormat
)) {
247 PTRACE(3,"PVideoDevice::SetVideoFormat failed");
254 int PVideoInput1394AvcDevice::GetBrightness()
259 BOOL
PVideoInput1394AvcDevice::SetBrightness(unsigned newBrightness
)
264 int PVideoInput1394AvcDevice::GetHue()
269 BOOL
PVideoInput1394AvcDevice::SetHue(unsigned newHue
)
274 int PVideoInput1394AvcDevice::GetContrast()
279 BOOL
PVideoInput1394AvcDevice::SetContrast(unsigned newContrast
)
284 BOOL
PVideoInput1394AvcDevice::SetColour(unsigned newColour
)
289 int PVideoInput1394AvcDevice::GetColour()
294 BOOL
PVideoInput1394AvcDevice::SetWhiteness(unsigned newWhiteness
)
299 int PVideoInput1394AvcDevice::GetWhiteness()
304 BOOL
PVideoInput1394AvcDevice::GetParameters (int *whiteness
, int *brightness
,
305 int *colour
, int *contrast
, int *hue
)
314 int PVideoInput1394AvcDevice::GetNumChannels()
319 Result
= dico
->GetSize();
327 BOOL
PVideoInput1394AvcDevice::SetChannel(int newChannel
)
329 if (PVideoDevice::SetChannel(newChannel
) == FALSE
)
340 BOOL
PVideoInput1394AvcDevice::SetFrameRate(unsigned rate
)
342 return PVideoDevice::SetFrameRate(rate
);
345 BOOL
PVideoInput1394AvcDevice::GetFrameSizeLimits(unsigned & minWidth
,
346 unsigned & minHeight
,
348 unsigned & maxHeight
)
357 BOOL
PVideoInput1394AvcDevice::GetFrame(PBYTEArray
& frame
)
360 if (!GetFrameData(frame
.GetPointer(GetMaxFrameBytes()), &returned
))
363 frame
.SetSize(returned
);
368 PINDEX
PVideoInput1394AvcDevice::GetMaxFrameBytes()
371 if(converter
== NULL
)
374 PINDEX bytes
= converter
->GetMaxDstFrameBytes();
375 if (bytes
> frameBytes
)
382 BOOL
PVideoInput1394AvcDevice::GetFrameDataNoDelay(BYTE
* buffer
,
383 PINDEX
* bytesReturned
)
385 if (!IsCapturing()) return FALSE
;
387 BOOL frame_complete
= FALSE
;
388 BOOL found_first_frame
= FALSE
;
390 int broken_frames
= 0;
391 BYTE capture_buffer
[150000];
392 BYTE
* capture_buffer_end
= capture_buffer
;
394 // this starts the bytes' rain
395 if (raw1394_start_iso_rcv(handle
, 63) < 0) {
396 PTRACE(3, "Cannot receive data on channel 63");
399 // calling the raw1394 event manager, to get a frame:
400 while(!frame_complete
) {
401 raw1394_loop_iterate(handle
);
402 if (*(uint32_t *)raw_buffer
>= 492) {
403 if (!found_first_frame
) {
404 if (raw_buffer
[16] == 0x1f && raw_buffer
[17] == 0x07)
405 found_first_frame
= TRUE
;
410 PTRACE (3, "Skipped much too many frames");
413 if (found_first_frame
) {
414 if (raw_buffer
[16] == 0x1f
415 && raw_buffer
[17] == 0x07
416 && (capture_buffer_end
- capture_buffer
> 480)) {
417 // check for a short read. check if we read less
418 // than a NTSC frame because it is the smaller one.
419 // still not sure how to handle NTSC vs. PAL
420 if (capture_buffer_end
- capture_buffer
< 120000) {
422 capture_buffer_end
= capture_buffer
;
425 frame_complete
= TRUE
;
427 if (!frame_complete
) {
428 memcpy (capture_buffer_end
, raw_buffer
+16, 480);
429 capture_buffer_end
+= 480;
431 } // found_first_frame
432 if (broken_frames
> 30) {
433 PTRACE(3, "Too many broken frames!");
438 // stops the bytes from coming at us!
439 raw1394_stop_iso_rcv(handle
, 63);
442 dv
= dv_decoder_new(TRUE
, FALSE
, FALSE
);
443 dv
->quality
= DV_QUALITY_BEST
; // FIXME: best!?
444 if(dv_parse_header(dv
, capture_buffer
) < 0) {
445 PTRACE(3, "cannot parse dv frame header");
449 dv_color_space_t color_space
;
453 pitches
[0] = dv
->width
* 3;
454 pitches
[1] = pitches
[2] = 0;
456 pixels
[0] = (uint8_t *)malloc(dv
->width
* dv
->height
* 3);
459 color_space
= e_dv_color_rgb
;
461 dv_decode_full_frame(dv
, capture_buffer
, color_space
, pixels
, pitches
);
463 // FIXME: this is a manual resize ; the original author wondered about it
464 float xRatio
= dv
->width
/ (float)frameWidth
;
465 float yRatio
= dv
->height
/ (float)frameHeight
;
466 for(uint y
= 0; y
< frameHeight
; y
++)
467 for (uint x
= 0; x
< frameWidth
; x
++) {
468 uint16_t sourceX
= (uint16_t) (x
* xRatio
);
469 uint16_t sourceY
= (uint16_t) (y
* yRatio
);
470 memcpy (pixels
[0]+3*(y
*frameWidth
+x
),
471 pixels
[0]+3*(sourceY
*dv
->width
+sourceX
),
474 // FIXME: BGR <-> RGB done by hand
476 int offset
= (y
*frameWidth
+x
)*3;
477 temp
= pixels
[0][offset
+0];
478 pixels
[0][offset
+0] = pixels
[0][offset
+2];
479 pixels
[0][offset
+2] = temp
;
481 if (converter
!= NULL
) {
482 converter
->Convert((const BYTE
*)pixels
[0], buffer
, bytesReturned
);
483 if (pixels
[0] != NULL
)
487 PTRACE(3, "Converter must exist. Something goes wrong.");
495 BOOL
PVideoInput1394AvcDevice::GetFrameData(BYTE
* buffer
,
496 PINDEX
* bytesReturned
)
498 int capturing_duration
= 10000; // FIXME: what is it for?
499 // notice: funny things happen when it is set too low!
502 if(msBetweenFrames
> capturing_duration
)
503 PThread::Current()->Sleep(msBetweenFrames
- capturing_duration
);
505 if(!GetFrameDataNoDelay(buffer
, bytesReturned
))
508 capturing_duration
= (int)((end
-start
).GetMilliSeconds());
511 return GetFrameDataNoDelay(buffer
, bytesReturned
);
514 void PVideoInput1394AvcDevice::ClearMapping()
519 BOOL
PVideoInput1394AvcDevice::TestAllFormats()
524 BOOL
PVideoInput1394AvcDevice::SetColourFormat(const PString
& newFormat
)
526 if (newFormat
!= colourFormat
)
532 BOOL
PVideoInput1394AvcDevice::SetFrameSize(unsigned width
, unsigned height
)
534 // FIXME: shouldn't it return FALSE when asked an unsupported frame size?
536 frameHeight
= height
;
537 colourFormat
= "RGB24F";
538 frameBytes
= PVideoDevice::CalculateFrameBytes(frameWidth
,
539 frameHeight
, colourFormat
);
544 BOOL
PVideoInput1394AvcDevice::SetFrameSizeConverter(unsigned width
,
549 SetFrameSize(width
, height
);
551 if (converter
!= NULL
)
554 desiredFrameWidth
= width
;
555 desiredFrameHeight
= height
;
557 converter
= PColourConverter::Create(colourFormat
,
558 desiredColourFormat
, width
, height
);
559 if (converter
== NULL
) {
560 PTRACE(3, "Failed to make a converter.");
564 if (converter
->SetSrcFrameSize(width
,height
) == FALSE
) {
565 PTRACE(3, "Failed to set source frame size of a converter.");
569 if (converter
->SetDstFrameSize(desiredFrameWidth
,
570 desiredFrameHeight
, FALSE
) == FALSE
) {
571 PTRACE(3, "Failed to set destination frame size (+scaling) of a converter.");
578 BOOL
PVideoInput1394AvcDevice::SetColourFormatConverter(const PString
&colourFmt
)
580 desiredColourFormat
= colourFmt
;
581 return SetFrameSizeConverter(desiredFrameWidth
, desiredFrameHeight
, FALSE
);
584 int RawISOHandler (raw1394handle_t handle
, int channel
, size_t length
, u_int32_t
* data
)
586 if (length
< RAW_BUFFER_SIZE
) {
587 *(u_int32_t
*) raw_buffer
= length
;
588 memcpy (raw_buffer
+ 4, data
, length
);
592 // End Of File ///////////////////////////////////////////////////////////////