dlib C++ Library - ffmpeg.cpp

#// 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

AltStyle によって変換されたページ (->オリジナル) /