2017-04-01

How to record video with GStreamer and Logitech's c920 webcam on a Mac in 1080p

This took me unnecessarily long, so I'm going to write it down here for anybody who tries to do a similar thing. It uses h264 hardware encoding, so it works at full 1080p 30fps. Unfortunately I couldn't manage to make the in-camera h264 encoding work. It doesn't seem to be supported by the avfoundation plugin. Still, it's the best way I've found to record full framerate 1080p video on a Mac from a Logitech c920 without the resulting video file taking a massive amount of space, as it's the case when recording with Quicktime. The only problem that I haven't been able to solve is recording from 2 cameras at the same time, since the second camera will invariably drop frames.

Okay, let's start.

First we have to install gstreamer:

brew install gstreamer
brew install gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly

Not all these plugins may be necessary, just in case.

Also, the only way I know how to list capture devices is using ffmpeg, so:

brew install ffmpeg

You can run this command to list available devices:

ffmpeg -f avfoundation -list_devices true -i ""

Then, to record just the video you can do this (supposing you want to record device number 0):

gst-launch-1.0 avfvideosrc -e device-index=0 ! video/x-raw,width=1920,height=1080 ! videoconvert ! vtenc_h264_hw realtime=true max-keyframe-interval=5 ! queue ! mp4mux ! filesink location='output.mp4'

To record and display what's being recorded at the same time, you can do this:

gst-launch-1.0 avfvideosrc -e device-index=0 ! video/x-raw,width=1920,height=1080 ! tee name=t t. ! queue ! osxvideosink sync=false t. ! videoconvert ! vtenc_h264_hw realtime=true max-keyframe-interval=5 ! queue ! mp4mux ! filesink location='output3.mp4'

Finally, to record, display, and record audio too, you can do this:

gst-launch-1.0 avfvideosrc -e device-index=0 ! video/x-raw,width=1920,height=1080 ! tee name=t t. ! queue ! osxvideosink sync=false t. ! videoconvert ! vtenc_h264_hw realtime=true max-keyframe-interval=5 ! queue ! mux. osxaudiosrc device=0 ! audioconvert ! avenc_aac ! queue ! mux. mp4mux name=mux ! filesink location='output.mp4'

This assumes that your audio recording device is number 0. This number doesn't match the device numbers you get from the ffmpeg command, and I don't know how to find what number represents each device. You'll have to do trial-and-error.

If I learn any more details about this I'll update this post. Please comment if you have any more information, since there's not much info online about how to use GStreamer on a Mac.

2012-01-03

How to fix IBus not working with QT4 in Ubuntu 11.10

I had a problem with the Anki software (Qt based) not working with the default input method in Ubuntu 11.10 for Chinese and Japanese (IBus).

After a bit of looking around the Internets and trying a few things that didn't work I read something that gave me the idea to search for a package related to using IBus on QT, and there it was, it's surprisingly called ibus-qt4.

So, to fix the problem, I just had to install this package, and voila:

 $ sudo apt-get install ibus-qt4

After this, just restart the application that uses QT and the input methods should be working on it (you may need to restart all applications that use QT, not sure about it, so in a KDE desktop you may need to log out and back in).

There you are, have fun!

And yes, the Earth went back to an arbitrary position after doing a full orbit around the Sun, so let's all congratulate each other for it!

2011-11-30

Using Wacom Bamboo CTH-470 and CTH-670 in Ubuntu 11.10 Oneiric Ocelot

As of today, the Wacom drivers in the latest kernel version in Oneiric (3.0.0-13) don't include support for the latest batch of Wacom tablets, so if we want to make it work we have do download and compile the driver ourselves.

There already are instructions on how to do it here, but I decided to write it in a more organized way.

