Added ignore file
[pwlib.git] / plugins / vidinput_avc / vidinput_avc.cxx
blob20cb7d070c6a02ec72a2e68cc8407b91cb8f8ea2
1 /*
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
43 * Technical Notes
44 * ------------------------------------------------------------
46 * Test Environment:
47 * This module was tested against:
48 * Hardware:
49 * AthlonXP 1800+
50 * Asus A7S333
51 * Sony DCR-TRV20 NTSC (http://www.sony.jp/products/Consumer/VD/DCR-TRV20/)
52 * Texas Instruments TSB12LV26 IEEE-1394 Controller
53 * Software:
54 * Linux vanilla kernel 2.4.20
55 * libraw1394 0.9.0
56 * libavc1394 0.4.1
57 * libdv 0.98
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
69 #endif
71 PCREATE_VIDINPUT_PLUGIN(AVC, PVideoInput1394AvcDevice);
73 static PMutex mutex;
74 static PDictionary<PString, PString> *dico;
75 static u_int8_t raw_buffer[RAW_BUFFER_SIZE];
77 ///////////////////////////////////////////////////////////////////////////////
78 // PVideoInput1394AVC
80 PVideoInput1394AvcDevice::PVideoInput1394AvcDevice()
82 handle = NULL;
83 is_capturing = FALSE;
84 dv_decoder = NULL;
87 PVideoInput1394AvcDevice::~PVideoInput1394AvcDevice()
89 Close();
92 BOOL PVideoInput1394AvcDevice::Open(const PString & devName, BOOL startImmediate)
94 PTRACE(3, "trying to open " << devName);
96 if (IsOpen())
97 Close();
99 UseDMA = TRUE; // FIXME: useful?
101 handle = raw1394_new_handle();
102 if (handle == NULL) {
103 PTRACE(3, "No handle.");
104 return FALSE;
107 mutex.Wait();
108 if(dico != NULL && sscanf((char *)dico->GetAt(devName), "%d", &port) == 1)
109 ; // well, got it
110 else
111 port = 0;
112 mutex.Signal();
114 if(raw1394_set_port(handle, port) != 0) {
115 PTRACE(3, "couldn't set the port");
116 Close();
117 return FALSE;
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");
132 Close();
133 return FALSE;
136 if (startImmediate && !Start()) {
137 Close();
138 return FALSE;
141 PTRACE(3, "Successfully opened avc1394");
142 return TRUE;
145 BOOL PVideoInput1394AvcDevice::IsOpen()
147 return handle != NULL;
150 BOOL PVideoInput1394AvcDevice::Close()
152 PTRACE(3, "Close()");
153 if (IsOpen()) {
154 if (IsCapturing())
155 Stop();
156 raw1394_destroy_handle(handle);
157 handle = NULL;
158 return TRUE;
160 else
161 return FALSE;
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");
171 return FALSE;
174 is_capturing = TRUE;
175 return TRUE;
178 BOOL PVideoInput1394AvcDevice::Stop()
180 if (IsCapturing()) {
181 is_capturing = FALSE;
182 return TRUE;
184 else
185 return FALSE;
188 BOOL PVideoInput1394AvcDevice::IsCapturing()
190 return is_capturing;
193 PStringList PVideoInput1394AvcDevice::GetInputDeviceNames()
195 PStringList Result;
196 raw1394handle_t hdl = NULL;
198 hdl = raw1394_new_handle();
200 if(hdl == NULL)
201 return Result;
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);
214 mutex.Wait();
215 if(dico == NULL)
216 dico = new PDictionary<PString, PString>;
217 if(dico->Contains(ufname)
218 && *dico->GetAt(ufname) != *devname) {
219 PString altname = ufname+ " (2)";
220 int i = 2;
221 while(dico->Contains(altname) && *dico->GetAt(altname) != *devname) {
222 i++;
223 altname = ufname+ " ("+(PString)i+")";
225 dico->SetAt(altname, devname);
226 Result.AppendString(altname);
228 else {
229 dico->SetAt(ufname, devname);
230 Result.AppendString(ufname);
232 mutex.Signal();
239 raw1394_destroy_handle(hdl);
240 return Result;
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");
248 return FALSE;
250 else
251 return TRUE;
254 int PVideoInput1394AvcDevice::GetBrightness()
256 return -1;
259 BOOL PVideoInput1394AvcDevice::SetBrightness(unsigned newBrightness)
261 return FALSE;
264 int PVideoInput1394AvcDevice::GetHue()
266 return -1;
269 BOOL PVideoInput1394AvcDevice::SetHue(unsigned newHue)
271 return FALSE;
274 int PVideoInput1394AvcDevice::GetContrast()
276 return -1;
279 BOOL PVideoInput1394AvcDevice::SetContrast(unsigned newContrast)
281 return FALSE;
284 BOOL PVideoInput1394AvcDevice::SetColour(unsigned newColour)
286 return -1;
289 int PVideoInput1394AvcDevice::GetColour()
291 return -1;
294 BOOL PVideoInput1394AvcDevice::SetWhiteness(unsigned newWhiteness)
296 return FALSE;
299 int PVideoInput1394AvcDevice::GetWhiteness()
301 return -1;
304 BOOL PVideoInput1394AvcDevice::GetParameters (int *whiteness, int *brightness,
305 int *colour, int *contrast, int *hue)
307 *whiteness = -1;
308 *brightness = -1;
309 *colour = -1;
310 *hue = -1;
311 return FALSE;
314 int PVideoInput1394AvcDevice::GetNumChannels()
316 int Result;
317 mutex.Wait();
318 if(dico != NULL)
319 Result = dico->GetSize();
320 else
321 Result = 0;
323 mutex.Signal();
324 return Result;
327 BOOL PVideoInput1394AvcDevice::SetChannel(int newChannel)
329 if (PVideoDevice::SetChannel(newChannel) == FALSE)
330 return FALSE;
332 if(IsCapturing()) {
333 Stop();
334 Start();
337 return TRUE;
340 BOOL PVideoInput1394AvcDevice::SetFrameRate(unsigned rate)
342 return PVideoDevice::SetFrameRate(rate);
345 BOOL PVideoInput1394AvcDevice::GetFrameSizeLimits(unsigned & minWidth,
346 unsigned & minHeight,
347 unsigned & maxWidth,
348 unsigned & maxHeight)
350 minWidth = 160;
351 maxWidth = 320;
352 minHeight = 120;
353 maxHeight = 240;
354 return TRUE;
357 BOOL PVideoInput1394AvcDevice::GetFrame(PBYTEArray & frame)
359 PINDEX returned;
360 if (!GetFrameData(frame.GetPointer(GetMaxFrameBytes()), &returned))
361 return FALSE;
363 frame.SetSize(returned);
364 return TRUE;
368 PINDEX PVideoInput1394AvcDevice::GetMaxFrameBytes()
371 if(converter == NULL)
372 return frameBytes;
374 PINDEX bytes = converter->GetMaxDstFrameBytes();
375 if (bytes > frameBytes)
376 return bytes;
377 else
378 return 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;
389 int skipped = 0;
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");
397 return FALSE;
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;
406 else
407 skipped ++;
409 if (skipped > 500) {
410 PTRACE (3, "Skipped much too many frames");
411 return FALSE;
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) {
421 broken_frames++;
422 capture_buffer_end = capture_buffer;
424 else
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!");
434 return FALSE;
438 // stops the bytes from coming at us!
439 raw1394_stop_iso_rcv(handle, 63);
441 dv_decoder_t *dv;
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");
446 return FALSE;
449 dv_color_space_t color_space;
450 BYTE * pixels[3];
451 int pitches[3];
453 pitches[0] = dv->width * 3;
454 pitches[1] = pitches[2] = 0;
456 pixels[0] = (uint8_t *)malloc(dv->width * dv->height * 3);
457 pixels[1] = NULL;
458 pixels[2] = NULL;
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
475 BYTE temp;
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)
484 free(pixels[0]);
486 else {
487 PTRACE(3, "Converter must exist. Something goes wrong.");
488 return FALSE;
491 return TRUE;
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!
501 if(frameRate>0) {
502 if(msBetweenFrames > capturing_duration)
503 PThread::Current()->Sleep(msBetweenFrames - capturing_duration);
504 PTime start;
505 if(!GetFrameDataNoDelay(buffer, bytesReturned))
506 return FALSE;
507 PTime end;
508 capturing_duration = (int)((end-start).GetMilliSeconds());
509 return TRUE;
511 return GetFrameDataNoDelay(buffer, bytesReturned);
514 void PVideoInput1394AvcDevice::ClearMapping()
516 // do nothing...
519 BOOL PVideoInput1394AvcDevice::TestAllFormats()
521 return TRUE;
524 BOOL PVideoInput1394AvcDevice::SetColourFormat(const PString & newFormat)
526 if (newFormat != colourFormat)
527 return FALSE;
529 return TRUE;
532 BOOL PVideoInput1394AvcDevice::SetFrameSize(unsigned width, unsigned height)
534 // FIXME: shouldn't it return FALSE when asked an unsupported frame size?
535 frameWidth = width;
536 frameHeight = height;
537 colourFormat = "RGB24F";
538 frameBytes = PVideoDevice::CalculateFrameBytes(frameWidth,
539 frameHeight, colourFormat);
541 return TRUE;
544 BOOL PVideoInput1394AvcDevice::SetFrameSizeConverter(unsigned width,
545 unsigned height,
546 BOOL bScaleNotCrop)
549 SetFrameSize(width, height);
551 if (converter != NULL)
552 delete converter;
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.");
561 return FALSE;
564 if (converter->SetSrcFrameSize(width,height) == FALSE) {
565 PTRACE(3, "Failed to set source frame size of a converter.");
566 return FALSE;
569 if (converter->SetDstFrameSize(desiredFrameWidth,
570 desiredFrameHeight, FALSE) == FALSE) {
571 PTRACE(3, "Failed to set destination frame size (+scaling) of a converter.");
572 return FALSE;
575 return TRUE;
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);
590 return 0;
592 // End Of File ///////////////////////////////////////////////////////////////