#// Copyright (C) 2023 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifdef DLIB_USE_FFMPEG #include <fstream> #include <vector> #include <chrono> #include <dlib/dir_nav.h> #include <dlib/config_reader.h> #include <dlib/media.h> #include <dlib/array2d.h> #include <dlib/matrix.h> #include <dlib/rand.h> #include <dlib/image_transforms.h> #include <dlib/image_io.h> #include "tester.h" #ifndef DLIB_FFMPEG_DATA static_assert(false, "Build is faulty. DLIB_VIDEOS_FILEPATH should be defined by cmake"); #endif namespace { using namespace std; using namespace std::chrono; using namespace test; using namespace dlib; using namespace dlib::ffmpeg; logger dlog("test.video"); ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// dlib::rand rng(10000); template <class pixel> matrix<pixel> get_random_image() { matrix<pixel> img; img.set_size(rng.get_integer_in_range(1, 128), rng.get_integer_in_range(1, 128)); for (long i = 0 ; i < img.nr() ; ++i) for (long j = 0 ; j < img.nc() ; ++j) assign_pixel(img(i,j), rng.get_random_8bit_number()); return img; } template <class pixel> void check_image ( const frame& f, const matrix<pixel>& img ) { DLIB_TEST(!f.is_empty()); DLIB_TEST(f.is_image()); DLIB_TEST(!f.is_audio()); DLIB_TEST(f.samplefmt() == AV_SAMPLE_FMT_NONE); DLIB_TEST(f.sample_rate() == 0); DLIB_TEST(f.nsamples() == 0); DLIB_TEST(f.layout() == 0); DLIB_TEST(f.nchannels() == 0); DLIB_TEST(f.pixfmt() == pix_traits<pixel>::fmt); DLIB_TEST(f.height() == img.nr()); DLIB_TEST(f.width() == img.nc()); matrix<pixel> dummy; convert(f, dummy); DLIB_TEST(dummy.nc() == img.nc()); DLIB_TEST(dummy.nr() == img.nr()); DLIB_TEST(img == dummy); } template <class pixel> void test_frame() { const matrix<pixel> img1 = get_random_image<pixel>(); const matrix<pixel> img2 = get_random_image<pixel>(); // Create a frame frame f1(img1.nr(), img1.nc(), pix_traits<pixel>::fmt, system_clock::time_point{}); convert(img1, f1); // Check frame is correct check_image(f1, img1); // Copy frame f2(f1); // Check it's a deepcopy, i.e. pointers are different but values are the same check_image(f2, img1); // Check pointers are different DLIB_TEST(f1.get_frame().data[0] != f2.get_frame().data[0]); // Set f2 and check this doesn't affect f1 f2 = frame(img2.nr(), img2.nc(), pix_traits<pixel>::fmt, system_clock::time_point{}); convert(img2, f2); check_image(f2, img2); check_image(f1, img1); DLIB_TEST(f1.get_frame().data[0] != f2.get_frame().data[0]); // Move f2 = std::move(f1); check_image(f2, img1); DLIB_TEST(f1.is_empty()); print_spinner(); } template <typename pixel_type> static double psnr(const matrix<pixel_type>& img1, const matrix<pixel_type>& img2) { DLIB_TEST(have_same_dimensions(img1, img2)); const long nk = width_step(img1) / img1.nc(); const long data_size = img1.size() * nk; auto* data1 = reinterpret_cast<const uint8_t*>(image_data(img1)); auto* data2 = reinterpret_cast<const uint8_t*>(image_data(img2)); double mse = 0; for (long i = 0; i < data_size; i += nk) { for (long k = 0; k < nk; ++k) mse += std::pow(static_cast<double>(data1[i + k]) - static_cast<double>(data2[i + k]), 2); } mse /= data_size; return 20 * std::log10(pixel_traits<pixel_type>::max()) - 10 * std::log10(mse); } void test_load_frame(const std::string& filename) { matrix<rgb_pixel> img1, img2; load_image(img1, filename); load_frame(img2, filename); DLIB_TEST(img1.nr() == img2.nr()); DLIB_TEST(img1.nc() == img2.nc()); const double similarity = psnr(img1, img2); DLIB_TEST_MSG(similarity > 25.0, "psnr " << similarity); } template<class pixel_type> void test_load_save_frame(const std::string& filename) { matrix<pixel_type> img1, img2; img1 = get_random_image<pixel_type>(); save_frame(img1, filename, {{"qmin", "1"}, {"qmax", "1"}}); load_frame(img2, filename); DLIB_TEST(img1.nr() == img2.nr()); DLIB_TEST(img1.nc() == img2.nc()); const double similarity = psnr(img1, img2); DLIB_TEST_MSG(similarity > 20.0, "psnr " << similarity); } ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// // DECODER ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// template < class image_type > void test_decoder_images_only( const std::string& filepath, const std::string& codec, const int nframes, const int height, const int width ) { decoder dec([&] { decoder::args args; args.codec_name = codec; return args; }()); DLIB_TEST(dec.is_open()); DLIB_TEST(dec.get_codec_name() == codec); DLIB_TEST(dec.is_image_decoder()); // You can't test for dec.height() or dec.width() yet because no data has been pushed to the decoder. image_type img; int counter{0}; ifstream fin{filepath, std::ios::binary}; std::vector<char> buf(1024); const auto callback = [&](image_type& img) mutable { DLIB_TEST(img.nr() == height); DLIB_TEST(img.nc() == width); DLIB_TEST(dec.height() == height); DLIB_TEST(dec.width() == width); ++counter; if (counter % 10 == 0) print_spinner(); }; while (fin) { fin.read(buf.data(), buf.size()); size_t ret = fin.gcount(); DLIB_TEST(dec.push((const uint8_t*)buf.data(), ret, wrap(callback))); } dec.flush(wrap(callback)); DLIB_TEST(counter == nframes); DLIB_TEST(!dec.is_open()); } template < class image_type > void test_decoder_full ( const std::string& filepath, const std::string& codec, const int nframes, const int height, const int width, const int sample_rate, bool has_image, bool has_audio ) { decoder dec([&] { decoder::args args; args.codec_name = codec; return args; }()); DLIB_TEST(dec.is_open()); DLIB_TEST(dec.get_codec_name() == codec); DLIB_TEST(dec.is_audio_decoder() == has_audio); DLIB_TEST(dec.is_image_decoder() == has_image); image_type img; audio<int16_t, 2> audio_buf; frame frame_copy; int count{0}; int nsamples{0}; int iteration{0}; ifstream fin{filepath, std::ios::binary}; std::vector<char> buf(1024); const resizing_args args_image { 0, 0, pix_traits<pixel_type_t<image_type>>::fmt }; const resampling_args args_audio { sample_rate, dlib::ffmpeg::details::get_layout_from_channels(decltype(audio_buf)::nchannels), sample_traits<decltype(audio_buf)::format>::fmt }; const auto callback = [&](frame& f) { if (has_audio) { convert(f, audio_buf); convert(audio_buf, frame_copy); DLIB_TEST(f.is_audio()); DLIB_TEST(f.sample_rate() == sample_rate); DLIB_TEST(f.samplefmt() == args_audio.fmt); DLIB_TEST(frame_copy.is_audio()); DLIB_TEST(frame_copy.sample_rate() == sample_rate); DLIB_TEST(frame_copy.samplefmt() == args_audio.fmt); nsamples += f.nsamples(); DLIB_TEST(dec.sample_rate() == sample_rate); DLIB_TEST(dec.sample_fmt() == args_audio.fmt); } else { convert(f, img); convert(img, frame_copy); DLIB_TEST(f.is_image()); DLIB_TEST(f.height() == height); DLIB_TEST(f.width() == width); DLIB_TEST(f.pixfmt() == args_image.fmt); DLIB_TEST(frame_copy.is_image()); DLIB_TEST(frame_copy.height() == height); DLIB_TEST(frame_copy.width() == width); DLIB_TEST(frame_copy.pixfmt() == args_image.fmt); DLIB_TEST(img.nr() == height); DLIB_TEST(img.nc() == width); ++count; DLIB_TEST(dec.height() == height); DLIB_TEST(dec.width() == width); } ++iteration; if (iteration % 10 == 0) print_spinner(); }; while (fin) { fin.read(buf.data(), buf.size()); size_t ret = fin.gcount(); DLIB_TEST(dec.push((const uint8_t*)buf.data(), ret, wrap(callback, args_image, args_audio))); } dec.flush(wrap(callback, args_image, args_audio)); DLIB_TEST(count == nframes); DLIB_TEST(!dec.is_open()); } ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// // DEMUXER ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// template < class image_type > void test_demuxer_images_only ( const std::string& filepath, const int nframes, const int height, const int width ) { demuxer cap({filepath, video_enabled, audio_enabled}); DLIB_TEST(cap.is_open()); DLIB_TEST(cap.video_enabled()); DLIB_TEST(cap.height() == height); DLIB_TEST(cap.width() == width); // DLIB_TEST(cap.estimated_nframes() == nframes); // This won't always work with ffmpeg v3. v4 onwards is fine image_type img; int counter{0}; while (cap.read(img)) { DLIB_TEST(img.nr() == height); DLIB_TEST(img.nc() == width); ++counter; if (counter % 10 == 0) print_spinner(); } DLIB_TEST(counter == nframes); DLIB_TEST(!cap.is_open()); } void test_demuxer_full ( const std::string& filepath, const int nframes, const int height, const int width, const int sample_rate, bool has_video, bool has_audio ) { demuxer cap(filepath); DLIB_TEST(cap.video_enabled() == has_video); DLIB_TEST(cap.audio_enabled() == has_audio); DLIB_TEST(cap.height() == height); DLIB_TEST(cap.width() == width); DLIB_TEST(cap.sample_rate() == sample_rate); // DLIB_TEST(cap.estimated_nframes() == nframes); // This won't always work with ffmpeg v3. v4 onwards is fine const int estimated_samples_min = cap.estimated_total_samples() - cap.sample_rate(); // - 1s const int estimated_samples_max = cap.estimated_total_samples() + cap.sample_rate(); // + 1s dlib::ffmpeg::frame frame; int counter_images{0}; int counter_samples{0}; int iteration{0}; resizing_args args_image; args_image.fmt = AV_PIX_FMT_RGB24; resampling_args args_audio; args_audio.sample_rate = sample_rate; args_audio.fmt = AV_SAMPLE_FMT_S16; args_audio.channel_layout = AV_CH_LAYOUT_STEREO; while (cap.read(frame, args_image, args_audio)) { if (frame.is_image()) { DLIB_TEST(frame.height() == height); DLIB_TEST(frame.width() == width); DLIB_TEST(frame.pixfmt() == args_image.fmt); ++counter_images; } if (frame.is_audio()) { DLIB_TEST(frame.sample_rate() == sample_rate); DLIB_TEST(frame.layout() == args_audio.channel_layout); DLIB_TEST(frame.samplefmt() == args_audio.fmt); counter_samples += frame.nsamples(); } ++iteration; if (iteration % 10 == 0) print_spinner(); } DLIB_TEST(counter_images == nframes); DLIB_TEST(counter_samples >= estimated_samples_min); //within 1 second DLIB_TEST(counter_samples <= estimated_samples_max); //within 1 second DLIB_TEST(!cap.is_open()); } ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// // ENCODER ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// void test_encoder ( const std::string& filepath, AVCodecID image_codec, AVCodecID audio_codec ) { // Load a video/audio as a source of frames demuxer cap(filepath); DLIB_TEST(cap.is_open()); const bool has_video = cap.video_enabled(); const bool has_audio = cap.audio_enabled(); const int height = cap.height(); const int width = cap.width(); const auto pixfmt = cap.pixel_fmt(); const auto fps = cap.fps(); const int rate = cap.sample_rate(); const auto samplefmt = cap.sample_fmt(); const auto layout = cap.channel_layout(); std::vector<frame> frames; frame f; int nimages{0}; int nsamples{0}; while (cap.read(f)) { if (f.is_image()) ++nimages; if (f.is_audio()) nsamples += f.nsamples(); frames.push_back(std::move(f)); } print_spinner(); // Encoder configured using same parameters as video encoder enc_image, enc_audio; std::vector<uint8_t> buf_image, buf_audio; if (has_video) { enc_image = encoder([&] { encoder::args args; args.args_codec.codec = image_codec; args.args_image.h = height; args.args_image.w = width; args.args_image.framerate = fps; args.args_image.fmt = AV_PIX_FMT_YUV420P; return args; }()); DLIB_TEST(enc_image.is_open()); DLIB_TEST(enc_image.is_image_encoder()); DLIB_TEST(enc_image.get_codec_id() == image_codec); DLIB_TEST(enc_image.height() == height); DLIB_TEST(enc_image.width() == width); // Can't check for framerate, as this might have been changed from requested value, due to codec availability. print_spinner(); } if (has_audio) { enc_audio = encoder([&] { encoder::args args; args.args_codec.codec = audio_codec; args.args_audio.sample_rate = rate; args.args_audio.channel_layout = layout; args.args_audio.fmt = samplefmt; return args; }()); DLIB_TEST(enc_audio.is_open()); DLIB_TEST(enc_audio.is_audio_encoder()); DLIB_TEST(enc_audio.get_codec_id() == audio_codec); print_spinner(); } int iteration{0}; for (auto& f : frames) { if (f.is_image()) DLIB_TEST(enc_image.push(std::move(f), sink(buf_image))); if (f.is_audio()) DLIB_TEST(enc_audio.push(std::move(f), sink(buf_audio))); if ((iteration++ % 10) == 0) print_spinner(); } enc_image.flush(sink(buf_image)); enc_audio.flush(sink(buf_audio)); print_spinner(); // Decode everything back decoder dec_image, dec_audio; std::queue<frame> queue; if (has_video) { dec_image = decoder([&] { decoder::args args; args.codec = image_codec; return args; }()); DLIB_TEST(dec_image.is_open()); DLIB_TEST(dec_image.is_image_decoder()); DLIB_TEST(dec_image.get_codec_id() == image_codec); DLIB_TEST(dec_image.push(buf_image.data(), buf_image.size(), wrap(queue))); dec_image.flush(wrap(queue)); int images = 0; while (!queue.empty()) { auto f = std::move(queue.front()); queue.pop(); ++images; DLIB_TEST(f.height() == height); DLIB_TEST(f.width() == width); DLIB_TEST(dec_image.height() == height); DLIB_TEST(dec_image.width() == width); print_spinner(); } DLIB_TEST(images == nimages); } if (has_audio) { dec_audio = decoder([&] { decoder::args args; args.codec = audio_codec; return args; }()); DLIB_TEST(dec_audio.is_open()); DLIB_TEST(dec_audio.is_audio_decoder()); DLIB_TEST(dec_audio.get_codec_id() == audio_codec); DLIB_TEST(dec_audio.push(buf_audio.data(), buf_audio.size(), wrap(queue, {}, {rate}))); dec_audio.flush(wrap(queue, {}, {rate})); int samples = 0; while (!queue.empty()) { auto f = std::move(queue.front()); queue.pop(); samples += f.nsamples(); DLIB_TEST(f.sample_rate() == rate); print_spinner(); } DLIB_TEST(samples > (nsamples - rate)); DLIB_TEST(samples < (nsamples + rate)); } } ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// // MUXER ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// template<class image_type> void test_muxer1 ( const std::string& filepath, AVCodecID image_codec ) { const std::string tmpfile = "dummy.avi"; // Load a video/audio as a source of frames demuxer cap({filepath, video_enabled, audio_disabled}); DLIB_TEST(cap.is_open()); DLIB_TEST(cap.video_enabled()); DLIB_TEST(!cap.audio_enabled()); const int height = cap.height(); const int width = cap.width(); // Open muxer muxer writer([&] { muxer::args args; args.filepath = tmpfile; args.enable_audio = false; args.args_image.codec = image_codec; args.args_image.h = cap.height(); args.args_image.w = cap.width(); args.args_image.framerate = cap.fps(); args.args_image.fmt = AV_PIX_FMT_YUV420P; return args; }()); DLIB_TEST(writer.is_open()); DLIB_TEST(!writer.audio_enabled()); DLIB_TEST(writer.video_enabled()); DLIB_TEST(writer.get_video_codec_id() == image_codec); DLIB_TEST(writer.height() == cap.height()); DLIB_TEST(writer.width() == cap.width()); // Demux then remux int nimages_demuxed{0}; image_type img; while (cap.read(img)) { ++nimages_demuxed; DLIB_TEST(img.nr() == height); DLIB_TEST(img.nc() == width); DLIB_TEST(writer.push(img)); if (nimages_demuxed % 10 == 0) print_spinner(); } writer.flush(); // Demux everything back demuxer cap2(tmpfile); DLIB_TEST(cap2.is_open()); DLIB_TEST(cap2.video_enabled()); DLIB_TEST(!cap2.audio_enabled()); DLIB_TEST(cap2.get_video_codec_id() == image_codec); DLIB_TEST(cap2.height() == height); DLIB_TEST(cap2.width() == width); int nimages_muxed{0}; while (cap2.read(img)) { ++nimages_muxed; if (nimages_muxed % 10 == 0) print_spinner(); } DLIB_TEST(nimages_muxed == nimages_demuxed); } void test_muxer2 ( const std::string& filepath, AVCodecID image_codec, AVCodecID audio_codec ) { const std::string tmpfile = "dummy.avi"; // Load a video/audio as a source of frames demuxer cap(filepath); DLIB_TEST(cap.is_open()); const bool has_video = cap.video_enabled(); const bool has_audio = cap.audio_enabled(); const int height = cap.height(); const int width = cap.width(); const auto pixfmt = cap.pixel_fmt(); const auto fps = cap.fps(); const int rate = cap.sample_rate(); const auto samplefmt = cap.sample_fmt(); const auto layout = cap.channel_layout(); std::vector<frame> frames; frame f; int nimages{0}; int nsamples{0}; while (cap.read(f)) { if (f.is_image()) ++nimages; if (f.is_audio()) nsamples += f.nsamples(); frames.push_back(std::move(f)); } print_spinner(); // Muxer configured using parameters in video { muxer writer([&] { muxer::args args; args.filepath = tmpfile; args.enable_image = has_video; args.enable_audio = has_audio; if (has_video) { args.args_image.codec = image_codec; args.args_image.h = height; args.args_image.w = width; args.args_image.framerate = fps; args.args_image.fmt = AV_PIX_FMT_YUV420P; } if (has_audio) { args.args_audio.codec = audio_codec; args.args_audio.sample_rate = rate; args.args_audio.channel_layout = layout; args.args_audio.fmt = samplefmt; } return args; }()); DLIB_TEST(writer.is_open()); DLIB_TEST(writer.audio_enabled() == has_audio); DLIB_TEST(writer.video_enabled() == has_video); if (has_video) { DLIB_TEST(writer.get_video_codec_id() == image_codec); DLIB_TEST(writer.height() == height); DLIB_TEST(writer.width() == width); } if (has_audio) { DLIB_TEST(writer.get_audio_codec_id() == audio_codec); //You can't guarantee that the requested sample rate or sample format are supported. //In which case, the object changes them to values that ARE supported. So we can't add //tests that check the sample rate is set to what we asked for. } for (auto& f : frames) writer.push(std::move(f)); // muxer.flush(); // You don't need to call this since muxer's destructor already does. } // Demux everything back demuxer cap2(tmpfile); DLIB_TEST(cap2.is_open()); DLIB_TEST(cap2.video_enabled() == has_video); DLIB_TEST(cap2.audio_enabled() == has_audio); if (has_video) DLIB_TEST(cap2.get_video_codec_id() == image_codec); if (has_audio) DLIB_TEST(cap2.get_audio_codec_id() == audio_codec); DLIB_TEST(cap2.height() == height); DLIB_TEST(cap2.width() == width); // Can't test for sample_rate since muxer may have changed it due to codec availability. int images{0}; int samples{0}; int iteration{0}; while (cap2.read(f, {}, {rate})) { if (f.is_image()) ++images; if (f.is_audio()) samples += f.nsamples(); ++iteration; if (iteration % 10 == 0) print_spinner(); } DLIB_TEST(images == nimages); DLIB_TEST(samples >= (nsamples - rate)); DLIB_TEST(samples <= (nsamples + rate)); } const auto codec_supported = [](const AVCodecID id) { return std::find_if(begin(list_codecs()), end(list_codecs()), [=](const auto& supported) { return supported.codec_id == id && supported.supports_encoding; }) != end(list_codecs()); }; class video_tester : public tester { public: video_tester ( ) : tester ("test_ffmpeg", "Runs tests on video IO.") {} void perform_test ( ) { for (int i = 0 ; i < 10 ; ++i) { test_frame<rgb_pixel>(); test_frame<bgr_pixel>(); test_frame<rgb_alpha_pixel>(); test_frame<bgr_alpha_pixel>(); if (codec_supported(AV_CODEC_ID_PNG)) { test_load_save_frame<rgb_pixel>("dummy.png"); test_load_save_frame<bgr_pixel>("dummy.png"); } if (codec_supported(AV_CODEC_ID_MJPEG)) { test_load_save_frame<rgb_pixel>("dummy.jpg"); test_load_save_frame<bgr_pixel>("dummy.jpg"); } if (codec_supported(AV_CODEC_ID_BMP)) { test_load_save_frame<rgb_pixel>("dummy.bmp"); test_load_save_frame<bgr_pixel>("dummy.bmp"); } if (codec_supported(AV_CODEC_ID_TIFF)) { test_load_save_frame<rgb_pixel>("dummy.tiff"); test_load_save_frame<bgr_pixel>("dummy.tiff"); } } dlib::file f(DLIB_FFMPEG_DATA); dlib::config_reader cfg(f.full_name()); { const auto& image_block = cfg.block("images"); std::vector<string> blocks; image_block.get_blocks(blocks); for (const auto& block : blocks) { const auto& sublock = image_block.block(block); const std::string filepath = get_parent_directory(f).full_name() + "/" + sublock["file"]; test_load_frame(filepath); } } { const auto& video_raw_block = cfg.block("decoding"); std::vector<string> blocks; video_raw_block.get_blocks(blocks); for (const auto& block : blocks) { const auto& sublock = video_raw_block.block(block); const std::string filepath = get_parent_directory(f).full_name() + "/" + sublock["file"]; const std::string codec = dlib::get_option(sublock, "codec", ""); const int nframes = dlib::get_option(sublock, "nframes", 0); const int height = dlib::get_option(sublock, "height", 0); const int width = dlib::get_option(sublock, "width", 0); const int sample_rate = dlib::get_option(sublock, "sample_rate", 0); const bool has_image = height > 0 && width > 0; const bool has_audio = sample_rate > 0; if (has_image) { test_decoder_images_only<array2d<rgb_pixel>>(filepath, codec, nframes, height, width); test_decoder_images_only<array2d<rgb_alpha_pixel>>(filepath, codec, nframes, height, width); test_decoder_images_only<matrix<bgr_pixel>>(filepath, codec, nframes, height, width); test_decoder_images_only<matrix<bgr_alpha_pixel>>(filepath, codec, nframes, height, width); } test_decoder_full<array2d<rgb_pixel>>(filepath, codec, nframes, height, width, sample_rate, has_image, has_audio); test_decoder_full<matrix<bgr_pixel>>(filepath, codec, nframes, height, width, sample_rate, has_image, has_audio); } } { const auto& video_file_block = cfg.block("demuxing"); std::vector<string> blocks; video_file_block.get_blocks(blocks); for (const auto& block : blocks) { const auto& sublock = video_file_block.block(block); const std::string filepath = get_parent_directory(f).full_name() + "/" + sublock["file"]; const std::string tmpfile = "dummy.avi"; const int nframes = dlib::get_option(sublock, "nframes", 0); const int height = dlib::get_option(sublock, "height", 0); const int width = dlib::get_option(sublock, "width", 0); const int sample_rate = dlib::get_option(sublock, "sample_rate", 0); const bool has_video = height > 0 && width > 0 && nframes > 0; const bool has_audio = sample_rate > 0; if (has_video) { test_demuxer_images_only<array2d<rgb_pixel>>(filepath, nframes, height, width); test_demuxer_images_only<matrix<bgr_pixel>>(filepath, nframes, height, width); } test_demuxer_full(filepath, nframes, height, width, sample_rate, has_video, has_audio); test_encoder(filepath, AV_CODEC_ID_MPEG4, AV_CODEC_ID_AC3); if (has_video) { test_muxer1<array2d<rgb_pixel>>(filepath, AV_CODEC_ID_MPEG4); test_muxer1<matrix<bgr_pixel>>(filepath, AV_CODEC_ID_MPEG4); } test_muxer2(filepath, AV_CODEC_ID_MPEG4, AV_CODEC_ID_AC3); } } } } a; } #endif