Search code examples
qtvideoqt5.3qt5.4

Recording Video using Qt5.4


I am building a cross-platform application to record multimedia files for ongoing processing. This is based on an inherited application and I am not able to re-write using alternative libraries.

My current issue is that the QMediaRecorder does not apparently save the video file onto the local drive - I have temporarily hard-coded the file to be saved as banana.mov into the users root folder.

When executed, the output file is not being saved.

I have tried forcing the resolution as suggested here and have seen that others have had issues recording from windows but OSX was fine

Development environment OSX 10.10 with Qt5.4 (the same issue is also happening on a Windows 8.1 machine using Qt5.3)

This code on Github is based on the camera example, with additional debug code added when trying to identify and reproduce the issue.

While investigating, the QMediaRecorder::​supportedAudioCodecs and QMediaRecorder::supportedVideoCodecs both return empty lists. This happens on both the OSX build and the Windows environments.

The debug output is as follows:

Status change SIGNAL 'The recorder is initializing.'
Output location file:~/banana.mov
2015 01 05 14:59:58.111 Number of supported AUDIO Codecs 0
2015 01 05 14:59:58.111 Number of Audio sample rates 0
2015 01 05 14:59:58.111 Number of Video Codecs 0
2015 01 05 14:59:58.111 Number of Video Frame Rates 0
2015 01 05 14:59:58.111 Number of Containers 0
Location Changed SIGNAL 'file:~/banana.mov'
State change SIGNAL 'The recording is requested.'
Recording should have started
2015 01 05 14:59:58.111 Number of supported AUDIO Codecs 0
2015 01 05 14:59:58.111 Number of Audio sample rates 0
2015 01 05 14:59:58.111 Number of Video Codecs 0
2015 01 05 14:59:58.111 Number of Video Frame Rates 0
2015 01 05 14:59:58.111 Number of Containers 0
Status change SIGNAL 'Recording is requested but not active yet.'

I have a feeling that I'm missing something really obvious, I just haven't spotted it quite yet!

edit 1 The obvious this is that the status is Recording is requested but not active yet and not Recording is active. I'm currently trying to work out why the recording has not started.

edit 2 The audio recorder example does record and save an audio file. It looks like QMediaRecorder does not return a list of available audio codecs, but QAudioRecorder does return a list of audio codecs. I am getting the same results on both Windows 8.1 using Qt5.3 and OSX using Qt 5.4


Solution

  • It's highly likely your looking at an OS specific artifact rather than a core problem with QT.

    I've seen this issue so many times in so many tool kits and frameworks that it scares me that no one has yet figured out an elegant solution.

    The Basis of the Problem

    Most operating systems implement some kind of protection for critical system files.

    Under *nix, this is in the form of the user/group permissions system, under windows it's similar but with the UAC (User Access Control) sub system.

    What this boils down to is that you often can't just pick an arbitrary location to write files to, not without first seeking permission from the various security API's and mechanisms in the OS to do so.

    The second half of the problem then, comes from variable expansion.

    In *nix particularly the tilde char '~' is expanded by the shell to mean the users home directory.

    When we say shell, we mean bash, tch, csh or what ever environment your running the app in.

    Within this mix, we'll also put the desktop environment too, as most things like Kde, Gnome, Unity or what ever else is being used, have some kind of operating system call that when a '~' is passed to it, knows to convert it to '/home/neil/' or what ever it needs to be expanded too.

    Windows also has a similar thing, whereby you can make an OS call and say 'Hey mr operating system, where are my user folders stored', which it will happily reply to with something like 'c:\users\'

    Why The Problem Manifests The Way It Does

    Simply because when your in charge of creating the path strings yourself, in your own application, things are often not passed to these various OS calls for expansion and security clearance, you pretty much have to make sure your the one that calls them.

    The exception to this rule, is if your using the *nix philosophy of small tools combined to do one job. In this case, you would often pass a result to a shell based program, which because it's running via the shell then knows that if it sees '~' it has to expand it.

    Beacuse your using direct file access, managing your own file paths when you thought you where writing to '\home\neil\file.mov' you where in actual fact trying to write a file called 'file.mov' in the current folder where your app is running from, in a folder called '~' which I'm willing to be doesn't exist.

    Add to this the fact that a lot of these frameworks (QT is no exception) are designed to hide and abstract away all the ugly details, there's a good chance it consumed the OS exception that was generated when you actually tried to write the file, if it had not then your app would most likely have crashed with some kind of exception dialog.

    How to Solve The Problem

    There are 3 approaches you can take to mitigating this.

    • 1) You can hard code the paths, that is you can explicitly say to the application always store your file at '/home/neil/videos/blah.mov', this has a disadvantage however that every user of the app will need a custom build, as it's unlikely 'person2' will have write permissions on 'neil's home directory.

    • 2) You can build in functionality that gives the user a dialog box and asks them where they would like to save the file. Since your using something like QT this should be very easy, most of these UI tool kits have built in functionality to easily present the user with such an experience.

    • 3) You can find out if your framework or the underlying OS has any calls for you to ask who the current user is, and where their home directory is, you can then use the returned information to dynamically build a static patch similar to that in option 1. Doing things this way ensures that the application adapts automatically to it's environment irrespective of the user.

    Myself personally, I generally adopt option 1 during development, then when development is complete I switch to number 2, very rarely do I use number 3 in desktop based software.

    Option 3 for me is often used when the application in question is designed to do a single job, such as converting a file for another process to work with, or generating some output for a server to display in a web page etc.

    For you, right now as a solution to this question however, Option 1 is your best bet.