Start by downloading the file wacom-bamboo.tar.gz from here (it's attached to the third post). Then open a terminal and uncompress it:
 $ gzip -dc wacom-bamboo.tar.gz | tar xvof -
 $ cd wacom
You will need to install the following packages to be able to compile the driver:
 $ sudo apt-get install build-essential libx11-dev libxi-dev x11proto-input-dev xserver-xorg-dev libxrandr-dev libncurses5-dev autoconf libtool
And the kernel headers:
 sudo apt-get install linux-headers-generic
We compile the module and copy it to its appropriate destination with the following commands:
 $ make -C /lib/modules/$(uname -r)/build SUBDIRS=$(pwd) modules
 $ sudo cp ./wacom.ko /lib/modules/`uname -r`/kernel/drivers/input/tablet/wacom.ko
Finally we need to update the list of module dependencies so the kernel can find our new driver:
 $ sudo depmod -a
After doing all this, just unplugging and plugging our tablet should get it working.

You can change a few of the tablet's settings, like button mapping and tablet orientation, by going to System Settings -> Wacom Graphics Tablet.

A thing that I found very annoying when trying to use my CTH-470 was that, since it includes finger support, it would keep detecting my hand and clicking around randomly when I was just trying to use the pen input, so I'm going to explain how to disable finger input using the xsetwacom command.

Open a terminal and write the following command:
 $ xsetwacom --list devices  
 Wacom Bamboo 16FG 4x5 Finger touch id: 13 type: TOUCH     
 Wacom Bamboo 16FG 4x5 Finger pad  id: 14 type: PAD      
 Wacom Bamboo 16FG 4x5 Pen stylus  id: 15 type: STYLUS    
 Wacom Bamboo 16FG 4x5 Pen eraser  id: 16 type: ERASER  
It will print a list of devices in our tablet as displayed. In our case, the one we are interested in is "Wacom Bamboo 16FG 4x5 Finger touch". Next we will disable it with the following command (modify according to the output of the previous command):
 $ xsetwacom set "Wacom Bamboo 16FG 4x5 Finger touch" Touch off  
In case we want to turn it on again, we can just replace off with on in the previous command. This configuration will be reset when we log out, so if you want to keep it always off you can write the command in a shell script and add it to the list of startup applications.

In case you want to change other settings besides the ones available in System Settings, please refer again to this thread in Ubuntu Forums.

original source (Japanese)

2011-11-29

Camera tethered capturing using libgphoto2

Here is some code showing how to use libgphoto2 to do some tethered capturing using a DSLR camera connected to the computer's USB port, since I haven't seen much documentation on how to do this, and the samples from libgphoto2 don't work as-is if you want to do continuous capturing.

This code is mainly based on the lunkwill-canon-capture.c example included with libgphoto2, but it has been adapted for continuous shooting by looking at the gphoto2 code.

This code has been tested with a Nikon D700, but any camera with tethered shooting support for libgphoto2 should work. Note that if you are using a Canon camera, you may have to enable the Canon-specific capture mode using the canon_enable_capture function found in the config.c file in the examples included with libgphoto2.

I'll just be showing how to capture pictures, so in case you want to use some of the other functions, like read or modify camera settings, please refer to the examples included with libgphoto2.

Also, please notice that for a lot of purposes it's easier to interface your code with gphoto2 through command line parameters or running the gphoto2 shell (with gphoto2 --shell) and sending commands through standard input. The libgphoto2 approach gives you more control, but it's also easier to mess up and leave the camera in an inconsistent state that will require unplugging and plugging it again.

Let's start with includes and globals:
 #include <stdio.h>  
 #include <fcntl.h>  
   
 #include <gphoto2/gphoto2-camera.h>  
   
 Camera *camera;  
 GPContext *context;  
The pointers camera and context are used by libgphoto2 to interact with the camera.

Now we are going to define the callback functions that libgphoto2 is going to call when there's an error with the capturing or it has something to tell us. They are mandatory, and failing to register them will results in errors being thrown when we try to initialize the camera. We'll use them to just print their respective messages:
void error_func (GPContext *context, const char *format, va_list args, void *data) {
 fprintf  (stderr, "*** Contexterror ***\n");
 vfprintf (stderr, format, args);
 fprintf  (stderr, "\n");
}

void message_func (GPContext *context, const char *format, va_list args, void *data) {
 vprintf (format, args);
 printf ("\n");
}
The next thing to do will be to define our main function. At the beginning we will just initialize the camera and context:
int main (int argc, char *argv[]) {
 gp_camera_new (&camera);
 context = gp_context_new();
Next step will be to register the callbacks we defined previously:
 // set callbacks for camera messages
 gp_context_set_error_func(context, error_func, NULL);
 gp_context_set_message_func(context, message_func, NULL);
Next we will detect cameras and choose the first one. In case you want to be fancier and let the user choose a different camera, check the sample-multi-detect.c file in libgphoto2 examples.
 //This call will autodetect cameras, take the first one from the list and use it
 printf("Camera init. Can take more than 10 seconds depending on the "
  "memory card's contents (remove card from camera to speed up).\n");
 int ret = gp_camera_init(camera, context);
 if (ret < GP_OK) {
  printf("No camera auto detected.\n");
  gp_camera_free(camera);
  return 1;
 }
Now we are ready to do some capturing. As an example of continuous capturing I just wrote a loop that captures ten shots as fast as possible. For your application you may want to pause for a certain time, or react to some user input, network buffer, etc.
 // take 10 shots
 char filename[256];
 int const nShots = 10;
 int i;

 // do some capturing
 for (i = 0; i < nShots; i++) {
  snprintf(filename, 256, "shot-%04d.nef", i);
  printf("Capturing to file %s\n", filename);
  capture(filename);
 }
This will generate consecutive file names for all the shots and do the capturing. The capture function contains all the juicy stuff, but we'll go to it in a while. Before that we'll finish writing the main function by closing the camera:
 // close camera
 gp_camera_unref(camera);
 gp_context_unref(context);

 return 0;
}
The capture function contains all the code necessary to take a shot and copy the result image to the computer's file system, while avoiding saturating the camera by sending it requests before it's done doing its work. First we'll define the variables needed.
int capture (const char *filename) {
 int fd, retval;
 CameraFile *file;
 CameraFilePath camera_file_path;

 // this was done in the libphoto2 example code, but doesn't seem to be necessary
 // NOP: This gets overridden in the library to /capt0000.jpg
 //snprintf(camera_file_path.folder, 1024, "/");
 //snprintf(camera_file_path.name, 128, "foo.jpg");
fd will be the file descriptor we use to write to disk. retval will hold the return value of libgphoto2 calls so we can check for errors. file will hold a reference to the picture in the camera's file system, and camera_file_path will tell us the path of the captured picture in the camera's file system (necessary to retrieve the captured picture).

Next we'll capture a picture:
 // take a shot
 retval = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, context);
 
 if (retval) {
  // do some error handling, probably return from this function
 }

 printf("Pathname on the camera: %s/%s\n", camera_file_path.folder, camera_file_path.name);
After running gp_camera_capture, the camera_file_path variable has been written. Next we'll open the file in the computer's file system and create the CameraFile object:
 fd = open(filename, O_CREAT | O_WRONLY, 0644);
 // create new CameraFile object from a file descriptor
 retval = gp_file_new_from_fd(&file, fd);
 
 if (retval) {
  // error handling
 }
Next we'll retrieve the picture from the camera and write it to disk:
 // copy picture from camera
 retval = gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name,
  GP_FILE_TYPE_NORMAL, file, context);
 
 if (retval) {
  // error handling
 }
