1 /*
2  * This file is essentially a rewrite of video4dc1394.cxx
3  * Check that one for more explanations
4  *
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
10  *
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
16  *
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
42  *
43  * Technical Notes
44  * ------------------------------------------------------------
45  *
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
58  *
59  * Author: Georgi Georgiev <chutz@gg3.net>
60  *
61  */
62 
63 #pragma implementation "vidinput_avc.h"
64 
65 #include "vidinput_avc.h"
66 
67 #ifndef RAW_BUFFER_SIZE
68 #define RAW_BUFFER_SIZE 512
69 #endif
70 
71 PCREATE_VIDINPUT_PLUGIN(1394AVC);
72 
73 static PMutex mutex;
74 static PDictionary<PString, PString> *dico;
75 static u_int8_t raw_buffer[RAW_BUFFER_SIZE];
76 
77 ///////////////////////////////////////////////////////////////////////////////
78 // PVideoInput1394AVC
79 
PVideoInputDevice_1394AVC()80 PVideoInputDevice_1394AVC::PVideoInputDevice_1394AVC()
81 {
82   handle = NULL;
83   is_capturing = PFalse;
84   dv_decoder = NULL;
85 }
86 
~PVideoInputDevice_1394AVC()87 PVideoInputDevice_1394AVC::~PVideoInputDevice_1394AVC()
88 {
89   Close();
90 }
91 
Open(const PString & devName,PBoolean startImmediate)92 PBoolean PVideoInputDevice_1394AVC::Open(const PString & devName, PBoolean startImmediate)
93 {
94   PTRACE(3, "trying to open " << devName);
95 
96   if (IsOpen())
97     Close();
98 
99   UseDMA = PTrue; // FIXME: useful?
100 
101   handle = raw1394_new_handle();
102   if (handle == NULL) {
103     PTRACE(3, "No handle.");
104     return PFalse;
105   }
106 
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();
113 
114   if(raw1394_set_port(handle, port) != 0) {
115     PTRACE(3, "couldn't set the port");
116     Close();
117     return PFalse;
118   }
119 
120   frameWidth = CIFWidth;
121   frameHeight = CIFHeight;
122   colourFormat = "RGB24";
123 
124   deviceName = devName; // FIXME: looks useless
125 
126   if (!SetChannel(channelNumber)
127       || !SetVideoFormat(videoFormat)) {
128     PTRACE(3, "SetChannel() or SetVideoFormat() failed");
129     Close();
130     return PFalse;
131   }
132 
133   if (startImmediate && !Start()) {
134     Close();
135     return PFalse;
136   }
137 
138   PTRACE(3, "Successfully opened avc1394");
139   return PTrue;
140 }
141 
IsOpen()142 PBoolean PVideoInputDevice_1394AVC::IsOpen()
143 {
144   return handle != NULL;
145 }
146 
Close()147 PBoolean PVideoInputDevice_1394AVC::Close()
148 {
149   PTRACE(3, "Close()");
150   if (IsOpen()) {
151     if (IsCapturing())
152       Stop();
153     raw1394_destroy_handle(handle);
154     handle = NULL;
155     return PTrue;
156   }
157   else
158     return PFalse;
159 }
160 
Start()161 PBoolean PVideoInputDevice_1394AVC::Start()
162 {
163   if (!IsOpen()) return PFalse;
164   if (IsCapturing()) return PTrue;
165 
166   if (raw1394_set_iso_handler(handle, 63, &RawISOHandler)!= NULL) {
167     PTRACE (3, "Cannot set_iso_handler");
168     return PFalse;
169   }
170 
171   is_capturing = PTrue;
172   return PTrue;
173 }
174 
Stop()175 PBoolean PVideoInputDevice_1394AVC::Stop()
176 {
177   if (IsCapturing()) {
178     is_capturing = PFalse;
179     return PTrue;
180   }
181   else
182     return PFalse;
183 }
184 
IsCapturing()185 PBoolean PVideoInputDevice_1394AVC::IsCapturing()
186 {
187   return is_capturing;
188 }
189 
GetInputDeviceNames()190 PStringArray PVideoInputDevice_1394AVC::GetInputDeviceNames()
191 {
192   PStringArray Result;
193   raw1394handle_t hdl = NULL;
194 
195   hdl = raw1394_new_handle();
196 
197   if (hdl == NULL)
198     return Result;
199 
200   // scan all nodes of all ports, check the real name of the device
201   int nb_ports = raw1394_get_port_info(hdl, NULL, 0);
202   for(int pt = 0; pt < nb_ports; pt++) {
203     if (raw1394_set_port(hdl, pt) >= 0) {
204       int nb_nodes = raw1394_get_nodecount(hdl);
205       for (int nd = 0; nd < nb_nodes; nd++) {
206         rom1394_directory dir;
207         rom1394_get_directory(hdl, nd, &dir);
208         if (rom1394_get_node_type(&dir) == ROM1394_NODE_TYPE_AVC) {
209           PString ufname = (PString)dir.label;
210           PString *devname = new PString(pt);
211  	  if (ufname.IsEmpty ())
212  	    ufname = "Nameless device";
213           mutex.Wait();
214           if (dico == NULL)
215             dico = new PDictionary<PString, PString>;
216           if (dico->Contains(ufname) && *dico->GetAt(ufname) != *devname) {
217             PString altname = ufname+ " (2)";
218             int i = 2;
219             while(dico->Contains(altname) && *dico->GetAt(altname) != *devname) {
220               i++;
221               altname = ufname+ " ("+(PString)i+")";
222             }
223             dico->SetAt(altname, devname);
224             Result.AppendString(altname);
225           }
226           else {
227             dico->SetAt(ufname, devname);
228             Result.AppendString(ufname);
229           }
230           mutex.Signal();
231         }
232       }
233     }
234   }
235 
236   raw1394_destroy_handle(hdl);
237   return Result;
238 }
239 
SetVideoFormat(VideoFormat newFormat)240 PBoolean PVideoInputDevice_1394AVC::SetVideoFormat(VideoFormat newFormat)
241 {
242   // FIXME: isn't it inherited from PVideoDevice anyway?
243   if (!PVideoDevice::SetVideoFormat(newFormat)) {
244     PTRACE(3,"PVideoDevice::SetVideoFormat failed");
245     return PFalse;
246   }
247   else
248     return PTrue;
249 }
250 
GetBrightness()251 int PVideoInputDevice_1394AVC::GetBrightness()
252 {
253   return -1;
254 }
255 
SetBrightness(unsigned newBrightness)256 PBoolean PVideoInputDevice_1394AVC::SetBrightness(unsigned newBrightness)
257 {
258   return PFalse;
259 }
260 
GetHue()261 int PVideoInputDevice_1394AVC::GetHue()
262 {
263   return -1;
264 }
265 
SetHue(unsigned newHue)266 PBoolean PVideoInputDevice_1394AVC::SetHue(unsigned newHue)
267 {
268   return PFalse;
269 }
270 
GetContrast()271 int PVideoInputDevice_1394AVC::GetContrast()
272 {
273   return -1;
274 }
275 
SetContrast(unsigned newContrast)276 PBoolean PVideoInputDevice_1394AVC::SetContrast(unsigned newContrast)
277 {
278   return PFalse;
279 }
280 
SetColour(unsigned newColour)281 PBoolean PVideoInputDevice_1394AVC::SetColour(unsigned newColour)
282 {
283   return -1;
284 }
285 
GetColour()286 int PVideoInputDevice_1394AVC::GetColour()
287 {
288   return -1;
289 }
290 
SetWhiteness(unsigned newWhiteness)291 PBoolean PVideoInputDevice_1394AVC::SetWhiteness(unsigned newWhiteness)
292 {
293   return PFalse;
294 }
295 
GetWhiteness()296 int PVideoInputDevice_1394AVC::GetWhiteness()
297 {
298   return -1;
299 }
300 
GetParameters(int * whiteness,int * brightness,int * colour,int * contrast,int * hue)301 PBoolean PVideoInputDevice_1394AVC::GetParameters (int *whiteness, int *brightness,
302                                        int *colour, int *contrast, int *hue)
303 {
304   *whiteness = -1;
305   *brightness = -1;
306   *colour = -1;
307   *hue = -1;
308   return PFalse;
309 }
310 
GetNumChannels()311 int PVideoInputDevice_1394AVC::GetNumChannels()
312 {
313   int Result;
314   mutex.Wait();
315   if(dico != NULL)
316     Result = dico->GetSize();
317   else
318     Result = 0;
319 
320   mutex.Signal();
321   return Result;
322 }
323 
SetChannel(int newChannel)324 PBoolean PVideoInputDevice_1394AVC::SetChannel(int newChannel)
325 {
326   if (PVideoDevice::SetChannel(newChannel) == PFalse)
327     return PFalse;
328 
329   if(IsCapturing()) {
330     Stop();
331     Start();
332   }
333 
334   return PTrue;
335 }
336 
SetFrameRate(unsigned rate)337 PBoolean PVideoInputDevice_1394AVC::SetFrameRate(unsigned rate)
338 {
339   return PVideoDevice::SetFrameRate(rate);
340 }
341 
GetFrameSizeLimits(unsigned & minWidth,unsigned & minHeight,unsigned & maxWidth,unsigned & maxHeight)342 PBoolean PVideoInputDevice_1394AVC::GetFrameSizeLimits(unsigned & minWidth,
343                                                    unsigned & minHeight,
344                                                    unsigned & maxWidth,
345                                                    unsigned & maxHeight)
346 {
347   minWidth = CIFWidth;
348   maxWidth = CIFWidth;
349   minHeight = CIFHeight;
350   maxHeight = CIFHeight;
351   return PTrue;
352 }
353 
354 
GetMaxFrameBytes()355 PINDEX PVideoInputDevice_1394AVC::GetMaxFrameBytes()
356 {
357   return GetMaxFrameBytesConverted(frameBytes);
358 }
359 
360 
GetFrameDataNoDelay(BYTE * buffer,PINDEX * bytesReturned)361 PBoolean PVideoInputDevice_1394AVC::GetFrameDataNoDelay(BYTE * buffer,
362                                                   PINDEX * bytesReturned)
363 {
364   if (!IsCapturing()) return PFalse;
365 
366   PBoolean frame_complete = PFalse;
367   PBoolean found_first_frame = PFalse;
368   int skipped = 0;
369   int broken_frames = 0;
370   BYTE capture_buffer[150000];
371   BYTE * capture_buffer_end = capture_buffer;
372 
373   // this starts the bytes' rain
374   if (raw1394_start_iso_rcv(handle, 63) < 0) {
375     PTRACE(3, "Cannot receive data on channel 63");
376     return PFalse;
377   }
378   // calling the raw1394 event manager, to get a frame:
379   while(!frame_complete) {
380     raw1394_loop_iterate(handle);
381     if (*(uint32_t *)raw_buffer >= 492) {
382       if (!found_first_frame) {
383         if (raw_buffer[16] == 0x1f && raw_buffer[17] == 0x07)
384           found_first_frame = PTrue;
385         else
386           skipped ++;
387       }
388       if (skipped > 500) {
389         PTRACE (3, "Skipped much too many frames");
390         return PFalse;
391       }
392       if (found_first_frame) {
393         if (raw_buffer[16] == 0x1f
394             && raw_buffer[17] == 0x07
395             && (capture_buffer_end - capture_buffer > 480)) {
396           // check for a short read. check if we read less
397           // than a NTSC frame because it is the smaller one.
398           // still not sure how to handle NTSC vs. PAL
399           if (capture_buffer_end - capture_buffer < 120000) {
400             broken_frames++;
401             capture_buffer_end = capture_buffer;
402           }
403           else
404             frame_complete = PTrue;
405         }
406         if (!frame_complete) {
407           memcpy (capture_buffer_end, raw_buffer+16, 480);
408           capture_buffer_end += 480;
409         }
410       } // found_first_frame
411       if (broken_frames > 30) {
412         PTRACE(3, "Too many broken frames!");
413         return PFalse;
414       }
415     }
416   }
417   // stops the bytes from coming at us!
418   raw1394_stop_iso_rcv(handle, 63);
419 
420   dv_decoder_t *dv;
421   dv = dv_decoder_new(PTrue, PFalse, PFalse);
422   dv->quality = DV_QUALITY_BEST; // FIXME: best!?
423   if(dv_parse_header(dv, capture_buffer) < 0) {
424     PTRACE(3, "cannot parse dv frame header");
425     return PFalse;
426   }
427 
428   dv_color_space_t color_space;
429   BYTE * pixels[3];
430   int  pitches[3];
431 
432   pitches[0] = dv->width * 3;
433   pitches[1] = pitches[2] = 0;
434 
435   pixels[0] = (uint8_t *)malloc(dv->width * dv->height * 3);
436   pixels[1] = NULL;
437   pixels[2] = NULL;
438   color_space = e_dv_color_rgb;
439 
440   dv_decode_full_frame(dv, capture_buffer, color_space, pixels, pitches);
441 
442   // FIXME: this is a manual resize ; the original author wondered about it
443   float xRatio = dv->width / (float)frameWidth;
444   float yRatio = dv->height/ (float)frameHeight;
445   for(uint y = 0; y < frameHeight; y++)
446     for (uint x = 0; x < frameWidth; x++) {
447       uint16_t sourceX = (uint16_t) (x * xRatio);
448       uint16_t sourceY = (uint16_t) (y * yRatio);
449       memcpy (pixels[0]+3*(y*frameWidth+x),
450               pixels[0]+3*(sourceY*dv->width+sourceX),
451               3);
452     }
453   if (converter != NULL) {
454     converter->Convert((const BYTE *)pixels[0], buffer, bytesReturned);
455     if (pixels[0] != NULL)
456       free(pixels[0]);
457   }
458   else {
459     PTRACE(3, "Converter must exist. Something goes wrong.");
460     return PFalse;
461   }
462 
463   return PTrue;
464 
465 }
466 
GetFrameData(BYTE * buffer,PINDEX * bytesReturned)467 PBoolean PVideoInputDevice_1394AVC::GetFrameData(BYTE * buffer, PINDEX * bytesReturned)
468 {
469   m_pacing.Delay(1000/GetFrameRate());
470   return GetFrameDataNoDelay(buffer, bytesReturned);
471 }
472 
ClearMapping()473 void PVideoInputDevice_1394AVC::ClearMapping()
474 {
475   // do nothing...
476 }
477 
TestAllFormats()478 PBoolean PVideoInputDevice_1394AVC::TestAllFormats()
479 {
480   return PTrue;
481 }
482 
SetColourFormat(const PString & newFormat)483 PBoolean PVideoInputDevice_1394AVC::SetColourFormat(const PString & newFormat)
484 {
485   return newFormat == colourFormat;
486 }
487 
SetFrameSize(unsigned width,unsigned height)488 PBoolean PVideoInputDevice_1394AVC::SetFrameSize(unsigned width, unsigned height)
489 {
490   if ( ! ( (width == CIFWidth && height == CIFHeight) ) )
491     return PFalse;
492 
493   frameWidth = width;
494   frameHeight = height;
495   colourFormat = "RGB24";
496   nativeVerticalFlip = true;
497   frameBytes = PVideoDevice::CalculateFrameBytes(frameWidth, frameHeight, colourFormat);
498 
499   return PTrue;
500 }
501 
502 
RawISOHandler(raw1394handle_t handle,int channel,size_t length,u_int32_t * data)503 int RawISOHandler (raw1394handle_t handle, int channel, size_t length, u_int32_t * data)
504 {
505   if (length < RAW_BUFFER_SIZE) {
506     *(u_int32_t *) raw_buffer = length;
507     memcpy (raw_buffer + 4, data, length);
508   }
509   return 0;
510 }
511 // End Of File ///////////////////////////////////////////////////////////////
512