Fix video/audio recording.
We create a namespace to deal with most of our recording solution. Besides that, we also add some functions to remove the need of including libavutil headers on other part of the code. This is meant to isolate most of recording solution components on the proper files. We will start with a limited number of codecs supported; slowly we should add them as they are tested (the previous one did not work for most codecs listed). This should support `ffmpeg 4.1` and further, including removing all compilation warnings related to versions discrepancy.
This commit is contained in:
parent
cfb03d8b3a
commit
5848feaea2
|
@ -280,7 +280,7 @@ set(
|
|||
if(ENABLE_FFMPEG)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
pkg_check_modules(FFMPEG REQUIRED libavcodec libavformat libswscale libavutil)
|
||||
pkg_check_modules(FFMPEG REQUIRED libavcodec libavformat libswscale libavutil libswresample)
|
||||
|
||||
if(FFMPEG_STATIC)
|
||||
set(FFMPEG_LIBRARIES ${FFMPEG_STATIC_LIBRARIES})
|
||||
|
@ -625,7 +625,7 @@ set(
|
|||
)
|
||||
|
||||
if(MSVC)
|
||||
set(SRC_MAIN ${SRC_MAIN} "dependencies/msvc/getopt.c")
|
||||
set(SRC_MAIN ${SRC_MAIN} "dependencies/msvc/getopt.c")
|
||||
endif()
|
||||
|
||||
set(
|
||||
|
@ -643,7 +643,7 @@ set(
|
|||
)
|
||||
|
||||
if(MSVC)
|
||||
set(HDR_MAIN ${HDR_MAIN} "dependencies/msvc/getopt.h")
|
||||
set(HDR_MAIN ${HDR_MAIN} "dependencies/msvc/getopt.h")
|
||||
endif()
|
||||
|
||||
if(ENABLE_FFMPEG)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,19 +3,36 @@
|
|||
|
||||
// simplified interface for recording audio and/or video from emulator
|
||||
|
||||
// unlike the rest of the wx code, this has no wx dependency at all, and
|
||||
// could be used by other front ends as well.
|
||||
// required for ffmpeg
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
|
||||
// this only supports selecting output format via file name extensions;
|
||||
// maybe some future version will support specifying a format. wx-2.9
|
||||
// has an extra widget for the file selector, but 2.8 doesn't.
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/avassert.h>
|
||||
#include <libavutil/channel_layout.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/mathematics.h>
|
||||
#include <libavutil/timestamp.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswresample/swresample.h>
|
||||
}
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace recording {
|
||||
|
||||
|
||||
// get supported audio/video codecs
|
||||
std::vector<char *> getSupVidNames();
|
||||
std::vector<char *> getSupVidExts();
|
||||
std::vector<char *> getSupAudNames();
|
||||
std::vector<char *> getSupAudExts();
|
||||
|
||||
// the only missing piece that I couldn't figure out how to do generically
|
||||
// is the code to find the available formats & associated extensions for
|
||||
// the file dialog.
|
||||
|
||||
// return codes
|
||||
// probably ought to put in own namespace, but this is good enough
|
||||
enum MediaRet {
|
||||
MRET_OK, // no errors
|
||||
MRET_ERR_NOMEM, // error allocating buffers or structures
|
||||
|
@ -40,41 +57,62 @@ class MediaRecorder
|
|||
void Stop();
|
||||
bool IsRecording()
|
||||
{
|
||||
return oc != NULL;
|
||||
return isRecording;
|
||||
}
|
||||
// add a frame of video; width+height+depth already given
|
||||
// assumes a 1-pixel border on top & right
|
||||
// always assumes being passed 1/60th of a second of video
|
||||
MediaRet AddFrame(const uint8_t *vid);
|
||||
// add a frame of audio; uses current sample rate to know length
|
||||
// always assumes being passed 1/60th of a second of audio.
|
||||
MediaRet AddFrame(const uint16_t *aud);
|
||||
// always assumes being passed 1/60th of a second of audio;
|
||||
// single sample, though (we need one for each channel).
|
||||
MediaRet AddFrame(const uint16_t *aud, int length);
|
||||
// set sampleRate; we need this to remove the GBA file header
|
||||
// include.
|
||||
void SetSampleRate(int newSampleRate)
|
||||
{
|
||||
sampleRate = newSampleRate;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool did_init;
|
||||
bool isRecording;
|
||||
int sampleRate;
|
||||
AVFormatContext *oc;
|
||||
AVOutputFormat *fmt;
|
||||
// pic info
|
||||
AVPixelFormat pixfmt;
|
||||
int pixsize, linesize;
|
||||
int tbord, rbord;
|
||||
struct SwsContext *sws;
|
||||
// stream info
|
||||
AVStream *st;
|
||||
AVCodec *vcodec;
|
||||
AVCodecContext *enc;
|
||||
int64_t npts; // for video frame pts
|
||||
AVFrame *frameIn;
|
||||
AVFrame *frameOut;
|
||||
// audio
|
||||
bool audioOnlyRecording;
|
||||
struct SwrContext *swr;
|
||||
AVCodec *acodec;
|
||||
AVStream *ast;
|
||||
AVCodecContext *aenc;
|
||||
int samplesCount; // for audio frame pts generation
|
||||
AVFrame *audioframe;
|
||||
AVFrame *audioframeTmp;
|
||||
// audio buffer
|
||||
uint16_t *audioBuffer;
|
||||
int posInAudioBuffer;
|
||||
int samplesInAudioBuffer;
|
||||
int audioBufferSize;
|
||||
|
||||
// these are to avoid polluting things with avcodec includes
|
||||
#ifndef priv_AVFormatContext
|
||||
#define priv_AVFormatContext void
|
||||
#define priv_AVStream void
|
||||
#define priv_AVOutputFormat void
|
||||
#define priv_AVFrame void
|
||||
#define priv_SwsContext void
|
||||
#define priv_PixelFormat int
|
||||
#endif
|
||||
priv_AVFormatContext *oc;
|
||||
priv_AVStream *vid_st, *aud_st;
|
||||
uint8_t *audio_buf, *video_buf;
|
||||
uint16_t *audio_buf2;
|
||||
int frame_len, sample_len, in_audio_buf2;
|
||||
int linesize, pixsize;
|
||||
priv_PixelFormat pixfmt;
|
||||
priv_AVFrame *pic, *convpic;
|
||||
priv_SwsContext *converter;
|
||||
|
||||
MediaRet setup_sound_stream(const char *fname, priv_AVOutputFormat *fmt);
|
||||
MediaRet setup_video_stream(const char *fname, int w, int h, int d);
|
||||
MediaRet setup_common(const char *fname);
|
||||
MediaRet setup_video_stream_info(int width, int height, int depth);
|
||||
MediaRet setup_video_stream(int width, int height);
|
||||
MediaRet setup_audio_stream();
|
||||
MediaRet finish_setup(const char *fname);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* WX_FFMPEG_H */
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
#ifndef NO_FFMPEG
|
||||
#define __STDC_LIMIT_MACROS // required for ffmpeg
|
||||
#define __STDC_CONSTANT_MACROS // required for ffmpeg
|
||||
#endif
|
||||
|
||||
#include "wxvbam.h"
|
||||
#include <algorithm>
|
||||
#include <wx/aboutdlg.h>
|
||||
|
@ -15,16 +10,6 @@
|
|||
#include <wx/wfstream.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#ifndef NO_FFMPEG
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
// For compatibility with 3.0+ ffmpeg
|
||||
#include <libavcodec/version.h>
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 56
|
||||
#define CODEC_ID_NONE AV_CODEC_ID_NONE
|
||||
#endif
|
||||
#endif
|
||||
#include "version.h"
|
||||
#include "../common/ConfigManager.h"
|
||||
#include "../gb/gbPrinter.h"
|
||||
|
@ -828,7 +813,7 @@ EVT_HANDLER_MASK(RomInformation, "ROM information...", CMDEN_GB | CMDEN_GBA)
|
|||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1179,23 +1164,20 @@ EVT_HANDLER_MASK(RecordSoundStartRecording, "Start sound recording...", CMDEN_NS
|
|||
|
||||
if (!sound_exts.size()) {
|
||||
sound_extno = -1;
|
||||
int extno;
|
||||
AVOutputFormat* fmt;
|
||||
int extno = 0;
|
||||
|
||||
for (fmt = NULL, extno = 0; (fmt = av_oformat_next(fmt));) {
|
||||
if (!fmt->extensions)
|
||||
continue;
|
||||
std::vector<char *> fmts = recording::getSupAudNames();
|
||||
std::vector<char *> exts = recording::getSupAudExts();
|
||||
|
||||
if (fmt->audio_codec == CODEC_ID_NONE)
|
||||
continue;
|
||||
|
||||
sound_exts.append(wxString(fmt->long_name ? fmt->long_name : fmt->name, wxConvLibc));
|
||||
for (size_t i = 0; i < fmts.size(); ++i)
|
||||
{
|
||||
sound_exts.append(wxString(fmts[i], wxConvLibc));
|
||||
sound_exts.append(_(" files ("));
|
||||
wxString ext(fmt->extensions, wxConvLibc);
|
||||
wxString ext(exts[i], wxConvLibc);
|
||||
ext.Replace(wxT(","), wxT(";*."));
|
||||
ext.insert(0, wxT("*."));
|
||||
|
||||
if (sound_extno < 0 && ext.find(wxT("*.wav")) != wxString::npos)
|
||||
if (sound_extno < 0 && ext.find(wxT("*.mp3")) != wxString::npos)
|
||||
sound_extno = extno;
|
||||
|
||||
sound_exts.append(ext);
|
||||
|
@ -1252,19 +1234,16 @@ EVT_HANDLER_MASK(RecordAVIStartRecording, "Start video recording...", CMDEN_NVRE
|
|||
|
||||
if (!vid_exts.size()) {
|
||||
vid_extno = -1;
|
||||
int extno;
|
||||
AVOutputFormat* fmt;
|
||||
int extno = 0;
|
||||
|
||||
for (fmt = NULL, extno = 0; (fmt = av_oformat_next(fmt));) {
|
||||
if (!fmt->extensions)
|
||||
continue;
|
||||
std::vector<char *> fmts = recording::getSupVidNames();
|
||||
std::vector<char *> exts = recording::getSupVidExts();
|
||||
|
||||
if (fmt->video_codec == CODEC_ID_NONE)
|
||||
continue;
|
||||
|
||||
vid_exts.append(wxString(fmt->long_name ? fmt->long_name : fmt->name, wxConvLibc));
|
||||
for (size_t i = 0; i < fmts.size(); ++i)
|
||||
{
|
||||
vid_exts.append(wxString(fmts[i], wxConvLibc));
|
||||
vid_exts.append(_(" files ("));
|
||||
wxString ext(fmt->extensions, wxConvLibc);
|
||||
wxString ext(exts[i], wxConvLibc);
|
||||
ext.Replace(wxT(","), wxT(";*."));
|
||||
ext.insert(0, wxT("*."));
|
||||
|
||||
|
|
|
@ -1016,8 +1016,8 @@ void GameArea::OnIdle(wxIdleEvent& event)
|
|||
// the userdata is freed on disconnect/destruction
|
||||
this->Connect(wxEVT_SIZE, wxSizeEventHandler(GameArea::OnSize), NULL, this);
|
||||
|
||||
// we need to check if the buttons stayed pressed when focus the panel
|
||||
w->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(GameArea::OnKillFocus), NULL, this);
|
||||
// we need to check if the buttons stayed pressed when focus the panel
|
||||
w->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(GameArea::OnKillFocus), NULL, this);
|
||||
|
||||
w->SetBackgroundStyle(wxBG_STYLE_CUSTOM);
|
||||
w->SetSize(wxSize(basic_width, basic_height));
|
||||
|
@ -1143,7 +1143,7 @@ static void clear_input_press()
|
|||
int i;
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
joypress[i] = 0;
|
||||
joypress[i] = 0;
|
||||
}
|
||||
keys_pressed.clear();
|
||||
}
|
||||
|
@ -2179,15 +2179,15 @@ void GLDrawingPanel::DrawingPanelInit()
|
|||
#define tex_fmt out_16 ? GL_BGRA : GL_RGBA, \
|
||||
out_16 ? GL_UNSIGNED_SHORT_1_5_5_5_REV : GL_UNSIGNED_BYTE
|
||||
#if 0
|
||||
texsize = width > height ? width : height;
|
||||
texsize = std::ceil(texsize * scale);
|
||||
// texsize = 1 << ffs(texsize);
|
||||
texsize = texsize | (texsize >> 1);
|
||||
texsize = texsize | (texsize >> 2);
|
||||
texsize = texsize | (texsize >> 4);
|
||||
texsize = texsize | (texsize >> 8);
|
||||
texsize = (texsize >> 1) + 1;
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, texsize, texsize, 0, tex_fmt, NULL);
|
||||
texsize = width > height ? width : height;
|
||||
texsize = std::ceil(texsize * scale);
|
||||
// texsize = 1 << ffs(texsize);
|
||||
texsize = texsize | (texsize >> 1);
|
||||
texsize = texsize | (texsize >> 2);
|
||||
texsize = texsize | (texsize >> 4);
|
||||
texsize = texsize | (texsize >> 8);
|
||||
texsize = (texsize >> 1) + 1;
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, texsize, texsize, 0, tex_fmt, NULL);
|
||||
#else
|
||||
// but really, most cards support non-p2 and rect
|
||||
// if not, use cairo or wx renderer
|
||||
|
@ -2311,22 +2311,22 @@ void DXDrawingPanel::DrawArea(wxWindowDC& dc)
|
|||
#endif
|
||||
|
||||
#ifndef NO_FFMPEG
|
||||
static const wxString media_err(MediaRet ret)
|
||||
static const wxString media_err(recording::MediaRet ret)
|
||||
{
|
||||
switch (ret) {
|
||||
case MRET_OK:
|
||||
case recording::MRET_OK:
|
||||
return wxT("");
|
||||
|
||||
case MRET_ERR_NOMEM:
|
||||
case recording::MRET_ERR_NOMEM:
|
||||
return _("memory allocation error");
|
||||
|
||||
case MRET_ERR_NOCODEC:
|
||||
case recording::MRET_ERR_NOCODEC:
|
||||
return _("error initializing codec");
|
||||
|
||||
case MRET_ERR_FERR:
|
||||
case recording::MRET_ERR_FERR:
|
||||
return _("error writing to output file");
|
||||
|
||||
case MRET_ERR_FMTGUESS:
|
||||
case recording::MRET_ERR_FMTGUESS:
|
||||
return _("can't guess output format from file name");
|
||||
|
||||
default:
|
||||
|
@ -2338,11 +2338,11 @@ static const wxString media_err(MediaRet ret)
|
|||
|
||||
void GameArea::StartVidRecording(const wxString& fname)
|
||||
{
|
||||
MediaRet ret;
|
||||
recording::MediaRet ret;
|
||||
|
||||
if ((ret = vid_rec.Record(fname.mb_str(), basic_width, basic_height,
|
||||
systemColorDepth))
|
||||
!= MRET_OK)
|
||||
!= recording::MRET_OK)
|
||||
wxLogError(_("Unable to begin recording to %s (%s)"), fname.mb_str(),
|
||||
media_err(ret));
|
||||
else {
|
||||
|
@ -2368,9 +2368,9 @@ void GameArea::StopVidRecording()
|
|||
|
||||
void GameArea::StartSoundRecording(const wxString& fname)
|
||||
{
|
||||
MediaRet ret;
|
||||
recording::MediaRet ret;
|
||||
|
||||
if ((ret = snd_rec.Record(fname.mb_str())) != MRET_OK)
|
||||
if ((ret = snd_rec.Record(fname.mb_str())) != recording::MRET_OK)
|
||||
wxLogError(_("Unable to begin recording to %s (%s)"), fname.mb_str(),
|
||||
media_err(ret));
|
||||
else {
|
||||
|
@ -2396,15 +2396,15 @@ void GameArea::StopSoundRecording()
|
|||
|
||||
void GameArea::AddFrame(const uint16_t* data, int length)
|
||||
{
|
||||
MediaRet ret;
|
||||
recording::MediaRet ret;
|
||||
|
||||
if ((ret = vid_rec.AddFrame(data)) != MRET_OK) {
|
||||
if ((ret = vid_rec.AddFrame(data, length)) != recording::MRET_OK) {
|
||||
wxLogError(_("Error in audio/video recording (%s); aborting"),
|
||||
media_err(ret));
|
||||
vid_rec.Stop();
|
||||
}
|
||||
|
||||
if ((ret = snd_rec.AddFrame(data)) != MRET_OK) {
|
||||
if ((ret = snd_rec.AddFrame(data, length)) != recording::MRET_OK) {
|
||||
wxLogError(_("Error in audio recording (%s); aborting"), media_err(ret));
|
||||
snd_rec.Stop();
|
||||
}
|
||||
|
@ -2412,9 +2412,9 @@ void GameArea::AddFrame(const uint16_t* data, int length)
|
|||
|
||||
void GameArea::AddFrame(const uint8_t* data)
|
||||
{
|
||||
MediaRet ret;
|
||||
recording::MediaRet ret;
|
||||
|
||||
if ((ret = vid_rec.AddFrame(data)) != MRET_OK) {
|
||||
if ((ret = vid_rec.AddFrame(data)) != recording::MRET_OK) {
|
||||
wxLogError(_("Error in video recording (%s); aborting"), media_err(ret));
|
||||
vid_rec.Stop();
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ public:
|
|||
|
||||
wxAcceleratorEntry_v GetAccels()
|
||||
{
|
||||
return accels;
|
||||
return accels;
|
||||
}
|
||||
|
||||
// the main configuration
|
||||
|
@ -632,7 +632,7 @@ protected:
|
|||
void OnKillFocus(wxFocusEvent& ev);
|
||||
|
||||
#ifndef NO_FFMPEG
|
||||
MediaRecorder snd_rec, vid_rec;
|
||||
recording::MediaRecorder snd_rec, vid_rec;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
|
Loading…
Reference in New Issue