And finally remove the picture from the camera's file system and free the CameraFile object:
 printf("Deleting\n");
 // remove picture from camera memory
 retval = gp_camera_file_delete(camera, camera_file_path.folder, camera_file_path.name,
   context);
 
 if (retval) {
  // error handling
 }

 // free CameraFile object
 gp_file_free(file);
Now, before we can return from the function and risk it being called again while the camera is still busy, we'll wait for events from the camera until we receive GP_EVENT_CAPTURE_COMPLETE and there are no more events to process. This probably could be done more efficiently, since the program could be doing something else while waiting for events.
 printf("Wait for events from camera\n");
 while(1) {
  retval = gp_camera_wait_for_event(camera, waittime, &type, &data, context);
  if(type == GP_EVENT_TIMEOUT) {
   break;
  }
  else if (type == GP_EVENT_CAPTURE_COMPLETE) {
   printf("Capture completed\n");
   waittime = 100;
  }
  else if (type != GP_EVENT_UNKNOWN) {
   printf("Unexpected event received from camera: %d\n", (int)type);
  }
 }
 return 0;
}
This is it. This should be all you need to do to tethered capturing using libgphoto2. I hope this is useful to somebody. Below I'll paste the entire source code. To compile it using gcc you can run:


gcc -o tethered tethered.c -lgphoto2


