parent
7145f8083d
commit
98215cfb30
|
@ -16,6 +16,16 @@ option( ENABLE_ASM_CORE "Enable x86 ASM CPU cores" OFF )
|
|||
option( ENABLE_ASM_SCALERS "Enable x86 ASM graphic filters" OFF )
|
||||
option( ENABLE_LINK "Enable GBA linking functionality" OFF )
|
||||
option( ENABLE_LIRC "Enable LIRC support" OFF )
|
||||
if(ENABLE_ASM_SCALERS)
|
||||
option( ENABLE_MMX "Enable MMX" OFF )
|
||||
endif(ENABLE_ASM_SCALERS)
|
||||
option( ENABLE_GBA_LOGGING "Enable extended GBA logging" ON )
|
||||
if( ENABLE_GBA_LOGGING )
|
||||
ADD_DEFINITIONS (-DGBA_LOGGING )
|
||||
endif( ENABLE_GBA_LOGGING )
|
||||
if(ENABLE_MMX)
|
||||
ADD_DEFINITIONS (-DMMX)
|
||||
endif(ENABLE_MMX)
|
||||
|
||||
# The SDL port can't be built without debugging support
|
||||
if( NOT ENABLE_DEBUGGER AND ENABLE_SDL )
|
||||
|
@ -24,7 +34,13 @@ endif( NOT ENABLE_DEBUGGER AND ENABLE_SDL )
|
|||
|
||||
# Set the version number with -DVERSION=X.X.X-uber
|
||||
IF( NOT VERSION )
|
||||
SET( VERSION "1.8.0-SVN" )
|
||||
FIND_PACKAGE(Subversion)
|
||||
IF(SUBVERSION_FOUND)
|
||||
Subversion_WC_INFO(${CMAKE_CURRENT_SOURCE_DIR} SVN_INFO)
|
||||
SET( VERSION "1.8.0-SVN${SVN_INFO_WC_REVISION}" )
|
||||
ELSE(SUBVERSION_FOUND)
|
||||
SET( VERSION "1.8.0-SVN" )
|
||||
ENDIF(SUBVERSION_FOUND)
|
||||
ENDIF( NOT VERSION )
|
||||
|
||||
# Fill in SDLMAIN_LIBRARY on OS X manually to avoid using SDLMain.m
|
||||
|
@ -62,6 +78,17 @@ IF( ENABLE_GTK )
|
|||
PKG_CHECK_MODULES ( GTKGLMM REQUIRED gtkglextmm-x11-1.2 )
|
||||
ENDIF( ENABLE_GTK )
|
||||
|
||||
option( ENABLE_FFMPEG "Enable ffmpeg A/V recording" ON )
|
||||
if(ENABLE_FFMPEG)
|
||||
FIND_PACKAGE ( PkgConfig REQUIRED )
|
||||
|
||||
PKG_CHECK_MODULES(FFMPEG REQUIRED libavcodec libavformat libswscale libavutil)
|
||||
endif(ENABLE_FFMPEG)
|
||||
|
||||
if(NOT ENABLE_FFMPEG)
|
||||
ADD_DEFINITIONS(-DNO_FFMPEG)
|
||||
endif(NOT ENABLE_FFMPEG)
|
||||
|
||||
IF( ENABLE_LIRC )
|
||||
SET( WITHLIRC 1 )
|
||||
ELSE( ENABLE_LIRC )
|
||||
|
@ -157,6 +184,10 @@ SET(SRC_MAIN
|
|||
src/common/memgzio.c
|
||||
)
|
||||
|
||||
if(ENABLE_FFMPEG)
|
||||
SET(SRC_MAIN ${SRC_MAIN} src/common/ffmpeg.cpp)
|
||||
endif(ENABLE_FFMPEG)
|
||||
|
||||
SET(SRC_GBA
|
||||
src/gba/agbprint.cpp
|
||||
src/gba/bios.cpp
|
||||
|
|
|
@ -72,13 +72,18 @@ static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d)
|
|||
|
||||
#else
|
||||
|
||||
// Mac cross-compile compiler:
|
||||
// can't find register in class 'BREG' while reloading 'asm'
|
||||
// so use class 'r' and register var binding
|
||||
register _b asm("%bx");
|
||||
__asm__ __volatile__ (
|
||||
"cpuid"
|
||||
: "=a" (*a) ,
|
||||
"=b" (*b) ,
|
||||
"=r" (_b) ,
|
||||
"=c" (*c) ,
|
||||
"=d" (*d)
|
||||
: "0" (function)) ;
|
||||
*b = _b;
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ struct EmulatedSystem {
|
|||
extern void log(const char *,...);
|
||||
|
||||
extern bool systemPauseOnFrame();
|
||||
extern void systemGbPrint(u8 *,int,int,int,int);
|
||||
extern void systemGbPrint(u8 *,int,int,int,int,int);
|
||||
extern void systemScreenCapture(int);
|
||||
extern void systemDrawScreen();
|
||||
// updates the joystick data
|
||||
|
|
|
@ -598,6 +598,7 @@ gzFile utilMemGzOpen(char *memory, int available, const char *mode)
|
|||
utilGzWriteFunc = memgzwrite;
|
||||
utilGzReadFunc = memgzread;
|
||||
utilGzCloseFunc = memgzclose;
|
||||
utilGzSeekFunc = memgzseek;
|
||||
|
||||
return memgzopen(memory, available, mode);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ int const silent_buf_size = 1; // size used for Silent_Blip_Buffer
|
|||
|
||||
Blip_Buffer::Blip_Buffer()
|
||||
{
|
||||
factor_ = LONG_MAX;
|
||||
factor_ = (blip_ulong)LONG_MAX;
|
||||
buffer_ = 0;
|
||||
buffer_size_ = 0;
|
||||
sample_rate_ = 0;
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
|
||||
class Gb_Env : public Gb_Osc {
|
||||
public:
|
||||
Gb_Env() : env_enabled(false), env_delay(0) {}
|
||||
int env_delay;
|
||||
int volume;
|
||||
bool env_enabled;
|
||||
|
|
|
@ -0,0 +1,468 @@
|
|||
// this code has been partially lifted from the output-example.c program in
|
||||
// libavformat. Not much of that original code remains.
|
||||
|
||||
// unlike the rest of the wx code, this has no wx dependency at all, and
|
||||
// could be used by other front ends as well.
|
||||
|
||||
#define __STDC_LIMIT_MACROS // required for ffmpeg
|
||||
#define __STDC_CONSTANT_MACROS // required for ffmpeg
|
||||
|
||||
#include "../gba/Sound.h"
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libavcodec/opt.h>
|
||||
#ifndef AV_PKT_FLAG_KEY
|
||||
#define AV_PKT_FLAG_KEY PKT_FLAG_KEY
|
||||
#endif
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,96,0)
|
||||
// note that there is no sane way to easily free a context w/o free_context()
|
||||
// so this will probably leak
|
||||
static void avformat_free_context(AVFormatContext *ctx)
|
||||
{
|
||||
if(ctx->pb)
|
||||
url_fclose(ctx->pb);
|
||||
for(int i = 0; i < ctx->nb_streams; i++) {
|
||||
if(ctx->streams[i]->codec)
|
||||
avcodec_close(ctx->streams[i]->codec);
|
||||
av_freep(&ctx->streams[i]->codec);
|
||||
av_freep(&ctx->streams[i]);
|
||||
}
|
||||
av_free(ctx);
|
||||
}
|
||||
#endif
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,45,0)
|
||||
#define av_guess_format guess_format
|
||||
#endif
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,105,0)
|
||||
#define avio_open url_fopen
|
||||
#define avio_close url_fclose
|
||||
#endif
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52,64,0)
|
||||
#define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
|
||||
#define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO
|
||||
#endif
|
||||
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(50,1,0)
|
||||
// this will almost definitely fail on big-endian systems
|
||||
#define PIX_FMT_RGB565LE PIX_FMT_RGB565
|
||||
#endif
|
||||
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52,38,0)
|
||||
#define AV_SAMPLE_FMT_S16 SAMPLE_FMT_S16
|
||||
#endif
|
||||
}
|
||||
|
||||
#define priv_AVFormatContext AVFormatContext
|
||||
#define priv_AVStream AVStream
|
||||
#define priv_AVOutputFormat AVOutputFormat
|
||||
#define priv_AVFrame AVFrame
|
||||
#define priv_SwsContext SwsContext
|
||||
#define priv_PixelFormat PixelFormat
|
||||
#include "ffmpeg.h"
|
||||
|
||||
// I have no idea what size to make these buffers
|
||||
// I don't see any ffmpeg functions to guess the size, either
|
||||
|
||||
// use frame size, or FF_MIN_BUFFER_SIZE (that seems to be what it wants)
|
||||
#define AUDIO_BUF_LEN (frame_len > FF_MIN_BUFFER_SIZE ? frame_len : FF_MIN_BUFFER_SIZE)
|
||||
// use maximum frame size * 32 bpp * 2 for good measure
|
||||
#define VIDEO_BUF_LEN (FF_MIN_BUFFER_SIZE + 256 * 244 * 4 * 2)
|
||||
|
||||
bool MediaRecorder::did_init = false;
|
||||
|
||||
MediaRecorder::MediaRecorder() : oc(0), vid_st(0), aud_st(0), video_buf(0),
|
||||
audio_buf(0), audio_buf2(0), converter(0), convpic(0)
|
||||
{
|
||||
if(!did_init) {
|
||||
did_init = true;
|
||||
av_register_all();
|
||||
}
|
||||
pic = avcodec_alloc_frame();
|
||||
}
|
||||
|
||||
MediaRet MediaRecorder::setup_sound_stream(const char *fname, AVOutputFormat *fmt)
|
||||
{
|
||||
oc = avformat_alloc_context();
|
||||
if(!oc)
|
||||
return MRET_ERR_NOMEM;
|
||||
oc->oformat = fmt;
|
||||
strncpy(oc->filename, fname, sizeof(oc->filename) - 1);
|
||||
oc->filename[sizeof(oc->filename) - 1] = 0;
|
||||
if(fmt->audio_codec == CODEC_ID_NONE)
|
||||
return MRET_OK;
|
||||
|
||||
AVCodecContext *ctx;
|
||||
aud_st = av_new_stream(oc, 1);
|
||||
if(!aud_st) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
return MRET_ERR_NOMEM;
|
||||
}
|
||||
ctx = aud_st->codec;
|
||||
ctx->codec_id = fmt->audio_codec;
|
||||
ctx->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
ctx->sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
ctx->bit_rate = 128000; // arbitrary; in case we're generating mp3
|
||||
ctx->sample_rate = soundGetSampleRate();
|
||||
ctx->channels = 2;
|
||||
ctx->time_base.den = 60;
|
||||
ctx->time_base.num = 1;
|
||||
if(fmt->flags & AVFMT_GLOBALHEADER)
|
||||
ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||
|
||||
AVCodec *codec = avcodec_find_encoder(fmt->audio_codec);
|
||||
if(!codec || avcodec_open(ctx, codec)) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
return MRET_ERR_NOCODEC;
|
||||
}
|
||||
|
||||
return MRET_OK;
|
||||
}
|
||||
|
||||
MediaRet MediaRecorder::setup_video_stream(const char *fname, int w, int h, int d)
|
||||
{
|
||||
AVCodecContext *ctx;
|
||||
vid_st = av_new_stream(oc, 0);
|
||||
if(!vid_st) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
return MRET_ERR_NOMEM;
|
||||
}
|
||||
ctx = vid_st->codec;
|
||||
ctx->codec_id = oc->oformat->video_codec;
|
||||
ctx->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
ctx->width = w;
|
||||
ctx->height = h;
|
||||
ctx->time_base.den = 60;
|
||||
ctx->time_base.num = 1;
|
||||
// dunno if any of these help; some output just looks plain crappy
|
||||
// will have to investigate further
|
||||
ctx->bit_rate = 400000;
|
||||
ctx->gop_size = 12;
|
||||
ctx->max_b_frames = 2;
|
||||
switch(d) {
|
||||
case 16:
|
||||
// FIXME: test & make endian-neutral
|
||||
pixfmt = PIX_FMT_RGB565LE;
|
||||
break;
|
||||
case 24:
|
||||
pixfmt = PIX_FMT_RGB24;
|
||||
break;
|
||||
case 32:
|
||||
default: // should never be anything else
|
||||
pixfmt = PIX_FMT_RGBA;
|
||||
break;
|
||||
}
|
||||
ctx->pix_fmt = pixfmt;
|
||||
pixsize = d >> 3;
|
||||
linesize = pixsize * w;
|
||||
ctx->max_b_frames = 2;
|
||||
if(oc->oformat->flags & AVFMT_GLOBALHEADER)
|
||||
ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||
|
||||
AVCodec *codec = avcodec_find_encoder(oc->oformat->video_codec);
|
||||
// make sure RGB is supported (mostly not)
|
||||
if(codec->pix_fmts) {
|
||||
const enum PixelFormat *p;
|
||||
int64_t mask = 0;
|
||||
for(p = codec->pix_fmts; *p != -1; p++) {
|
||||
// may get complaints about 1LL; thus the cast
|
||||
mask |= ((int64_t)1) << *p;
|
||||
if(*p == pixfmt)
|
||||
break;
|
||||
}
|
||||
if(*p == -1) {
|
||||
// if not supported, use a converter to the next best format
|
||||
// this is swscale, the converter used by the output demo
|
||||
enum PixelFormat dp = (PixelFormat)avcodec_find_best_pix_fmt(mask, pixfmt, 0, NULL);
|
||||
if(dp == -1)
|
||||
dp = codec->pix_fmts[0];
|
||||
if(!(convpic = avcodec_alloc_frame()) ||
|
||||
avpicture_alloc((AVPicture *)convpic, dp, w, h) < 0) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
return MRET_ERR_NOMEM;
|
||||
}
|
||||
#if LIBSWSCALE_VERSION_INT < AV_VERSION_INT(0, 12, 0)
|
||||
converter = sws_getContext(w, h, pixfmt, w, h, dp, SWS_BICUBIC,
|
||||
NULL, NULL, NULL);
|
||||
#else
|
||||
converter = sws_alloc_context();
|
||||
// what a convoluted, inefficient way to set options
|
||||
av_set_int(converter, "sws_flags", SWS_BICUBIC);
|
||||
av_set_int(converter, "srcw", w);
|
||||
av_set_int(converter, "srch", h);
|
||||
av_set_int(converter, "dstw", w);
|
||||
av_set_int(converter, "dsth", h);
|
||||
av_set_int(converter, "src_format", pixfmt);
|
||||
av_set_int(converter, "dst_format", dp);
|
||||
sws_init_context(converter, NULL, NULL);
|
||||
#endif
|
||||
ctx->pix_fmt = dp;
|
||||
}
|
||||
}
|
||||
if(!codec || avcodec_open(ctx, codec)) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
return MRET_ERR_NOCODEC;
|
||||
}
|
||||
|
||||
return MRET_OK;
|
||||
}
|
||||
|
||||
MediaRet MediaRecorder::finish_setup(const char *fname)
|
||||
{
|
||||
if(av_set_parameters(oc, NULL) < 0) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
return MRET_ERR_NOCODEC;
|
||||
}
|
||||
if(audio_buf)
|
||||
free(audio_buf);
|
||||
if(audio_buf2)
|
||||
free(audio_buf2);
|
||||
audio_buf2 = NULL;
|
||||
in_audio_buf2 = 0;
|
||||
if(aud_st) {
|
||||
frame_len = aud_st->codec->frame_size * 4;
|
||||
sample_len = soundGetSampleRate() * 4 / 60;
|
||||
switch(aud_st->codec->codec_id) {
|
||||
case CODEC_ID_PCM_S16LE:
|
||||
case CODEC_ID_PCM_S16BE:
|
||||
case CODEC_ID_PCM_U16LE:
|
||||
case CODEC_ID_PCM_U16BE:
|
||||
frame_len = sample_len;
|
||||
}
|
||||
audio_buf = (u8 *)malloc(AUDIO_BUF_LEN);
|
||||
if(!audio_buf) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
return MRET_ERR_NOMEM;
|
||||
}
|
||||
if(frame_len != sample_len && (frame_len > sample_len || sample_len % frame_len)) {
|
||||
audio_buf2 = (u16 *)malloc(frame_len);
|
||||
if(!audio_buf2) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
return MRET_ERR_NOMEM;
|
||||
}
|
||||
}
|
||||
} else
|
||||
audio_buf = NULL;
|
||||
if(video_buf)
|
||||
free(video_buf);
|
||||
if(vid_st) {
|
||||
video_buf = (u8 *)malloc(VIDEO_BUF_LEN);
|
||||
if(!video_buf) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
return MRET_ERR_NOMEM;
|
||||
}
|
||||
} else {
|
||||
video_buf = NULL;
|
||||
}
|
||||
if(!(oc->oformat->flags & AVFMT_NOFILE)) {
|
||||
if(avio_open(&oc->pb, fname, URL_WRONLY) < 0) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
return MRET_ERR_FERR;
|
||||
}
|
||||
}
|
||||
av_write_header(oc);
|
||||
return MRET_OK;
|
||||
}
|
||||
|
||||
MediaRet MediaRecorder::Record(const char *fname, int width, int height, int depth)
|
||||
{
|
||||
if(oc)
|
||||
return MRET_ERR_RECORDING;
|
||||
aud_st = vid_st = NULL;
|
||||
AVOutputFormat *fmt = av_guess_format(NULL, fname, NULL);
|
||||
if(!fmt)
|
||||
fmt = av_guess_format("avi", NULL, NULL);
|
||||
if(!fmt || fmt->video_codec == CODEC_ID_NONE)
|
||||
return MRET_ERR_FMTGUESS;
|
||||
MediaRet ret;
|
||||
if((ret = setup_sound_stream(fname, fmt)) == MRET_OK &&
|
||||
(ret = setup_video_stream(fname, width, height, depth)) == MRET_OK)
|
||||
ret = finish_setup(fname);
|
||||
return ret;
|
||||
}
|
||||
|
||||
MediaRet MediaRecorder::Record(const char *fname)
|
||||
{
|
||||
if(oc)
|
||||
return MRET_ERR_RECORDING;
|
||||
aud_st = vid_st = NULL;
|
||||
AVOutputFormat *fmt = av_guess_format(NULL, fname, NULL);
|
||||
if(!fmt)
|
||||
fmt = av_guess_format("wav", NULL, NULL);
|
||||
if(!fmt || fmt->audio_codec == CODEC_ID_NONE)
|
||||
return MRET_ERR_FMTGUESS;
|
||||
MediaRet ret;
|
||||
if((ret = setup_sound_stream(fname, fmt)) == MRET_OK)
|
||||
ret = finish_setup(fname);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MediaRecorder::Stop()
|
||||
{
|
||||
if(oc) {
|
||||
if(in_audio_buf2)
|
||||
AddFrame((u16 *)0);
|
||||
av_write_trailer(oc);
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
}
|
||||
if(audio_buf) {
|
||||
free(audio_buf);
|
||||
audio_buf = NULL;
|
||||
}
|
||||
if(video_buf) {
|
||||
free(video_buf);
|
||||
video_buf = NULL;
|
||||
}
|
||||
if(audio_buf2) {
|
||||
free(audio_buf2);
|
||||
audio_buf2 = NULL;
|
||||
}
|
||||
if(convpic) {
|
||||
avpicture_free((AVPicture *)convpic);
|
||||
av_free(convpic);
|
||||
convpic = NULL;
|
||||
}
|
||||
if(converter) {
|
||||
sws_freeContext(converter);
|
||||
converter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MediaRecorder::~MediaRecorder()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
MediaRet MediaRecorder::AddFrame(const u8 *vid)
|
||||
{
|
||||
if(!oc || !vid_st)
|
||||
return MRET_OK;
|
||||
|
||||
AVCodecContext *ctx = vid_st->codec;
|
||||
AVPacket pkt;
|
||||
|
||||
// strip borders. inconsistent between depths for some reason
|
||||
// but fortunately consistent between gb/gba.
|
||||
int tbord, rbord;
|
||||
switch(pixsize) {
|
||||
case 2:
|
||||
// 16-bit: 2 @ right, 1 @ top
|
||||
tbord = 1; rbord = 2; break;
|
||||
case 3:
|
||||
// 24-bit: no border
|
||||
tbord = rbord = 0; break;
|
||||
case 4:
|
||||
// 32-bit: 1 @ right, 1 @ top
|
||||
tbord = 1; rbord = 1; break;
|
||||
}
|
||||
avpicture_fill((AVPicture *)pic, (uint8_t *)vid + tbord * (linesize + pixsize * rbord),
|
||||
(PixelFormat)pixfmt, ctx->width + rbord, ctx->height);
|
||||
// satisfy stupid sws_scale()'s integrity check
|
||||
pic->data[1] = pic->data[2] = pic->data[3] = pic->data[0];
|
||||
pic->linesize[1] = pic->linesize[2] = pic->linesize[3] = pic->linesize[0];
|
||||
|
||||
AVFrame *f = pic;
|
||||
|
||||
if(converter) {
|
||||
sws_scale(converter, pic->data, pic->linesize, 0, ctx->height,
|
||||
convpic->data, convpic->linesize);
|
||||
f = convpic;
|
||||
}
|
||||
av_init_packet(&pkt);
|
||||
pkt.stream_index = vid_st->index;
|
||||
if(oc->oformat->flags & AVFMT_RAWPICTURE) {
|
||||
// this won't work due to border
|
||||
// not sure what formats set this, anyway
|
||||
pkt.flags |= AV_PKT_FLAG_KEY;
|
||||
pkt.data = f->data[0];
|
||||
pkt.size = linesize * ctx->height;
|
||||
} else {
|
||||
pkt.size = avcodec_encode_video(ctx, video_buf, VIDEO_BUF_LEN, f);
|
||||
if(!pkt.size)
|
||||
return MRET_OK;
|
||||
if(ctx->coded_frame && ctx->coded_frame->pts != AV_NOPTS_VALUE)
|
||||
pkt.pts = av_rescale_q(ctx->coded_frame->pts, ctx->time_base, vid_st->time_base);
|
||||
if(pkt.size > VIDEO_BUF_LEN) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
return MRET_ERR_BUFSIZE;
|
||||
}
|
||||
if(ctx->coded_frame->key_frame)
|
||||
pkt.flags |= AV_PKT_FLAG_KEY;
|
||||
pkt.data = video_buf;
|
||||
}
|
||||
if(av_interleaved_write_frame(oc, &pkt) < 0) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
// yeah, err might not be a file error, but if it isn't, it's a
|
||||
// coding error rather than a user-controllable error
|
||||
// and better resolved using debugging
|
||||
return MRET_ERR_FERR;
|
||||
}
|
||||
return MRET_OK;
|
||||
}
|
||||
|
||||
MediaRet MediaRecorder::AddFrame(const u16 *aud)
|
||||
{
|
||||
if(!oc || !aud_st)
|
||||
return MRET_OK;
|
||||
// aud == NULL means just flush out last frame
|
||||
if(!aud && !in_audio_buf2)
|
||||
return MRET_OK;
|
||||
AVCodecContext *ctx = aud_st->codec;
|
||||
AVPacket pkt;
|
||||
|
||||
int len = sample_len;
|
||||
if(in_audio_buf2) {
|
||||
int ncpy = frame_len - in_audio_buf2;
|
||||
if(ncpy > len)
|
||||
ncpy = len;
|
||||
if(aud) {
|
||||
memcpy(audio_buf2 + in_audio_buf2/2, aud, ncpy);
|
||||
len -= ncpy;
|
||||
aud += ncpy / 2;
|
||||
} else {
|
||||
memset(audio_buf2 + in_audio_buf2/2, 0, ncpy);
|
||||
len = 0;
|
||||
}
|
||||
in_audio_buf2 += ncpy;
|
||||
}
|
||||
while(len + in_audio_buf2 >= frame_len) {
|
||||
av_init_packet(&pkt);
|
||||
pkt.size = avcodec_encode_audio(ctx, audio_buf, frame_len,
|
||||
(const short *)(in_audio_buf2 ? audio_buf2 : aud));
|
||||
if(ctx->coded_frame && ctx->coded_frame->pts != AV_NOPTS_VALUE)
|
||||
pkt.pts = av_rescale_q(ctx->coded_frame->pts, ctx->time_base, aud_st->time_base);
|
||||
pkt.flags |= AV_PKT_FLAG_KEY;
|
||||
pkt.stream_index = aud_st->index;
|
||||
pkt.data = audio_buf;
|
||||
if(av_interleaved_write_frame(oc, &pkt) < 0) {
|
||||
avformat_free_context(oc);
|
||||
oc = NULL;
|
||||
// yeah, err might not be a file error, but if it isn't, it's a
|
||||
// coding error rather than a user-controllable error
|
||||
// and better resolved using debugging
|
||||
return MRET_ERR_FERR;
|
||||
}
|
||||
if(in_audio_buf2)
|
||||
in_audio_buf2 = 0;
|
||||
else {
|
||||
aud += frame_len / 2;
|
||||
len -= frame_len;
|
||||
}
|
||||
}
|
||||
if(len > 0) {
|
||||
memcpy(audio_buf2, aud, len);
|
||||
in_audio_buf2 = len;
|
||||
}
|
||||
return MRET_OK;
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
#ifndef WX_FFMPEG_H
|
||||
#define WX_FFMPEG_H
|
||||
|
||||
// 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.
|
||||
|
||||
// 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.
|
||||
|
||||
// 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.
|
||||
|
||||
#include "../common/Types.h"
|
||||
|
||||
// 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
|
||||
MRET_ERR_NOCODEC, // error opening codec
|
||||
MRET_ERR_FERR, // error writing output file
|
||||
MRET_ERR_RECORDING, // attempt to start recording when already doing it
|
||||
MRET_ERR_FMTGUESS, // can't guess format from file name
|
||||
MRET_ERR_BUFSIZE // buffer overflow (fatal)
|
||||
};
|
||||
|
||||
class MediaRecorder
|
||||
{
|
||||
public:
|
||||
MediaRecorder();
|
||||
virtual ~MediaRecorder();
|
||||
|
||||
// start audio+video (also video-only codecs)
|
||||
MediaRet Record(const char *fname, int width, int height, int depth);
|
||||
// start audio only
|
||||
MediaRet Record(const char *fname);
|
||||
// stop both
|
||||
void Stop();
|
||||
bool IsRecording() { return oc != NULL; }
|
||||
// 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 u8 *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 u16 *aud);
|
||||
|
||||
private:
|
||||
static bool did_init;
|
||||
|
||||
// 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;
|
||||
u8 *audio_buf, *video_buf;
|
||||
u16 *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 finish_setup(const char *fname);
|
||||
};
|
||||
|
||||
#endif /* WX_FFMPEG_H */
|
|
@ -697,3 +697,21 @@ long ZEXPORT memtell(file)
|
|||
|
||||
return memTell(s->file);
|
||||
}
|
||||
|
||||
long ZEXPORT memgzseek(gzFile file, long off, int whence)
|
||||
{
|
||||
if(whence != SEEK_CUR) {
|
||||
fputs("FIXME: memgzio does not support seeking\n", stderr);
|
||||
exit(1);
|
||||
}
|
||||
// this is inefficient, but the best I can do without actually reading
|
||||
// the above code
|
||||
char buf[80];
|
||||
while(off > 0) {
|
||||
int r = memgzread(file, buf, off > 80 ? 80 : off);
|
||||
if(r <= 0)
|
||||
return -1;
|
||||
off -= r;
|
||||
}
|
||||
return memtell(file);
|
||||
}
|
||||
|
|
|
@ -19,5 +19,6 @@ int ZEXPORT memgzread(gzFile file, voidp buf, unsigned len);
|
|||
int ZEXPORT memgzwrite(gzFile file, const voidp buf, unsigned len);
|
||||
int ZEXPORT memgzclose(gzFile file);
|
||||
long ZEXPORT memtell(gzFile file);
|
||||
long ZEXPORT memgzseek(gzFile file, long offset, int whence);
|
||||
|
||||
#endif // MEMGZIO_H
|
||||
|
|
|
@ -4976,6 +4976,8 @@ void gbEmulate(int ticksToStop)
|
|||
gbSgbRenderBorder();
|
||||
//if (gbScreenOn)
|
||||
systemDrawScreen();
|
||||
if(systemPauseOnFrame())
|
||||
ticksToStop = 0;
|
||||
}
|
||||
gbFrameSkipCount = 0;
|
||||
} else
|
||||
|
@ -5160,6 +5162,8 @@ void gbEmulate(int ticksToStop)
|
|||
gbSgbRenderBorder();
|
||||
//if (gbScreenOn)
|
||||
systemDrawScreen();
|
||||
if(systemPauseOnFrame())
|
||||
ticksToStop = 0;
|
||||
}
|
||||
}
|
||||
if(systemReadJoypads()) {
|
||||
|
|
|
@ -38,6 +38,7 @@ void gbPrinterReset()
|
|||
void gbPrinterShowData()
|
||||
{
|
||||
systemGbPrint(gbPrinterData,
|
||||
gbPrinterDataCount,
|
||||
gbPrinterPacket[6],
|
||||
gbPrinterPacket[7],
|
||||
gbPrinterPacket[8],
|
||||
|
@ -87,6 +88,7 @@ void gbPrinterShowData()
|
|||
|
||||
void gbPrinterReceiveData()
|
||||
{
|
||||
int i = gbPrinterDataCount;
|
||||
if(gbPrinterPacket[3]) { // compressed
|
||||
u8 *data = &gbPrinterPacket[6];
|
||||
u8 *dest = &gbPrinterData[gbPrinterDataCount];
|
||||
|
@ -97,16 +99,17 @@ void gbPrinterReceiveData()
|
|||
control &= 0x7f;
|
||||
control += 2;
|
||||
memset(dest, *data++, control);
|
||||
len += control;
|
||||
len += 2;
|
||||
dest += control;
|
||||
} else { // raw data
|
||||
control++;
|
||||
memcpy(dest, data, control);
|
||||
dest += control;
|
||||
data += control;
|
||||
len += control;
|
||||
len += control + 1;
|
||||
}
|
||||
}
|
||||
gbPrinterDataCount = (int)(dest - gbPrinterData);
|
||||
} else {
|
||||
memcpy(&gbPrinterData[gbPrinterDataCount],
|
||||
&gbPrinterPacket[6],
|
||||
|
|
|
@ -40,7 +40,7 @@ u8 gbSgbScreenBuffer[4160];
|
|||
|
||||
inline void gbSgbDraw24Bit(u8 *p, u16 v)
|
||||
{
|
||||
*((u32*) p) = systemColorMap32[v];
|
||||
memcpy(p, &systemColorMap32[v], 3);
|
||||
}
|
||||
|
||||
inline void gbSgbDraw32Bit(u32 *p, u16 v)
|
||||
|
|
|
@ -5,7 +5,11 @@ extern int armExecute();
|
|||
extern int thumbExecute();
|
||||
|
||||
#ifdef __GNUC__
|
||||
#ifndef __APPLE__
|
||||
# define INSN_REGPARM __attribute__((regparm(1)))
|
||||
#else
|
||||
# define INSN_REGPARM /*nothing*/
|
||||
#endif
|
||||
# define LIKELY(x) __builtin_expect(!!(x),1)
|
||||
# define UNLIKELY(x) __builtin_expect(!!(x),0)
|
||||
#else
|
||||
|
|
|
@ -115,6 +115,7 @@ int systemGetSensorY()
|
|||
}
|
||||
|
||||
void systemGbPrint(u8 * _puiData,
|
||||
int _iLen,
|
||||
int _iPages,
|
||||
int _iFeed,
|
||||
int _iPalette,
|
||||
|
|
|
@ -2591,7 +2591,7 @@ u32 systemGetClock()
|
|||
return SDL_GetTicks();
|
||||
}
|
||||
|
||||
void systemGbPrint(u8 *data,int pages,int feed,int palette, int contrast)
|
||||
void systemGbPrint(u8 *data,int len,int pages,int feed,int palette, int contrast)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -302,26 +302,26 @@ static void debuggerPrintBaseType(Type *t, u32 value, u32 location,
|
|||
case DW_ATE_signed:
|
||||
switch(debuggerRadix) {
|
||||
case 0:
|
||||
printf("%lld", value);
|
||||
printf("%lld", (long long)value);
|
||||
break;
|
||||
case 1:
|
||||
printf("0x%llx", value);
|
||||
printf("0x%llx", (long long)value);
|
||||
break;
|
||||
case 2:
|
||||
printf("0%llo", value);
|
||||
printf("0%llo", (long long)value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DW_ATE_unsigned:
|
||||
switch(debuggerRadix) {
|
||||
case 0:
|
||||
printf("%llu", value);
|
||||
printf("%llu", (unsigned long long)value);
|
||||
break;
|
||||
case 1:
|
||||
printf("0x%llx", value);
|
||||
printf("0x%llx", (unsigned long long)value);
|
||||
break;
|
||||
case 2:
|
||||
printf("0%llo", value);
|
||||
printf("0%llo", (unsigned long long)value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -467,12 +467,14 @@ void GBPrinterDlg::OnPaint()
|
|||
}
|
||||
|
||||
void systemGbPrint(u8 *data,
|
||||
int datalen,
|
||||
int pages,
|
||||
int feed,
|
||||
int palette,
|
||||
int contrast)
|
||||
{
|
||||
GBPrinterDlg printer;
|
||||
memset(data + datalen, 0, 160*144/4 - datalen);
|
||||
printer.processData(data);
|
||||
printer.DoModal();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue