Revert "Refactor movie capture to use FFMPEG only"
parent
7b52f23c97
commit
f731579681
|
@ -23,7 +23,8 @@ set(CELESTIA_SOURCES
|
||||||
|
|
||||||
if(ENABLE_FFMPEG)
|
if(ENABLE_FFMPEG)
|
||||||
list(APPEND CELESTIA_SOURCES
|
list(APPEND CELESTIA_SOURCES
|
||||||
moviecapture.cpp
|
ffmpegcapture.cpp
|
||||||
|
ffmpegcapture.h
|
||||||
moviecapture.h
|
moviecapture.h
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -87,13 +87,6 @@ static const float MinimumFOV = degToRad(0.001f);
|
||||||
static float KeyRotationAccel = degToRad(120.0f);
|
static float KeyRotationAccel = degToRad(120.0f);
|
||||||
static float MouseRotationSensitivity = degToRad(1.0f);
|
static float MouseRotationSensitivity = degToRad(1.0f);
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
enum CodecID
|
|
||||||
{
|
|
||||||
CEL_CODEC_ID_FFVHUFF = AV_CODEC_ID_FFVHUFF,
|
|
||||||
CEL_CODEC_ID_H264 = AV_CODEC_ID_H264,
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void warning(string s)
|
static void warning(string s)
|
||||||
{
|
{
|
||||||
|
@ -176,10 +169,8 @@ CelestiaCore::CelestiaCore() :
|
||||||
|
|
||||||
CelestiaCore::~CelestiaCore()
|
CelestiaCore::~CelestiaCore()
|
||||||
{
|
{
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
if (movieCapture != nullptr)
|
if (movieCapture != nullptr)
|
||||||
recordEnd();
|
recordEnd();
|
||||||
#endif
|
|
||||||
|
|
||||||
delete timer;
|
delete timer;
|
||||||
delete renderer;
|
delete renderer;
|
||||||
|
@ -816,7 +807,6 @@ void CelestiaCore::keyDown(int key, int modifiers)
|
||||||
case Key_F7:
|
case Key_F7:
|
||||||
sim->setTargetSpeed(1.0_ly);
|
sim->setTargetSpeed(1.0_ly);
|
||||||
break;
|
break;
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
case Key_F11:
|
case Key_F11:
|
||||||
if (movieCapture != nullptr)
|
if (movieCapture != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -830,7 +820,6 @@ void CelestiaCore::keyDown(int key, int modifiers)
|
||||||
if (movieCapture != nullptr)
|
if (movieCapture != nullptr)
|
||||||
recordEnd();
|
recordEnd();
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
case Key_NumPad2:
|
case Key_NumPad2:
|
||||||
case Key_NumPad4:
|
case Key_NumPad4:
|
||||||
case Key_NumPad6:
|
case Key_NumPad6:
|
||||||
|
@ -1926,7 +1915,6 @@ void CelestiaCore::tick()
|
||||||
// The time step is normally driven by the system clock; however, when
|
// The time step is normally driven by the system clock; however, when
|
||||||
// recording a movie, we fix the time step the frame rate of the movie.
|
// recording a movie, we fix the time step the frame rate of the movie.
|
||||||
double dt = 0.0;
|
double dt = 0.0;
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
if (movieCapture != nullptr && recording)
|
if (movieCapture != nullptr && recording)
|
||||||
{
|
{
|
||||||
dt = 1.0 / movieCapture->getFrameRate();
|
dt = 1.0 / movieCapture->getFrameRate();
|
||||||
|
@ -1935,9 +1923,6 @@ void CelestiaCore::tick()
|
||||||
{
|
{
|
||||||
dt = sysTime - lastTime;
|
dt = sysTime - lastTime;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
dt = sysTime - lastTime;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Pause script execution
|
// Pause script execution
|
||||||
if (scriptState == ScriptPaused)
|
if (scriptState == ScriptPaused)
|
||||||
|
@ -2147,10 +2132,8 @@ void CelestiaCore::draw()
|
||||||
if (toggleAA)
|
if (toggleAA)
|
||||||
renderer->enableMSAA();
|
renderer->enableMSAA();
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
if (movieCapture != nullptr && recording)
|
if (movieCapture != nullptr && recording)
|
||||||
movieCapture->captureFrame();
|
movieCapture->captureFrame();
|
||||||
#endif
|
|
||||||
|
|
||||||
// Frame rate counter
|
// Frame rate counter
|
||||||
nFrames++;
|
nFrames++;
|
||||||
|
@ -3523,7 +3506,6 @@ void CelestiaCore::renderOverlay()
|
||||||
overlay->setFont(font);
|
overlay->setFont(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
if (movieCapture != nullptr)
|
if (movieCapture != nullptr)
|
||||||
{
|
{
|
||||||
int movieWidth = movieCapture->getWidth();
|
int movieWidth = movieCapture->getWidth();
|
||||||
|
@ -3571,7 +3553,6 @@ void CelestiaCore::renderOverlay()
|
||||||
|
|
||||||
overlay->restorePos();
|
overlay->restorePos();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (editMode)
|
if (editMode)
|
||||||
{
|
{
|
||||||
|
@ -4417,33 +4398,25 @@ void CelestiaCore::setOverlayElements(int _overlayElements)
|
||||||
overlayElements = _overlayElements;
|
overlayElements = _overlayElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
void CelestiaCore::initMovieCapture(MovieCapture* mc)
|
||||||
bool CelestiaCore::initMovieCapture(const fs::path &path, int width, int height,
|
|
||||||
float fps, int64_t bitrate, int codec)
|
|
||||||
{
|
{
|
||||||
if (movieCapture != nullptr)
|
if (movieCapture == nullptr)
|
||||||
return false;
|
movieCapture = mc;
|
||||||
|
|
||||||
movieCapture = make_unique<celestia::MovieCapture>(getRenderer());
|
|
||||||
movieCapture->setVideoCodec(static_cast<AVCodecID>(codec));
|
|
||||||
movieCapture->setBitRate(bitrate);
|
|
||||||
if (codec == CEL_CODEC_ID_H264)
|
|
||||||
movieCapture->setEncoderOptions(getConfig()->x264EncoderOptions);
|
|
||||||
else
|
|
||||||
movieCapture->setEncoderOptions(getConfig()->ffvhEncoderOptions);
|
|
||||||
|
|
||||||
return movieCapture->start(path, width, height, fps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CelestiaCore::recordBegin()
|
void CelestiaCore::recordBegin()
|
||||||
{
|
{
|
||||||
if (movieCapture != nullptr)
|
if (movieCapture != nullptr)
|
||||||
|
{
|
||||||
recording = true;
|
recording = true;
|
||||||
|
movieCapture->recordingStatus(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CelestiaCore::recordPause()
|
void CelestiaCore::recordPause()
|
||||||
{
|
{
|
||||||
recording = false;
|
recording = false;
|
||||||
|
if (movieCapture != nullptr) movieCapture->recordingStatus(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CelestiaCore::recordEnd()
|
void CelestiaCore::recordEnd()
|
||||||
|
@ -4452,6 +4425,7 @@ void CelestiaCore::recordEnd()
|
||||||
{
|
{
|
||||||
recordPause();
|
recordPause();
|
||||||
movieCapture->end();
|
movieCapture->end();
|
||||||
|
delete movieCapture;
|
||||||
movieCapture = nullptr;
|
movieCapture = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4465,7 +4439,6 @@ bool CelestiaCore::isRecording()
|
||||||
{
|
{
|
||||||
return recording;
|
return recording;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void CelestiaCore::flash(const string& s, double duration)
|
void CelestiaCore::flash(const string& s, double duration)
|
||||||
{
|
{
|
||||||
|
@ -4779,43 +4752,3 @@ void CelestiaCore::setLogFile(const fs::path &fn)
|
||||||
fmt::fprintf(cerr, "Unable to open log file %s\n", fn.string());
|
fmt::fprintf(cerr, "Unable to open log file %s\n", fn.string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
auto CelestiaCore::getSupportedMovieSizes() const
|
|
||||||
-> celestia::util::array_view<MovieSize>
|
|
||||||
{
|
|
||||||
static std::array<MovieSize, 8> MovieSizes =
|
|
||||||
{{
|
|
||||||
{ 320, 240 },
|
|
||||||
{ 640, 480 },
|
|
||||||
{ 720, 480 },
|
|
||||||
{ 720, 576 },
|
|
||||||
{ 1024, 768 },
|
|
||||||
{ 1280, 720 },
|
|
||||||
{ 1920, 1080 },
|
|
||||||
{ 3840, 2160 }
|
|
||||||
}};
|
|
||||||
return MovieSizes;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CelestiaCore::getSupportedMovieFramerates() const
|
|
||||||
-> celestia::util::array_view<float>
|
|
||||||
{
|
|
||||||
static std::array<float, 5> MovieFramerates =
|
|
||||||
{
|
|
||||||
15.0f, 24.0f, 25.0f, 29.97f, 30.0f
|
|
||||||
};
|
|
||||||
return MovieFramerates;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CelestiaCore::getSupportedMovieCodecs() const
|
|
||||||
-> celestia::util::array_view<MovieCodec>
|
|
||||||
{
|
|
||||||
static std::array<MovieCodec, 2> MovieCodecs =
|
|
||||||
{{
|
|
||||||
{ CEL_CODEC_ID_FFVHUFF, N_("Lossless") },
|
|
||||||
{ CEL_CODEC_ID_H264, N_("Lossy (H.264)") }
|
|
||||||
}};
|
|
||||||
return MovieCodecs;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
|
||||||
#include <celutil/filetype.h>
|
#include <celutil/filetype.h>
|
||||||
#include <celutil/timer.h>
|
#include <celutil/timer.h>
|
||||||
#include <celutil/watcher.h>
|
#include <celutil/watcher.h>
|
||||||
|
@ -29,9 +28,7 @@
|
||||||
#include "configfile.h"
|
#include "configfile.h"
|
||||||
#include "favorites.h"
|
#include "favorites.h"
|
||||||
#include "destination.h"
|
#include "destination.h"
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
#include "moviecapture.h"
|
#include "moviecapture.h"
|
||||||
#endif
|
|
||||||
#include "view.h"
|
#include "view.h"
|
||||||
#ifdef CELX
|
#ifdef CELX
|
||||||
#include <celscript/lua/celx.h>
|
#include <celscript/lua/celx.h>
|
||||||
|
@ -58,18 +55,6 @@ public:
|
||||||
virtual void update(const std::string&) = 0;
|
virtual void update(const std::string&) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MovieSize
|
|
||||||
{
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MovieCodec
|
|
||||||
{
|
|
||||||
int codecId;
|
|
||||||
const char *codecDescr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CelestiaCore // : public Watchable<CelestiaCore>
|
class CelestiaCore // : public Watchable<CelestiaCore>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -259,17 +244,12 @@ class CelestiaCore // : public Watchable<CelestiaCore>
|
||||||
void setTextEnterMode(int);
|
void setTextEnterMode(int);
|
||||||
int getTextEnterMode() const;
|
int getTextEnterMode() const;
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
void initMovieCapture(MovieCapture*);
|
||||||
bool initMovieCapture(const fs::path &path, int width, int height, float fps, int64_t bitrate, int codec);
|
|
||||||
void recordBegin();
|
void recordBegin();
|
||||||
void recordPause();
|
void recordPause();
|
||||||
void recordEnd();
|
void recordEnd();
|
||||||
bool isCaptureActive();
|
bool isCaptureActive();
|
||||||
bool isRecording();
|
bool isRecording();
|
||||||
celestia::util::array_view<MovieSize> getSupportedMovieSizes() const;
|
|
||||||
celestia::util::array_view<float> getSupportedMovieFramerates() const;
|
|
||||||
celestia::util::array_view<MovieCodec> getSupportedMovieCodecs() const;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void runScript(const fs::path& filename, bool i18n = true);
|
void runScript(const fs::path& filename, bool i18n = true);
|
||||||
void cancelScript();
|
void cancelScript();
|
||||||
|
@ -491,10 +471,8 @@ class CelestiaCore // : public Watchable<CelestiaCore>
|
||||||
bool shiftKeysPressed[KeyCount];
|
bool shiftKeysPressed[KeyCount];
|
||||||
double KeyAccel{ 1.0 };
|
double KeyAccel{ 1.0 };
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
MovieCapture* movieCapture{ nullptr };
|
||||||
std::unique_ptr<celestia::MovieCapture> movieCapture;
|
|
||||||
bool recording{ false };
|
bool recording{ false };
|
||||||
#endif
|
|
||||||
|
|
||||||
Alerter* alerter{ nullptr };
|
Alerter* alerter{ nullptr };
|
||||||
std::vector<CelestiaWatcher*> watchers;
|
std::vector<CelestiaWatcher*> watchers;
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
#define AVCODEC_DEBUG 0
|
#define AVCODEC_DEBUG 0
|
||||||
|
|
||||||
#include <iostream>
|
#include "ffmpegcapture.h"
|
||||||
#include <vector>
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
#define __STDC_CONSTANT_MACROS
|
#define __STDC_CONSTANT_MACROS
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
#include <libavcodec/avcodec.h>
|
|
||||||
#include <libavutil/timestamp.h>
|
#include <libavutil/timestamp.h>
|
||||||
#include <libavutil/pixdesc.h>
|
#include <libavutil/pixdesc.h>
|
||||||
#include <libavutil/opt.h>
|
#include <libavutil/opt.h>
|
||||||
|
@ -15,18 +12,17 @@ extern "C"
|
||||||
#include <libswscale/swscale.h>
|
#include <libswscale/swscale.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <celengine/render.h>
|
#include <iostream>
|
||||||
#include "moviecapture.h"
|
#include <vector>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace celestia
|
|
||||||
{
|
|
||||||
// a wrapper around a single output AVStream
|
// a wrapper around a single output AVStream
|
||||||
class MovieCapturePrivate
|
class FFMPEGCapturePrivate
|
||||||
{
|
{
|
||||||
MovieCapturePrivate() = default;
|
FFMPEGCapturePrivate() = default;
|
||||||
~MovieCapturePrivate();
|
~FFMPEGCapturePrivate();
|
||||||
|
|
||||||
bool init(const fs::path& fn);
|
bool init(const fs::path& fn);
|
||||||
bool addStream(int w, int h, float fps);
|
bool addStream(int w, int h, float fps);
|
||||||
|
@ -45,7 +41,7 @@ class MovieCapturePrivate
|
||||||
AVFrame *tmpfr { nullptr };
|
AVFrame *tmpfr { nullptr };
|
||||||
AVCodecContext *enc { nullptr };
|
AVCodecContext *enc { nullptr };
|
||||||
AVFormatContext *oc { nullptr };
|
AVFormatContext *oc { nullptr };
|
||||||
const AVCodec *vc { nullptr };
|
AVCodec *vc { nullptr };
|
||||||
AVPacket *pkt { nullptr };
|
AVPacket *pkt { nullptr };
|
||||||
SwsContext *swsc { nullptr };
|
SwsContext *swsc { nullptr };
|
||||||
|
|
||||||
|
@ -54,7 +50,7 @@ class MovieCapturePrivate
|
||||||
// pts of the next frame that will be generated
|
// pts of the next frame that will be generated
|
||||||
int64_t nextPts { 0 };
|
int64_t nextPts { 0 };
|
||||||
// requested bitrate
|
// requested bitrate
|
||||||
int64_t bitrate { 400000 };
|
int64_t bit_rate { 400000 };
|
||||||
|
|
||||||
AVCodecID vc_id { AV_CODEC_ID_FFVHUFF };
|
AVCodecID vc_id { AV_CODEC_ID_FFVHUFF };
|
||||||
AVPixelFormat format { AV_PIX_FMT_NONE };
|
AVPixelFormat format { AV_PIX_FMT_NONE };
|
||||||
|
@ -69,22 +65,22 @@ class MovieCapturePrivate
|
||||||
#if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)) // ffmpeg < 4.0
|
#if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)) // ffmpeg < 4.0
|
||||||
static bool registered;
|
static bool registered;
|
||||||
#endif
|
#endif
|
||||||
friend class MovieCapture;
|
friend class FFMPEGCapture;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)) // ffmpeg < 4.0
|
#if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)) // ffmpeg < 4.0
|
||||||
bool MovieCapturePrivate::registered = false;
|
bool FFMPEGCapturePrivate::registered = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool MovieCapturePrivate::init(const fs::path& filename)
|
bool FFMPEGCapturePrivate::init(const fs::path& filename)
|
||||||
{
|
{
|
||||||
this->filename = filename;
|
this->filename = filename;
|
||||||
|
|
||||||
#if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)) // ffmpeg < 4.0
|
#if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)) // ffmpeg < 4.0
|
||||||
if (!MovieCapturePrivate::registered)
|
if (!FFMPEGCapturePrivate::registered)
|
||||||
{
|
{
|
||||||
av_register_all();
|
av_register_all();
|
||||||
MovieCapturePrivate::registered = true;
|
FFMPEGCapturePrivate::registered = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -96,7 +92,7 @@ bool MovieCapturePrivate::init(const fs::path& filename)
|
||||||
return oc != nullptr;
|
return oc != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MovieCapturePrivate::isSupportedPixelFormat(enum AVPixelFormat format) const
|
bool FFMPEGCapturePrivate::isSupportedPixelFormat(enum AVPixelFormat format) const
|
||||||
{
|
{
|
||||||
const enum AVPixelFormat *p = vc->pix_fmts;
|
const enum AVPixelFormat *p = vc->pix_fmts;
|
||||||
if (p == nullptr)
|
if (p == nullptr)
|
||||||
|
@ -193,7 +189,7 @@ static void listEncoderParameters(const AVCodec *vc)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int MovieCapturePrivate::writePacket()
|
int FFMPEGCapturePrivate::writePacket()
|
||||||
{
|
{
|
||||||
// rescale output packet timestamp values from codec to stream timebase
|
// rescale output packet timestamp values from codec to stream timebase
|
||||||
av_packet_rescale_ts(pkt, enc->time_base, st->time_base);
|
av_packet_rescale_ts(pkt, enc->time_base, st->time_base);
|
||||||
|
@ -204,7 +200,7 @@ int MovieCapturePrivate::writePacket()
|
||||||
}
|
}
|
||||||
|
|
||||||
// add an output stream
|
// add an output stream
|
||||||
bool MovieCapturePrivate::addStream(int width, int height, float fps)
|
bool FFMPEGCapturePrivate::addStream(int width, int height, float fps)
|
||||||
{
|
{
|
||||||
this->fps = fps;
|
this->fps = fps;
|
||||||
|
|
||||||
|
@ -235,12 +231,13 @@ bool MovieCapturePrivate::addStream(int width, int height, float fps)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
enc->codec_id = vc_id;
|
enc->codec_id = oc->oformat->video_codec = vc_id;
|
||||||
enc->bit_rate = bitrate;
|
|
||||||
|
enc->bit_rate = bit_rate;
|
||||||
#if 0
|
#if 0
|
||||||
enc->rc_min_rate = ...;
|
enc->rc_min_rate = ...;
|
||||||
enc->rc_max_rate = ...;
|
enc->rc_max_rate = ...;
|
||||||
enc->bitrate_tolerance = 0;
|
enc->bit_rate_tolerance = 0;
|
||||||
#endif
|
#endif
|
||||||
// Resolution must be a multiple of two
|
// Resolution must be a multiple of two
|
||||||
enc->width = width;
|
enc->width = width;
|
||||||
|
@ -290,7 +287,7 @@ bool MovieCapturePrivate::addStream(int width, int height, float fps)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MovieCapturePrivate::start()
|
bool FFMPEGCapturePrivate::start()
|
||||||
{
|
{
|
||||||
// open the output file, if needed
|
// open the output file, if needed
|
||||||
if ((oc->oformat->flags & AVFMT_NOFILE) == 0)
|
if ((oc->oformat->flags & AVFMT_NOFILE) == 0)
|
||||||
|
@ -320,7 +317,7 @@ bool MovieCapturePrivate::start()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MovieCapturePrivate::openVideo()
|
bool FFMPEGCapturePrivate::openVideo()
|
||||||
{
|
{
|
||||||
AVDictionary *opts = nullptr;
|
AVDictionary *opts = nullptr;
|
||||||
const char *str = "";
|
const char *str = "";
|
||||||
|
@ -419,7 +416,7 @@ static void captureImage(AVFrame *pict, int width, int height, const Renderer *r
|
||||||
|
|
||||||
// encode one video frame and send it to the muxer
|
// encode one video frame and send it to the muxer
|
||||||
// return 1 when encoding is finished, 0 otherwise
|
// return 1 when encoding is finished, 0 otherwise
|
||||||
bool MovieCapturePrivate::writeVideoFrame(bool finalize)
|
bool FFMPEGCapturePrivate::writeVideoFrame(bool finalize)
|
||||||
{
|
{
|
||||||
AVFrame *frame = finalize ? nullptr : this->frame;
|
AVFrame *frame = finalize ? nullptr : this->frame;
|
||||||
const int bytesPerPixel = hasAlpha ? 4 : 3;
|
const int bytesPerPixel = hasAlpha ? 4 : 3;
|
||||||
|
@ -451,9 +448,7 @@ bool MovieCapturePrivate::writeVideoFrame(bool finalize)
|
||||||
frame->pts = nextPts++;
|
frame->pts = nextPts++;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 133, 100))
|
|
||||||
av_init_packet(pkt);
|
av_init_packet(pkt);
|
||||||
#endif
|
|
||||||
|
|
||||||
// encode the image
|
// encode the image
|
||||||
if (avcodec_send_frame(enc, frame) < 0)
|
if (avcodec_send_frame(enc, frame) < 0)
|
||||||
|
@ -485,7 +480,7 @@ bool MovieCapturePrivate::writeVideoFrame(bool finalize)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MovieCapturePrivate::finish()
|
void FFMPEGCapturePrivate::finish()
|
||||||
{
|
{
|
||||||
writeVideoFrame(true);
|
writeVideoFrame(true);
|
||||||
|
|
||||||
|
@ -499,7 +494,7 @@ void MovieCapturePrivate::finish()
|
||||||
avio_closep(&oc->pb);
|
avio_closep(&oc->pb);
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieCapturePrivate::~MovieCapturePrivate()
|
FFMPEGCapturePrivate::~FFMPEGCapturePrivate()
|
||||||
{
|
{
|
||||||
avcodec_free_context(&enc);
|
avcodec_free_context(&enc);
|
||||||
av_frame_free(&frame);
|
av_frame_free(&frame);
|
||||||
|
@ -509,40 +504,41 @@ MovieCapturePrivate::~MovieCapturePrivate()
|
||||||
av_packet_free(&pkt);
|
av_packet_free(&pkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieCapture::MovieCapture(const Renderer *r) :
|
FFMPEGCapture::FFMPEGCapture(const Renderer *r) :
|
||||||
d(new MovieCapturePrivate)
|
MovieCapture(r),
|
||||||
|
d(new FFMPEGCapturePrivate)
|
||||||
{
|
{
|
||||||
d->renderer = r;
|
d->renderer = r;
|
||||||
d->hasAlpha = r->getPreferredCaptureFormat() == PixelFormat::RGBA;
|
d->hasAlpha = r->getPreferredCaptureFormat() == PixelFormat::RGBA;
|
||||||
d->format = d->hasAlpha ? AV_PIX_FMT_RGBA : AV_PIX_FMT_RGB24;
|
d->format = d->hasAlpha ? AV_PIX_FMT_RGBA : AV_PIX_FMT_RGB24;
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieCapture::~MovieCapture()
|
FFMPEGCapture::~FFMPEGCapture()
|
||||||
{
|
{
|
||||||
delete d;
|
delete d;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MovieCapture::getFrameCount() const
|
int FFMPEGCapture::getFrameCount() const
|
||||||
{
|
{
|
||||||
return d->nextPts;
|
return d->nextPts;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MovieCapture::getWidth() const
|
int FFMPEGCapture::getWidth() const
|
||||||
{
|
{
|
||||||
return d->enc->width;
|
return d->enc->width;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MovieCapture::getHeight() const
|
int FFMPEGCapture::getHeight() const
|
||||||
{
|
{
|
||||||
return d->enc->height;
|
return d->enc->height;
|
||||||
}
|
}
|
||||||
|
|
||||||
float MovieCapture::getFrameRate() const
|
float FFMPEGCapture::getFrameRate() const
|
||||||
{
|
{
|
||||||
return d->fps;
|
return d->fps;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MovieCapture::start(const fs::path& filename, int width, int height, float fps)
|
bool FFMPEGCapture::start(const fs::path& filename, int width, int height, float fps)
|
||||||
{
|
{
|
||||||
if (!d->init(filename) ||
|
if (!d->init(filename) ||
|
||||||
!d->addStream(width, height, fps) ||
|
!d->addStream(width, height, fps) ||
|
||||||
|
@ -557,7 +553,7 @@ bool MovieCapture::start(const fs::path& filename, int width, int height, float
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MovieCapture::end()
|
bool FFMPEGCapture::end()
|
||||||
{
|
{
|
||||||
if (!d->capturing)
|
if (!d->capturing)
|
||||||
return false;
|
return false;
|
||||||
|
@ -569,23 +565,22 @@ bool MovieCapture::end()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MovieCapture::captureFrame()
|
bool FFMPEGCapture::captureFrame()
|
||||||
{
|
{
|
||||||
return d->capturing && d->writeVideoFrame();
|
return d->capturing && d->writeVideoFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MovieCapture::setVideoCodec(AVCodecID vc_id)
|
void FFMPEGCapture::setVideoCodec(AVCodecID vc_id)
|
||||||
{
|
{
|
||||||
d->vc_id = vc_id;
|
d->vc_id = vc_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MovieCapture::setBitRate(int64_t bitrate)
|
void FFMPEGCapture::setBitRate(int64_t bit_rate)
|
||||||
{
|
{
|
||||||
d->bitrate = bitrate;
|
d->bit_rate = bit_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MovieCapture::setEncoderOptions(const std::string &s)
|
void FFMPEGCapture::setEncoderOptions(const std::string &s)
|
||||||
{
|
{
|
||||||
d->vc_options = s;
|
d->vc_options = s;
|
||||||
}
|
}
|
||||||
}
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "moviecapture.h"
|
||||||
|
#define __STDC_CONSTANT_MACROS
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <celengine/hash.h>
|
||||||
|
|
||||||
|
class FFMPEGCapturePrivate;
|
||||||
|
|
||||||
|
class FFMPEGCapture : public MovieCapture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FFMPEGCapture(const Renderer *r);
|
||||||
|
~FFMPEGCapture() override;
|
||||||
|
|
||||||
|
bool start(const fs::path&, int, int, float) override;
|
||||||
|
bool end() override;
|
||||||
|
bool captureFrame() override;
|
||||||
|
|
||||||
|
int getFrameCount() const override;
|
||||||
|
int getWidth() const override;
|
||||||
|
int getHeight() const override;
|
||||||
|
float getFrameRate() const override;
|
||||||
|
|
||||||
|
void setAspectRatio(int, int) override {};
|
||||||
|
void setQuality(float) override {};
|
||||||
|
void recordingStatus(bool) override {};
|
||||||
|
|
||||||
|
void setVideoCodec(AVCodecID);
|
||||||
|
void setBitRate(int64_t);
|
||||||
|
void setEncoderOptions(const std::string&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FFMPEGCapturePrivate *d{ nullptr };
|
||||||
|
};
|
|
@ -30,6 +30,9 @@
|
||||||
#include <celcompat/charconv.h>
|
#include <celcompat/charconv.h>
|
||||||
#include <celutil/filetype.h>
|
#include <celutil/filetype.h>
|
||||||
#include <celutil/gettext.h>
|
#include <celutil/gettext.h>
|
||||||
|
#ifdef USE_FFMPEG
|
||||||
|
#include <celestia/ffmpegcapture.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "actions.h"
|
#include "actions.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
@ -53,14 +56,41 @@ using namespace std;
|
||||||
static void openScript(const char* filename, AppData* app);
|
static void openScript(const char* filename, AppData* app);
|
||||||
static void captureImage(const char* filename, AppData* app);
|
static void captureImage(const char* filename, AppData* app);
|
||||||
#ifdef USE_FFMPEG
|
#ifdef USE_FFMPEG
|
||||||
static void captureMovie(const char* filename, int w, int h, float fps,
|
static void captureMovie(const char* filename, const int resolution[], float fps,
|
||||||
int codec, int64_t bitrate, AppData* app);
|
AVCodecID codec, float bitrate, AppData* app);
|
||||||
#endif
|
#endif
|
||||||
static void textInfoDialog(const char *txt, const char *title, AppData* app);
|
static void textInfoDialog(const char *txt, const char *title, AppData* app);
|
||||||
static void setRenderFlag(AppData* a, uint64_t flag, gboolean state);
|
static void setRenderFlag(AppData* a, uint64_t flag, gboolean state);
|
||||||
static void setOrbitMask(AppData* a, int mask, gboolean state);
|
static void setOrbitMask(AppData* a, int mask, gboolean state);
|
||||||
static void setLabelMode(AppData* a, int mode, gboolean state);
|
static void setLabelMode(AppData* a, int mode, gboolean state);
|
||||||
|
|
||||||
|
#ifdef USE_FFMPEG
|
||||||
|
static const int MovieSizes[][2] =
|
||||||
|
{
|
||||||
|
{ 160, 120 },
|
||||||
|
{ 320, 240 },
|
||||||
|
{ 640, 480 },
|
||||||
|
{ 720, 480 },
|
||||||
|
{ 720, 576 },
|
||||||
|
{ 1024, 768 },
|
||||||
|
{ 1280, 720 },
|
||||||
|
{ 1920, 1080 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const float MovieFramerates[] = { 15.0f, 23.976f, 24.0f, 25.0f, 29.97f, 30.0f, 60.0f };
|
||||||
|
|
||||||
|
struct MovieCodec
|
||||||
|
{
|
||||||
|
AVCodecID codecId;
|
||||||
|
const char *codecDesc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static MovieCodec MovieCodecs[2] =
|
||||||
|
{
|
||||||
|
{ AV_CODEC_ID_FFVHUFF, N_("Lossless") },
|
||||||
|
{ AV_CODEC_ID_H264, N_("Lossy (H.264)") }
|
||||||
|
};
|
||||||
|
|
||||||
static void insert_text_event(GtkEditable *editable, const gchar *text, gint length, gint *position, gpointer data)
|
static void insert_text_event(GtkEditable *editable, const gchar *text, gint length, gint *position, gpointer data)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
|
@ -72,6 +102,7 @@ static void insert_text_event(GtkEditable *editable, const gchar *text, gint len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* File -> Copy URL */
|
/* File -> Copy URL */
|
||||||
void actionCopyURL(GtkAction*, AppData* app)
|
void actionCopyURL(GtkAction*, AppData* app)
|
||||||
|
@ -237,12 +268,10 @@ void actionCaptureMovie(GtkAction*, AppData* app)
|
||||||
gtk_box_pack_start(GTK_BOX(hbox), rlabel, TRUE, TRUE, 0);
|
gtk_box_pack_start(GTK_BOX(hbox), rlabel, TRUE, TRUE, 0);
|
||||||
|
|
||||||
GtkWidget* vscombo = gtk_combo_box_text_new();
|
GtkWidget* vscombo = gtk_combo_box_text_new();
|
||||||
auto movieSizes = app->core->getSupportedMovieSizes();
|
for (const auto& size : MovieSizes)
|
||||||
for (const auto& size : movieSizes)
|
|
||||||
{
|
{
|
||||||
auto s = fmt::format("{} x {}", size.width, size.height);
|
|
||||||
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(vscombo),
|
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(vscombo),
|
||||||
s.c_str());
|
fmt::format("{} x {}", size[0], size[1]).c_str());
|
||||||
}
|
}
|
||||||
gtk_combo_box_set_active(GTK_COMBO_BOX(vscombo), 0);
|
gtk_combo_box_set_active(GTK_COMBO_BOX(vscombo), 0);
|
||||||
gtk_box_pack_start(GTK_BOX(hbox), vscombo, FALSE, FALSE, 0);
|
gtk_box_pack_start(GTK_BOX(hbox), vscombo, FALSE, FALSE, 0);
|
||||||
|
@ -251,8 +280,7 @@ void actionCaptureMovie(GtkAction*, AppData* app)
|
||||||
gtk_box_pack_start(GTK_BOX(hbox), flabel, TRUE, TRUE, 0);
|
gtk_box_pack_start(GTK_BOX(hbox), flabel, TRUE, TRUE, 0);
|
||||||
|
|
||||||
GtkWidget* frcombo = gtk_combo_box_text_new();
|
GtkWidget* frcombo = gtk_combo_box_text_new();
|
||||||
auto movieFramerates = app->core->getSupportedMovieFramerates();
|
for (float i : MovieFramerates)
|
||||||
for (float i : movieFramerates)
|
|
||||||
{
|
{
|
||||||
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(frcombo),
|
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(frcombo),
|
||||||
fmt::format("{:.3f}", i).c_str());
|
fmt::format("{:.3f}", i).c_str());
|
||||||
|
@ -264,11 +292,10 @@ void actionCaptureMovie(GtkAction*, AppData* app)
|
||||||
gtk_box_pack_start(GTK_BOX(hbox), vclabel, TRUE, TRUE, 0);
|
gtk_box_pack_start(GTK_BOX(hbox), vclabel, TRUE, TRUE, 0);
|
||||||
|
|
||||||
GtkWidget* vccombo = gtk_combo_box_text_new();
|
GtkWidget* vccombo = gtk_combo_box_text_new();
|
||||||
auto movieCodecs = app->core->getSupportedMovieCodecs();
|
for (const auto &mcodec : MovieCodecs)
|
||||||
for (const auto &mcodec : movieCodecs)
|
|
||||||
{
|
{
|
||||||
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(vccombo),
|
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(vccombo),
|
||||||
mcodec.codecDescr);
|
mcodec.codecDesc);
|
||||||
}
|
}
|
||||||
gtk_combo_box_set_active(GTK_COMBO_BOX(vccombo), 0);
|
gtk_combo_box_set_active(GTK_COMBO_BOX(vccombo), 0);
|
||||||
gtk_box_pack_start(GTK_BOX(hbox), vccombo, FALSE, FALSE, 0);
|
gtk_box_pack_start(GTK_BOX(hbox), vccombo, FALSE, FALSE, 0);
|
||||||
|
@ -290,17 +317,17 @@ void actionCaptureMovie(GtkAction*, AppData* app)
|
||||||
int fridx = gtk_combo_box_get_active(GTK_COMBO_BOX(frcombo));
|
int fridx = gtk_combo_box_get_active(GTK_COMBO_BOX(frcombo));
|
||||||
int vcidx = gtk_combo_box_get_active(GTK_COMBO_BOX(vccombo));
|
int vcidx = gtk_combo_box_get_active(GTK_COMBO_BOX(vccombo));
|
||||||
const gchar *brtext = gtk_entry_get_text(GTK_ENTRY(brentry));
|
const gchar *brtext = gtk_entry_get_text(GTK_ENTRY(brentry));
|
||||||
const auto &dim = movieSizes[vsidx];
|
const int *resolution = MovieSizes[vsidx];
|
||||||
float fps = movieFramerates[fridx];
|
float fps = MovieFramerates[fridx];
|
||||||
int codec = movieCodecs[vcidx].codecId;
|
AVCodecID codec = MovieCodecs[vcidx].codecId;
|
||||||
int64_t bitrate = 400000;
|
float bitrate = 400000;
|
||||||
const gchar *last = &brtext[gtk_entry_get_text_length(GTK_ENTRY(brentry))];
|
const gchar *last = &brtext[gtk_entry_get_text_length(GTK_ENTRY(brentry))];
|
||||||
std::from_chars(brtext, last, bitrate);
|
std::from_chars(brtext, last, bitrate);
|
||||||
|
|
||||||
gtk_widget_destroy(fs);
|
gtk_widget_destroy(fs);
|
||||||
for (int i=0; i < 10 && gtk_events_pending ();i++)
|
for (int i=0; i < 10 && gtk_events_pending ();i++)
|
||||||
gtk_main_iteration ();
|
gtk_main_iteration ();
|
||||||
captureMovie(filename, dim.width, dim.height, fps, codec, bitrate, app);
|
captureMovie(filename, resolution, fps, codec, bitrate, app);
|
||||||
g_free(filename);
|
g_free(filename);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1085,17 +1112,26 @@ static void captureImage(const char* filename, AppData* app)
|
||||||
|
|
||||||
/* Image capturing helper called by actionCaptureImage() */
|
/* Image capturing helper called by actionCaptureImage() */
|
||||||
#ifdef USE_FFMPEG
|
#ifdef USE_FFMPEG
|
||||||
static void captureMovie(const char* filename,
|
static void captureMovie(const char* filename, const int resolution[], float fps,
|
||||||
int width, int height,
|
AVCodecID codec, float bitrate, AppData* app)
|
||||||
float fps, int codec,
|
|
||||||
int64_t bitrate, AppData* app)
|
|
||||||
{
|
{
|
||||||
bool ok = app->core->initMovieCapture(filename,
|
auto* movieCapture = new FFMPEGCapture(app->renderer);
|
||||||
width, height,
|
movieCapture->setVideoCodec(codec);
|
||||||
fps, bitrate,
|
movieCapture->setBitRate(bitrate);
|
||||||
codec);
|
if (codec == AV_CODEC_ID_H264)
|
||||||
if (!ok)
|
movieCapture->setEncoderOptions(app->core->getConfig()->x264EncoderOptions);
|
||||||
|
else
|
||||||
|
movieCapture->setEncoderOptions(app->core->getConfig()->ffvhEncoderOptions);
|
||||||
|
|
||||||
|
bool success = movieCapture->start(filename, resolution[0], resolution[1], fps);
|
||||||
|
if (success)
|
||||||
{
|
{
|
||||||
|
app->core->initMovieCapture(movieCapture);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete movieCapture;
|
||||||
|
|
||||||
GtkWidget* errBox = gtk_message_dialog_new(GTK_WINDOW(app->mainWindow),
|
GtkWidget* errBox = gtk_message_dialog_new(GTK_WINDOW(app->mainWindow),
|
||||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||||
GTK_MESSAGE_ERROR,
|
GTK_MESSAGE_ERROR,
|
||||||
|
|
|
@ -1,34 +1,44 @@
|
||||||
#pragma once
|
// moviecapture.h
|
||||||
|
//
|
||||||
|
// Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
|
||||||
#define __STDC_CONSTANT_MACROS
|
#ifndef _MOVIECAPTURE_H_
|
||||||
extern "C"
|
#define _MOVIECAPTURE_H_
|
||||||
{
|
|
||||||
#include <libavformat/avformat.h>
|
#include <string>
|
||||||
}
|
#include <vector>
|
||||||
|
#include <celengine/render.h>
|
||||||
|
#include <celcompat/filesystem.h>
|
||||||
|
|
||||||
namespace celestia
|
|
||||||
{
|
|
||||||
class MovieCapturePrivate;
|
|
||||||
class MovieCapture
|
class MovieCapture
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MovieCapture(const Renderer *r);
|
MovieCapture(const Renderer *r) : renderer(r) {};
|
||||||
~MovieCapture();
|
virtual ~MovieCapture() {};
|
||||||
|
|
||||||
bool start(const fs::path&, int, int, float);
|
virtual bool start(const fs::path& filename,
|
||||||
bool end();
|
int width, int height,
|
||||||
bool captureFrame();
|
float fps) = 0;
|
||||||
|
virtual bool end() = 0;
|
||||||
|
virtual bool captureFrame() = 0;
|
||||||
|
|
||||||
int getFrameCount() const;
|
virtual int getFrameCount() const = 0;
|
||||||
int getWidth() const;
|
virtual int getWidth() const = 0;
|
||||||
int getHeight() const;
|
virtual int getHeight() const = 0;
|
||||||
float getFrameRate() const;
|
virtual float getFrameRate() const = 0;
|
||||||
|
|
||||||
void setVideoCodec(AVCodecID);
|
virtual void setAspectRatio(int aspectNumerator, int aspectDenominator) = 0;
|
||||||
void setBitRate(int64_t);
|
virtual void setQuality(float) = 0;
|
||||||
void setEncoderOptions(const std::string&);
|
virtual void recordingStatus(bool started) = 0; /* to update UI recording status indicator */
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
MovieCapturePrivate *d{ nullptr };
|
const Renderer *renderer{ nullptr };
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
#endif // _MOVIECAPTURE_H_
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
// of the License, or (at your option) any later version.
|
// of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
|
||||||
|
//#include <ctime>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
@ -64,6 +65,10 @@
|
||||||
#include <celestia/url.h>
|
#include <celestia/url.h>
|
||||||
#include "qtbookmark.h"
|
#include "qtbookmark.h"
|
||||||
|
|
||||||
|
#ifdef USE_FFMPEG
|
||||||
|
#include "celestia/ffmpegcapture.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_DATA_DIR
|
#ifndef CONFIG_DATA_DIR
|
||||||
#define CONFIG_DATA_DIR "./"
|
#define CONFIG_DATA_DIR "./"
|
||||||
#endif
|
#endif
|
||||||
|
@ -84,6 +89,22 @@ static const int CELESTIA_MAIN_WINDOW_VERSION = 12;
|
||||||
static int fps_to_ms(int fps) { return fps > 0 ? 1000 / fps : 0; }
|
static int fps_to_ms(int fps) { return fps > 0 ? 1000 / fps : 0; }
|
||||||
static int ms_to_fps(int ms) { return ms > 0? 1000 / ms : 0; }
|
static int ms_to_fps(int ms) { return ms > 0? 1000 / ms : 0; }
|
||||||
|
|
||||||
|
#ifdef USE_FFMPEG
|
||||||
|
static const int videoSizes[][2] =
|
||||||
|
{
|
||||||
|
{ 160, 120 },
|
||||||
|
{ 320, 240 },
|
||||||
|
{ 640, 480 },
|
||||||
|
{ 720, 480 },
|
||||||
|
{ 720, 576 },
|
||||||
|
{ 1024, 768 },
|
||||||
|
{ 1280, 720 },
|
||||||
|
{ 1920, 1080 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const float videoFrameRates[] = { 15.0f, 23.976f, 24.0f, 25.0f, 29.97f, 30.0f, 60.0f };
|
||||||
|
#endif
|
||||||
|
|
||||||
// Progress notifier class receives update messages from CelestiaCore
|
// Progress notifier class receives update messages from CelestiaCore
|
||||||
// at startup. This simple implementation just forwards messages on
|
// at startup. This simple implementation just forwards messages on
|
||||||
// to the main Celestia window.
|
// to the main Celestia window.
|
||||||
|
@ -644,27 +665,20 @@ void CelestiaAppWindow::slotCaptureVideo()
|
||||||
QComboBox* resolutionCombo = new QComboBox(&videoInfoDialog);
|
QComboBox* resolutionCombo = new QComboBox(&videoInfoDialog);
|
||||||
layout->addWidget(new QLabel(_("Resolution:"), &videoInfoDialog), 0, 0);
|
layout->addWidget(new QLabel(_("Resolution:"), &videoInfoDialog), 0, 0);
|
||||||
layout->addWidget(resolutionCombo, 0, 1);
|
layout->addWidget(resolutionCombo, 0, 1);
|
||||||
auto videoSizes = m_appCore->getSupportedMovieSizes();
|
|
||||||
for (const auto& size : videoSizes)
|
for (const auto& size : videoSizes)
|
||||||
{
|
resolutionCombo->addItem(QString(_("%1 x %2")).arg(size[0]).arg(size[1]), QSize(size[0], size[1]));
|
||||||
int w = size.width;
|
|
||||||
int h = size.height;
|
|
||||||
resolutionCombo->addItem(QString(_("%1 x %2")).arg(w).arg(h), QSize(w, h));
|
|
||||||
}
|
|
||||||
|
|
||||||
QComboBox* frameRateCombo = new QComboBox(&videoInfoDialog);
|
QComboBox* frameRateCombo = new QComboBox(&videoInfoDialog);
|
||||||
layout->addWidget(new QLabel(_("Frame rate:"), &videoInfoDialog), 1, 0);
|
layout->addWidget(new QLabel(_("Frame rate:"), &videoInfoDialog), 1, 0);
|
||||||
layout->addWidget(frameRateCombo, 1, 1);
|
layout->addWidget(frameRateCombo, 1, 1);
|
||||||
auto videoFrameRates = m_appCore->getSupportedMovieFramerates();
|
|
||||||
for (float i : videoFrameRates)
|
for (float i : videoFrameRates)
|
||||||
frameRateCombo->addItem(QString::number(i), i);
|
frameRateCombo->addItem(QString::number(i), i);
|
||||||
|
|
||||||
QComboBox* codecCombo = new QComboBox(&videoInfoDialog);
|
QComboBox* codecCombo = new QComboBox(&videoInfoDialog);
|
||||||
layout->addWidget(new QLabel(_("Video codec:"), &videoInfoDialog), 2, 0);
|
layout->addWidget(new QLabel(_("Video codec:"), &videoInfoDialog), 2, 0);
|
||||||
layout->addWidget(codecCombo, 2, 1);
|
layout->addWidget(codecCombo, 2, 1);
|
||||||
auto videoCodecs = m_appCore->getSupportedMovieCodecs();
|
codecCombo->addItem(_("Lossless"), AV_CODEC_ID_FFVHUFF);
|
||||||
for (const auto &c : videoCodecs)
|
codecCombo->addItem(_("Lossy (H.264)"), AV_CODEC_ID_H264);
|
||||||
codecCombo->addItem(_(c.codecDescr), c.codecId);
|
|
||||||
|
|
||||||
QLineEdit* bitrateEdit = new QLineEdit("400000", &videoInfoDialog);
|
QLineEdit* bitrateEdit = new QLineEdit("400000", &videoInfoDialog);
|
||||||
bitrateEdit->setInputMask("D000000000");
|
bitrateEdit->setInputMask("D000000000");
|
||||||
|
@ -682,12 +696,24 @@ void CelestiaAppWindow::slotCaptureVideo()
|
||||||
{
|
{
|
||||||
QSize videoSize = resolutionCombo->itemData(resolutionCombo->currentIndex()).toSize();
|
QSize videoSize = resolutionCombo->itemData(resolutionCombo->currentIndex()).toSize();
|
||||||
float frameRate = frameRateCombo->itemData(frameRateCombo->currentIndex()).toFloat();
|
float frameRate = frameRateCombo->itemData(frameRateCombo->currentIndex()).toFloat();
|
||||||
int codec = codecCombo->itemData(codecCombo->currentIndex()).toInt();
|
AVCodecID vc = static_cast<AVCodecID>(codecCombo->itemData(codecCombo->currentIndex()).toInt());
|
||||||
int64_t bitrate = bitrateEdit->text().toLongLong();
|
int br = bitrateEdit->text().toLongLong();
|
||||||
|
|
||||||
m_appCore->initMovieCapture(saveAsName.toStdString(),
|
auto *movieCapture = new FFMPEGCapture(m_appCore->getRenderer());
|
||||||
videoSize.width(), videoSize.height(),
|
movieCapture->setVideoCodec(vc);
|
||||||
frameRate, bitrate, codec);
|
movieCapture->setBitRate(br);
|
||||||
|
if (vc == AV_CODEC_ID_H264)
|
||||||
|
movieCapture->setEncoderOptions(m_appCore->getConfig()->x264EncoderOptions);
|
||||||
|
else
|
||||||
|
movieCapture->setEncoderOptions(m_appCore->getConfig()->ffvhEncoderOptions);
|
||||||
|
|
||||||
|
bool ok = movieCapture->start(saveAsName.toStdString(),
|
||||||
|
videoSize.width(), videoSize.height(),
|
||||||
|
frameRate);
|
||||||
|
if (ok)
|
||||||
|
m_appCore->initMovieCapture(movieCapture);
|
||||||
|
else
|
||||||
|
delete movieCapture;
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.beginGroup("Preferences");
|
settings.beginGroup("Preferences");
|
||||||
|
|
|
@ -488,9 +488,9 @@ FONT 8, "Segoe UI", 0, 0, 0
|
||||||
COMBOBOX IDC_COMBO_MOVIE_SIZE, 38, 2, 82, 93, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT
|
COMBOBOX IDC_COMBO_MOVIE_SIZE, 38, 2, 82, 93, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT
|
||||||
RTEXT "Frame rate:", IDC_STATIC, 120, 4, 74, 8, SS_RIGHT, WS_EX_LEFT
|
RTEXT "Frame rate:", IDC_STATIC, 120, 4, 74, 8, SS_RIGHT, WS_EX_LEFT
|
||||||
COMBOBOX IDC_COMBO_MOVIE_FRAMERATE, 196, 2, 82, 90, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT
|
COMBOBOX IDC_COMBO_MOVIE_FRAMERATE, 196, 2, 82, 90, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT
|
||||||
LTEXT "Codec:", IDC_STATIC, 6, 20, 30, 8, SS_LEFT, WS_EX_LEFT
|
LTEXT "Codec:", 0, 6, 20, 30, 8, SS_LEFT, WS_EX_LEFT
|
||||||
COMBOBOX IDC_COMBO_MOVIE_CODEC, 38, 18, 82, 90, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT
|
COMBOBOX IDC_COMBO_MOVIE_CODEC, 38, 18, 82, 30, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT
|
||||||
RTEXT "Bitrate:", IDC_STATIC, 120, 20, 74, 8, SS_RIGHT, WS_EX_LEFT
|
RTEXT "Bitrate:", 0, 120, 20, 74, 8, SS_RIGHT, WS_EX_LEFT
|
||||||
EDITTEXT IDC_EDIT_MOVIE_BITRATE, 196, 18, 82, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT
|
EDITTEXT IDC_EDIT_MOVIE_BITRATE, 196, 18, 82, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,8 @@
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <windowsx.h> // GET_X_LPARAM, GET_Y_LPARAM
|
|
||||||
#include <commctrl.h>
|
#include <commctrl.h>
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
#include <oleidl.h> // IDropTarget
|
|
||||||
#include <commdlg.h>
|
#include <commdlg.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
|
||||||
|
@ -44,6 +42,7 @@
|
||||||
#include <celscript/legacy/cmdparser.h>
|
#include <celscript/legacy/cmdparser.h>
|
||||||
|
|
||||||
#include "celestia/celestiacore.h"
|
#include "celestia/celestiacore.h"
|
||||||
|
#include "celestia/ffmpegcapture.h"
|
||||||
#include "celestia/helper.h"
|
#include "celestia/helper.h"
|
||||||
#include "celestia/scriptmenu.h"
|
#include "celestia/scriptmenu.h"
|
||||||
#include "celestia/url.h"
|
#include "celestia/url.h"
|
||||||
|
@ -128,6 +127,31 @@ static POINT lastMouseMove;
|
||||||
class WinCursorHandler;
|
class WinCursorHandler;
|
||||||
WinCursorHandler* cursorHandler = NULL;
|
WinCursorHandler* cursorHandler = NULL;
|
||||||
|
|
||||||
|
static int MovieSizes[8][2] = {
|
||||||
|
{ 160, 120 },
|
||||||
|
{ 320, 240 },
|
||||||
|
{ 640, 480 },
|
||||||
|
{ 720, 480 },
|
||||||
|
{ 720, 576 },
|
||||||
|
{ 1024, 768 },
|
||||||
|
{ 1280, 720 },
|
||||||
|
{ 1920, 1080 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static float MovieFramerates[5] = { 15.0f, 24.0f, 25.0f, 29.97f, 30.0f };
|
||||||
|
|
||||||
|
struct MovieCodec
|
||||||
|
{
|
||||||
|
AVCodecID codecId;
|
||||||
|
const char *codecDesc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static MovieCodec MovieCodecs[2] =
|
||||||
|
{
|
||||||
|
{ AV_CODEC_ID_FFVHUFF, N_("Lossless") },
|
||||||
|
{ AV_CODEC_ID_H264, N_("Lossy (H.264)") }
|
||||||
|
};
|
||||||
|
|
||||||
static int movieSize = 1;
|
static int movieSize = 1;
|
||||||
static int movieFramerate = 1;
|
static int movieFramerate = 1;
|
||||||
static int movieCodec = 1;
|
static int movieCodec = 1;
|
||||||
|
@ -417,6 +441,31 @@ static void ShowLocalTime(CelestiaCore* appCore)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool BeginMovieCapture(const Renderer* renderer,
|
||||||
|
const std::string& filename,
|
||||||
|
int width, int height,
|
||||||
|
float framerate,
|
||||||
|
AVCodecID codec,
|
||||||
|
int64_t bitrate)
|
||||||
|
{
|
||||||
|
auto* movieCapture = new FFMPEGCapture(renderer);
|
||||||
|
movieCapture->setVideoCodec(codec);
|
||||||
|
movieCapture->setBitRate(bitrate);
|
||||||
|
if (vc == AV_CODEC_ID_H264)
|
||||||
|
movieCapture->setEncoderOptions(appCore->getConfig()->x264EncoderOptions);
|
||||||
|
else
|
||||||
|
movieCapture->setEncoderOptions(appCore->getConfig()->ffvhEncoderOptions);
|
||||||
|
|
||||||
|
bool success = movieCapture->start(filename, width, height, framerate);
|
||||||
|
if (success)
|
||||||
|
appCore->initMovieCapture(movieCapture);
|
||||||
|
else
|
||||||
|
delete movieCapture;
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool CopyStateURLToClipboard()
|
static bool CopyStateURLToClipboard()
|
||||||
{
|
{
|
||||||
BOOL b;
|
BOOL b;
|
||||||
|
@ -628,7 +677,6 @@ BOOL APIENTRY GLInfoProc(HWND hDlg,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
UINT CALLBACK ChooseMovieParamsProc(HWND hDlg, UINT message,
|
UINT CALLBACK ChooseMovieParamsProc(HWND hDlg, UINT message,
|
||||||
WPARAM wParam, LPARAM lParam)
|
WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
|
@ -638,32 +686,33 @@ UINT CALLBACK ChooseMovieParamsProc(HWND hDlg, UINT message,
|
||||||
{
|
{
|
||||||
char buf[100];
|
char buf[100];
|
||||||
HWND hwnd = GetDlgItem(hDlg, IDC_COMBO_MOVIE_SIZE);
|
HWND hwnd = GetDlgItem(hDlg, IDC_COMBO_MOVIE_SIZE);
|
||||||
|
int nSizes = sizeof MovieSizes / sizeof MovieSizes[0];
|
||||||
|
|
||||||
auto movieSizes = appCore->getSupportedMovieSizes();
|
for (int i = 0; i < nSizes; i++)
|
||||||
for (auto const &s : movieSizes)
|
|
||||||
{
|
{
|
||||||
sprintf(buf, _("%d x %d"), s.width, s.height);
|
sprintf(buf, _("%d x %d"), MovieSizes[i][0], MovieSizes[i][1]);
|
||||||
SendMessage(hwnd, CB_INSERTSTRING, -1,
|
SendMessage(hwnd, CB_INSERTSTRING, -1,
|
||||||
reinterpret_cast<LPARAM>(buf));
|
reinterpret_cast<LPARAM>(buf));
|
||||||
}
|
}
|
||||||
SendMessage(hwnd, CB_SETCURSEL, movieSize, 0);
|
SendMessage(hwnd, CB_SETCURSEL, movieSize, 0);
|
||||||
|
|
||||||
hwnd = GetDlgItem(hDlg, IDC_COMBO_MOVIE_FRAMERATE);
|
hwnd = GetDlgItem(hDlg, IDC_COMBO_MOVIE_FRAMERATE);
|
||||||
auto movieFramerates = appCore->getSupportedMovieFramerates();
|
int nFramerates = sizeof MovieFramerates / sizeof MovieFramerates[0];
|
||||||
for (float fps : movieFramerates)
|
for (int i = 0; i < nFramerates; i++)
|
||||||
{
|
{
|
||||||
sprintf(buf, "%.2f", fps);
|
sprintf(buf, "%.2f", MovieFramerates[i]);
|
||||||
SendMessage(hwnd, CB_INSERTSTRING, -1,
|
SendMessage(hwnd, CB_INSERTSTRING, -1,
|
||||||
reinterpret_cast<LPARAM>(buf));
|
reinterpret_cast<LPARAM>(buf));
|
||||||
}
|
}
|
||||||
SendMessage(hwnd, CB_SETCURSEL, movieFramerate, 0);
|
SendMessage(hwnd, CB_SETCURSEL, movieFramerate, 0);
|
||||||
|
|
||||||
hwnd = GetDlgItem(hDlg, IDC_COMBO_MOVIE_CODEC);
|
hwnd = GetDlgItem(hDlg, IDC_COMBO_MOVIE_CODEC);
|
||||||
auto movieCodecs = appCore->getSupportedMovieCodecs();
|
int nCodecs = sizeof MovieCodecs / sizeof MovieCodecs[0];
|
||||||
for (auto &c : movieCodecs)
|
for (int i = 0; i < nCodecs; i++)
|
||||||
{
|
{
|
||||||
SendMessage(hwnd, CB_INSERTSTRING, -1,
|
SendMessage(hwnd, CB_INSERTSTRING,
|
||||||
reinterpret_cast<LPARAM>(_(c.codecDescr)));
|
reinterpret_cast<WPARAM>(MovieCodecs[i].codecId),
|
||||||
|
reinterpret_cast<LPARAM>(_(MovieCodecs[i].codecDesc)));
|
||||||
}
|
}
|
||||||
SendMessage(hwnd, CB_SETCURSEL, movieCodec, 0);
|
SendMessage(hwnd, CB_SETCURSEL, movieCodec, 0);
|
||||||
|
|
||||||
|
@ -705,15 +754,14 @@ UINT CALLBACK ChooseMovieParamsProc(HWND hDlg, UINT message,
|
||||||
movieCodec = item;
|
movieCodec = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (LOWORD(wParam) == IDOK)
|
else if (LOWORD(wParam) == IDOK IDC_EDIT_MOVIE_BITRATE)
|
||||||
{
|
{
|
||||||
char buf[24], out[24];
|
char buf[24], out[24];
|
||||||
wchar_t wbuff[48];
|
wchar_t wbuff[48];
|
||||||
int wlen = 0;
|
|
||||||
int len = GetDlgItemText(hDlg, IDC_EDIT_MOVIE_BITRATE, buf, sizeof(buf));
|
int len = GetDlgItemText(hDlg, IDC_EDIT_MOVIE_BITRATE, buf, sizeof(buf));
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
{
|
{
|
||||||
wlen = MultiByteToWideChar(CP_ACP, 0, buf, -1, wbuff, sizeof(wbuff));
|
int wlen = MultiByteToWideChar(CP_ACP, 0, buf, -1, wbuff, sizeof(wbuff));
|
||||||
WideCharToMultiByte(CP_UTF8, 0, wbuff, wlen, out, sizeof(out), NULL, NULL);
|
WideCharToMultiByte(CP_UTF8, 0, wbuff, wlen, out, sizeof(out), NULL, NULL);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -728,7 +776,6 @@ UINT CALLBACK ChooseMovieParamsProc(HWND hDlg, UINT message,
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
BOOL APIENTRY FindObjectProc(HWND hDlg,
|
BOOL APIENTRY FindObjectProc(HWND hDlg,
|
||||||
|
@ -2691,7 +2738,6 @@ static void HandleCaptureImage(HWND hWnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
static void HandleCaptureMovie(HWND hWnd)
|
static void HandleCaptureMovie(HWND hWnd)
|
||||||
{
|
{
|
||||||
// TODO: The menu item should be disable so that the user doesn't even
|
// TODO: The menu item should be disable so that the user doesn't even
|
||||||
|
@ -2785,15 +2831,13 @@ static void HandleCaptureMovie(HWND hWnd)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto movieSizes = appCore->getSupportedMovieSizes();
|
success = BeginMovieCapture(appCore->getRenderer(),
|
||||||
auto movieFramerates = appCore->getSupportedMovieFramerates();
|
string(Ofn.lpstrFile),
|
||||||
auto movieCodecs = appCore->getSupportedMovieCodecs();
|
MovieSizes[movieSize][0],
|
||||||
success = appCore->initMovieCapture(Ofn.lpstrFile,
|
MovieSizes[movieSize][1],
|
||||||
movieSizes[movieSize].width,
|
MovieFramerates[movieFramerate],
|
||||||
movieSizes[movieSize].height,
|
MovieCodecs[movieCodec],
|
||||||
movieFramerates[movieFramerate],
|
movieBitrate);
|
||||||
movieBitrate,
|
|
||||||
movieCodecs[movieCodec].codecId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
|
@ -2809,7 +2853,6 @@ static void HandleCaptureMovie(HWND hWnd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static void HandleOpenScript(HWND hWnd, CelestiaCore* appCore)
|
static void HandleOpenScript(HWND hWnd, CelestiaCore* appCore)
|
||||||
|
@ -4268,11 +4311,9 @@ LRESULT CALLBACK MainWindowProc(HWND hWnd,
|
||||||
HandleCaptureImage(hWnd);
|
HandleCaptureImage(hWnd);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
case ID_FILE_CAPTUREMOVIE:
|
case ID_FILE_CAPTUREMOVIE:
|
||||||
HandleCaptureMovie(hWnd);
|
HandleCaptureMovie(hWnd);
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
|
|
||||||
case ID_FILE_EXIT:
|
case ID_FILE_EXIT:
|
||||||
SendMessage(hWnd, WM_CLOSE, 0, 0);
|
SendMessage(hWnd, WM_CLOSE, 0, 0);
|
||||||
|
|
Loading…
Reference in New Issue