You will need the development files of libgphoto. In Ubuntu you can install them by running:

sudo apt-get install libgphoto2-2-dev

Here is the complete source code:

#include <stdio.h>
#include <fcntl.h>

#include <gphoto2/gphoto2-camera.h>

Camera *camera;
GPContext *context;

void error_func (GPContext *context, const char *format, va_list args, void *data) {
 fprintf(stderr, "*** Contexterror ***\n");
 vfprintf(stderr, format, args);
 fprintf(stderr, "\n");
}

void message_func (GPContext *context, const char *format, va_list args, void *data) {
 vprintf(format, args);
 printf("\n");
}

int capture (const char *filename) {
 int fd, retval;
 CameraFile *file;
 CameraFilePath camera_file_path;

 // this was done in the libphoto2 example code, but doesn't seem to be necessary
 // NOP: This gets overridden in the library to /capt0000.jpg
 //snprintf(camera_file_path.folder, 1024, "/");
 //snprintf(camera_file_path.name, 128, "foo.jpg");

 // take a shot
 retval = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, context);
 
 if (retval) {
  // do some error handling, probably return from this function
 }

 printf("Pathname on the camera: %s/%s\n", camera_file_path.folder, camera_file_path.name);

 fd = open(filename, O_CREAT | O_WRONLY, 0644);
 // create new CameraFile object from a file descriptor
 retval = gp_file_new_from_fd(&file, fd);
 
 if (retval) {
  // error handling
 }

 // copy picture from camera
 retval = gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name,
   GP_FILE_TYPE_NORMAL, file, context);
 
 if (retval) {
  // error handling
 }

 printf("Deleting\n");
 // remove picture from camera memory
 retval = gp_camera_file_delete(camera, camera_file_path.folder, camera_file_path.name,
   context);
 
 if (retval) {
  // error handling
 }

 // free CameraFile object
 gp_file_free(file);

 // Code from here waits for camera to complete everything.
 // Trying to take two successive captures without waiting
 // will result in the camera getting randomly stuck.
 int waittime = 2000;
 CameraEventType type;
 void *data;

 printf("Wait for events from camera\n");
 while(1) {
  retval = gp_camera_wait_for_event(camera, waittime, &type, &data, context);
  if(type == GP_EVENT_TIMEOUT) {
   break;
  }
  else if (type == GP_EVENT_CAPTURE_COMPLETE) {
   printf("Capture completed\n");
   waittime = 100;
  }
  else if (type != GP_EVENT_UNKNOWN) {
   printf("Unexpected event received from camera: %d\n", (int)type);
  }
 }
 return 0;
}

int main (int argc, char *argv[]) {
 gp_camera_new (&camera);
 context = gp_context_new();

 // set callbacks for camera messages
 gp_context_set_error_func(context, error_func, NULL);
 gp_context_set_message_func(context, message_func, NULL);

 //This call will autodetect cameras, take the first one from the list and use it
 printf("Camera init. Can take more than 10 seconds depending on the "
  "memory card's contents (remove card from camera to speed up).\n");
 int ret = gp_camera_init(camera, context);
 if (ret < GP_OK) {
  printf("No camera auto detected.\n");
  gp_camera_free(camera);
  return 1;
 }

 // take 10 shots
 char filename[256];
 int const nShots = 10;
 int i;

 // do some capturing
 for (i = 0; i < nShots; i++) {
  snprintf(filename, 256, "shot-%04d.nef", i);
  printf("Capturing to file %s\n", filename);
  capture(filename);
 }

 // close camera
 gp_camera_unref(camera);
 gp_context_unref(context);

 return 0;
}

2011-11-28

Just my two cents here...

This is just going to be a place where I write about stuff I figure out so people can later find it using a search engine.

It's probably not worth a lot subscribing to this blog, since I'm going to be writing about quite unrelated stuff (although mostly about computing).