Using the C++ API to record videos

acquisition
recording
genicam
mingw
#1

Hello,

we are attempting to use the C++ API to connect to a Teledyne gig-e-camera and record videos with it. We are on Windows 10 and compiling with CMake and g++.

Connecting to the camera and grabbing images is working fine. We accomplish this with

    Cvb::String path(CVB_LIT("%CVB%/drivers/GenICam.vin"));
    path = Cvb::ExpandPath(path);
    auto device = Cvb::DeviceFactory::Open(path);
    auto stream = device->Stream(0);
    stream->Start();
    auto waitResult = stream->WaitFor(std::chrono::seconds(10));
    auto img = waitResult.Image;

However, we are not able to record videos with the C++ API. Looking at the documentation, we are trying to use the Recorder class which is found in cvb/movie2/recorder.hpp.

    // Path
    std::string video_path = "here.avi";

    // Size
    int video_height = 512;
    int video_width = 512;
    Cvb::Size2D <int> video_size(video_height, video_width);
        
    // Format
    auto recorder_format = Cvb::Movie2::RecorderPixelFormat::Color;
    
    // Create Recorder
    Cvb::Movie2::Recorder recorder(video_path, video_size, recorder_format);

Unfortunately, when we try to compile this, we get linker errors that look like this:

    CMakeFiles\gig_e_cam.dir/objects.a(gig_e_cam.cpp.obj):gig_e_cam.cpp:(.text$_ZN3Cvb5V_1_16Movie28RecorderC1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEENS0_6Size2DIivEENS1_19RecorderPixelFormatENS1_16RecorderSettingsE[_ZN3Cvb5V_1_16Movie28RecorderC1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEENS0_6Size2DIivEENS1_19RecorderPixelFormatENS1_16RecorderSettingsE]+0x828): undefined reference to `Movie2StartRecording'

My questions are:

  • are we on the right track with the Movie2::Recorder Class if we want to grab videos, both for streaming and saving to disk? If yes, how is it connected to the stream object from above?
  • What is causing this linker Error?

Any help would be much appreciated.
Thank you.

#2

Hi and welcome @F_E,

we use Visual C++ to create our libraries. This produces lib files instead of the a files required by g++. g++ is not officially supported for :cvb: on Windows.

I assume you use MinGW64 to compile with g++? As we use C exports it might just work to explicitly link all the lib files. For that don’t use -l option as g++ then expects an a file. Just add the lib files like a cpp file. If cmake doesn’t prevent it, it might be enough to give the full path to the lib files in add_executable (e.g. ${cvb_path}/Lib/C/CVCImg.lib).

Also, if acquisition works, and you use our FindCVB.cmake: Did you add the CVB::Movie2 target?

#3

Hi @parsd,

thank you for the welcome and the reply.

Your assumption with MinGW64 / g++ is correct, this is what we use.

Adding the CVB::Movie2 target got rid of the linker error, so thank you! However, when we try to record a video, the file that CVB outputs appears to be broken and not playable.

Our current way of trying to record a video works like this: (For brevity, the variables video_height, video_width, video_path and stream are defined as above.)

Cvb::Movie2::RecorderSettings recorder_settings = Cvb::Movie2::RecorderSettings();

recorder_settings.SetCodec("DV Video Encoder"); // "MJPEG Compressor" and "No Compression" also lead to broken files.
//These Codecs are all the Codecs returned by recorder_settings.AvailableCodecs();

Cvb::Movie2::Recorder recorder(video_path, video_size, recorder_format, recorder_settings);

for (int i = 0 ; i < 100 ; i++){

        auto waitResult = stream->WaitFor(std::chrono::seconds(10));

        if (waitResult.Status == Cvb::WaitStatus::Timeout){
            throw std::runtime_error("acquisition timeout");
        }
        
        auto img = waitResult.Image;
        recorder.Write(* img);
}

stream->Stop();

Is this the correct way of doing it?

The output files do not contain any bytes that would identify them as avi files. Intact avi files all contain the RIFF header at the beginning. The output generated contains about 100.000 0's instead of any header. No video player (including VLC) is able to play these files.

Do you have any idea as to why it is not working? Does one need to use Visual Studio to work with CVB or is there some programming error on our side?

#4

First: cool that linking works :smile:

Second: I would guess if linking works, this is no g++ issue as the underlying handles are generated within the CVB libraries (CVB++ is just a header only wrapper around our C-API) C-API is often a bit cumberson to use, but enables for example things like this.

Did you try not setting a codec? Also: does your img match your video_size and recorder_format? It must be either Mono8 or RGB8.

I did a tiny test (with VS2017, though):

#include <iostream>

#include <cvb/device_factory.hpp>
#include <cvb/utilities/system_info.hpp>
#include <cvb/movie2/recorder.hpp>

int main()
{
  const auto inputPath = Cvb::Utilities::SystemInfo::InstallPath() + CVB_LIT("Tutorial\\ClassicSwitch.emu");

  const auto device = Cvb::DeviceFactory::Open(inputPath);

  Cvb::Movie2::Recorder recorder{CVB_LIT("test.avi"), device->DeviceImage()->Size(), Cvb::Movie2::RecorderPixelFormat::Mono};

  auto stream = std::dynamic_pointer_cast<Cvb::Driver::IndexedStream>(device->Stream());
  stream->Start();

  std::cout << "Writing frames to working directory\n";
  for(int i = 0, count = stream->ImageCount(); i < count; ++i)
  {
    auto waitResult = stream->Wait(); // emus don't time-out

    recorder.Write(*waitResult.Image);

    std::cout << "#" << (i + 1) << "/" << count << "\n";
  }
  std::cout << "done...\n";
  stream->Stop();
}

This is the CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)
project(MovieTest)

file(TO_CMAKE_PATH "$ENV{CVB}/cmake" CVB_MODULE_PATH)
list(APPEND CMAKE_MODULE_PATH "${CVB_MODULE_PATH}")

find_package(CVB REQUIRED)

set (CMAKE_CXX_STANDARD 11)
add_executable(MovieTest
  main.cpp
)

target_link_libraries(MovieTest 
  CVB::CVCDriver 
  CVB::Movie2 
  CVB::CVCUtilities
)