Uncommented beaudio code
[pwlib.git] / src / ptlib / unix / video4avc1394.cxx
blob22590bf160495cd5d2a43ddfdd18a9f43ba2a8ab
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 "videoio1394avc.h"
65 #include <ptlib.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
75 // for debugging
76 static PInt64 start_time;
77 static int num_captured;
78 #endif
80 #ifndef RAW_BUFFER_SIZE
81 #define RAW_BUFFER_SIZE 512
82 #endif
84 static u_int8_t raw_buffer[RAW_BUFFER_SIZE];
87 ///////////////////////////////////////////////////////////////////////////////
88 // PVideoInput1394AVC
90 PVideoInput1394AvcDevice::PVideoInput1394AvcDevice()
92 PTRACE(3, "PVideo1394AvcDevice::PVideo1394AvcDevice()");
93 handle = NULL;
94 is_capturing = FALSE;
95 capturing_duration = 10000; // arbitrary large value suffices
96 dv_decoder = NULL;
99 PVideoInput1394AvcDevice::~PVideoInput1394AvcDevice()
101 Close();
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) {
110 return FALSE;
112 raw1394_destroy_handle(handle);
113 return TRUE;
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");
121 return FALSE;
124 if (IsOpen()) {
125 PTRACE(0, "You cannot open PVideoInput1394AvcDevice twice.");
126 return FALSE;
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")
132 UseDMA = FALSE;
133 // Don't forget /dev/video1394/0
134 else if (strncmp(devName, "/dev/video1394", 14) == 0)
135 UseDMA = TRUE;
136 else {
137 PTRACE(0, "devName must be /dev/raw1394 or /dev/video1394*");
138 return FALSE;
141 // See if devName is accessible.
142 if (!PFile::Exists(devName)) {
143 PTRACE(1, devName << " is not accessible.");
144 return FALSE;
147 /*-----------------------------------------------------------------------
148 * Open ohci and asign handle to it
149 *-----------------------------------------------------------------------*/
150 handle = raw1394_new_handle();
151 if (handle==NULL)
153 PTRACE(0, "Unable to aquire a raw1394 handle"
154 "did you insmod the drivers?\n");
155 return FALSE;
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);
164 if (num_cards < 0) {
165 PTRACE(0,"Couldn't get card info");
166 raw1394_destroy_handle(handle);
167 handle = NULL;
168 return FALSE;
170 for (int i=0;i<num_cards;i++) {
171 PTRACE(3, "Found card "<< g_pinf[i].name << " with " << g_pinf[i].nodes << " nodes");
174 GetAvcCameras();
175 if (numCameras < 1) {
176 PTRACE(0, "no cameras found");
177 raw1394_destroy_handle(handle);
178 handle = NULL;
179 return FALSE;
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");
204 Close();
205 return FALSE;
208 if (startImmediate && !Start()) {
209 Close();
210 return FALSE;
212 PTRACE(3, "Successfully opened avc1394");
213 return TRUE;
217 BOOL PVideoInput1394AvcDevice::IsOpen()
219 return handle != NULL;
223 BOOL PVideoInput1394AvcDevice::Close()
225 PTRACE(3, "Close()");
226 if (IsOpen()) {
227 if (IsCapturing())
228 Stop();
229 raw1394_destroy_handle(handle);
230 handle = NULL;
231 return TRUE;
232 } else
233 return FALSE;
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)
246 else {
247 PTRACE(1, "Frame size is neither 320x240 or 160x120" << frameWidth <<
248 "x" << frameHeight);
249 return FALSE;
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");
260 return FALSE;
262 #if 0
263 // channelNumber 63
264 if (raw1394_start_iso_rcv(handle, 63) < 0) {
265 PTRACE (0, "Cannot start_iso_rcv");
266 return FALSE;
268 #endif
269 is_capturing = TRUE;
270 #ifdef ESTIMATE_CAPTURE_PERFORMANCE
271 PTime now;
272 start_time = now.GetTimestamp();
273 num_captured = 0;
274 #endif
275 return TRUE;
279 BOOL PVideoInput1394AvcDevice::Stop()
281 PTRACE(3,"Stop()");
282 if (IsCapturing()) {
283 // channelNumber
284 #if 0
285 raw1394_stop_iso_rcv(handle, 63);
286 #endif
287 is_capturing = FALSE;
288 return TRUE;
289 } else
290 return FALSE;
294 BOOL PVideoInput1394AvcDevice::IsCapturing()
296 return is_capturing;
299 PStringList PVideoInput1394AvcDevice::GetInputDeviceNames()
301 PTRACE(3, "GetInputDeviceNames()");
302 PStringList list;
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);
313 else
314 break;
316 else if (PFile::Exists("/dev/video1394"))
317 /* traditional naming */
318 list.AppendString("/dev/video1394");
319 return list;
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);
328 return FALSE;
330 return TRUE;
333 int PVideoInput1394AvcDevice::GetBrightness()
335 return -1;
339 BOOL PVideoInput1394AvcDevice::SetBrightness(unsigned newBrightness)
341 return FALSE;
345 int PVideoInput1394AvcDevice::GetHue()
347 return -1;
351 BOOL PVideoInput1394AvcDevice::SetHue(unsigned newHue)
353 return FALSE;
357 int PVideoInput1394AvcDevice::GetContrast()
359 return -1;
363 BOOL PVideoInput1394AvcDevice::SetContrast(unsigned newContrast)
365 return FALSE;
368 BOOL PVideoInput1394AvcDevice::SetColour(unsigned newColour)
370 return -1;
373 int PVideoInput1394AvcDevice::GetColour()
375 return -1;
379 BOOL PVideoInput1394AvcDevice::SetWhiteness(unsigned newWhiteness)
381 return FALSE;
384 int PVideoInput1394AvcDevice::GetWhiteness()
386 return -1;
390 BOOL PVideoInput1394AvcDevice::GetParameters (int *whiteness, int *brightness,
391 int *colour, int *contrast, int *hue)
393 *whiteness = -1;
394 *brightness = -1;
395 *colour = -1;
396 *hue = -1;
397 return FALSE;
401 int PVideoInput1394AvcDevice::GetNumChannels()
403 return numCameras;
407 BOOL PVideoInput1394AvcDevice::SetChannel(int newChannel)
409 PTRACE(3, "SetChannel("<<newChannel<<")");
410 if (PVideoDevice::SetChannel(newChannel) == FALSE)
411 return FALSE;
412 if(IsCapturing()) {
413 Stop();
414 Start();
416 return TRUE;
421 BOOL PVideoInput1394AvcDevice::SetFrameRate(unsigned rate)
423 return PVideoDevice::SetFrameRate(rate);
427 BOOL PVideoInput1394AvcDevice::GetFrameSizeLimits(unsigned & minWidth,
428 unsigned & minHeight,
429 unsigned & maxWidth,
430 unsigned & maxHeight)
432 PTRACE(3, "GetFrameSizeLimits()");
433 minWidth = 160;
434 maxWidth = 320;
435 minHeight = 120;
436 maxHeight = 240;
437 return TRUE;
442 PINDEX PVideoInput1394AvcDevice::GetMaxFrameBytes()
444 PTRACE(3, "GetMaxFrameBytes()");
445 if (converter != NULL) {
446 PINDEX bytes = converter->GetMaxDstFrameBytes();
447 if (bytes > frameBytes)
448 return bytes;
451 return 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");
459 file.WriteString(w);
460 file.WriteString("\n");
461 file.WriteString(h);
462 file.WriteString("\n255\n");
463 file.Write(data, w*h*3);
464 file.Close();
465 return TRUE;
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;
477 int skipped = 0;
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");
484 return FALSE;
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;
492 else skipped ++;
494 if (skipped > 500) {
495 PTRACE (0, "Skipped too many frames");
496 return FALSE;
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) {
503 broken_frames ++;
504 capture_buffer_end = capture_buffer;
505 } else {
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");
516 return FALSE;
520 raw1394_stop_iso_rcv(handle, 63);
521 PTRACE(3, "got "<< capture_buffer_end - capture_buffer << " bytes... now convert DV -> RGB");
523 dv_decoder_t * dv;
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");
528 return FALSE;
531 dv_color_space_t color_space;
532 BYTE * pixels[3];
533 int pitches[3];
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]);
544 #if 1
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
555 #if 1
556 BYTE temp;
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;
561 #endif
563 #endif
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) {
568 free(pixels[0]);
570 } else {
571 PTRACE(1, "Converter must exist. Something goes wrong.");
572 return FALSE;
574 PTRACE(3, "1394avc: return frame: "<< frameWidth << "x"<<frameHeight);
575 #ifdef ESTIMATE_CAPTURE_PERFORMANCE
576 ++num_captured;
577 PTime now;
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);
581 #endif
583 return TRUE;
586 BOOL PVideoInput1394AvcDevice::GetFrameData(BYTE * buffer, PINDEX * bytesReturned)
588 PTRACE(2, "1394avc::getframedata");
589 if(frameRate>0) {
590 if (msBetweenFrames > capturing_duration)
591 PThread::Current()->Sleep(msBetweenFrames - capturing_duration);
592 PTime start;
593 if ( !GetFrameDataNoDelay(buffer, bytesReturned))
594 return FALSE;
595 PTime end;
596 capturing_duration = (int)((end-start).GetMilliSeconds());
597 return TRUE;
599 return GetFrameDataNoDelay(buffer,bytesReturned);
603 void PVideoInput1394AvcDevice::ClearMapping()
605 PTRACE(3, "ClearMapping()");
609 BOOL PVideoInput1394AvcDevice::TestAllFormats()
611 return TRUE;
614 BOOL PVideoInput1394AvcDevice::SetColourFormat(const PString & newFormat)
616 PTRACE(3, "SetColourFormat("<<newFormat<<")");
617 if (newFormat != colourFormat) {
618 return FALSE;
620 return TRUE;
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)))
630 return FALSE;
632 #if 0
633 if ((width != 720) || (height != 480))
634 return FALSE;
635 #endif
636 frameWidth = width;
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
649 if (IsCapturing()) {
650 Stop(); Start();
653 return TRUE;
657 BOOL PVideoInput1394AvcDevice::SetFrameSizeConverter(unsigned width, unsigned height,
658 BOOL bScaleNotCrop)
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);
666 else {
667 PTRACE(1, width << "x" << height << " is not supported.");
668 return FALSE;
671 SetFrameSize(width,height);
672 if (converter != NULL)
673 delete converter;
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.");
682 return FALSE;
684 if (converter->SetSrcFrameSize(width,height) == FALSE) {
685 PTRACE(1, "Failed to set source frame size of a converter.");
686 return FALSE;
688 if (converter->SetDstFrameSize(desiredFrameWidth, desiredFrameHeight, FALSE) == FALSE) {
689 PTRACE(1, "Failed to set destination frame size (+scaling) of a converter.");
690 return FALSE;
692 return TRUE;
695 BOOL PVideoInput1394AvcDevice::SetColourFormatConverter(const PString & colourFmt)
697 PTRACE(3, "SetColourFormatConverter("<<colourFmt<<")");
699 if (colourFmt != "YUV420P") {
700 PTRACE(1, colourFmt << " is unsupported.");
701 return FALSE;
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");
714 return;
716 numCameras = 0;
717 rom1394_directory rom_dir;
718 if (handle == NULL) {
719 PTRACE(0, "Weird, handle not created yet :-/");
720 return;
722 if (raw1394_set_port(handle, 0) < 0) {
723 PTRACE(0, "Cannot set port");
724 return;
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);
731 return;
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);
747 return 0;
749 // End Of File ///////////////////////////////////////////////////////////////