From 1058d94ec1aab9564c0bc43fb13226ffd739fcb1 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 9 Oct 2011 12:27:02 +0200 Subject: [PATCH] x264 RGB recording. --- qb/config.libs.sh | 2 +- qb/config.params.sh | 3 ++- record/ffemu.c | 61 +++++++++++++++++++++++++++++++-------------- 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/qb/config.libs.sh b/qb/config.libs.sh index e7b7fa73bc..ed89bf2735 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -96,7 +96,7 @@ check_pkgconf PYTHON python3 add_define_make OS $OS # Creates config.mk and config.h. -VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER" +VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER X264RGB" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/qb/config.params.sh b/qb/config.params.sh index 13e9df5f32..d0ce8496ac 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -7,9 +7,10 @@ PACKAGE_VERSION=0.8.2 # $1: Variable (HAVE_ALSA, HAVE_OSS, etc) # $2: Comment # $3: Default arg. auto implies that HAVE_ALSA will be set according to library checks later on. -add_command_line_enable DYNAMIC "Enable dynamic loading of libsnes library." no +add_command_line_enable DYNAMIC "Enable dynamic loading of libsnes library" no add_command_line_string LIBSNES "libsnes library used" "-lsnes" add_command_line_enable FFMPEG "Enable FFmpeg recording support" no +add_command_line_enable X264RGB "Enable lossless X264 RGB recording" no add_command_line_enable DYLIB "Enable dynamic loading support" auto add_command_line_enable NETPLAY "Enable netplay support" auto add_command_line_enable SRC "Enable libsamplerate support" no diff --git a/record/ffemu.c b/record/ffemu.c index b8e5ea40cf..683f79d719 100644 --- a/record/ffemu.c +++ b/record/ffemu.c @@ -31,6 +31,7 @@ struct video_info size_t outbuf_size; int fmt; + int pix_fmt; int pix_size; AVFormatContext *format; @@ -120,7 +121,11 @@ static bool init_audio(struct audio_info *audio, struct ffemu_params *param) static bool init_video(struct video_info *video, struct ffemu_params *param) { +#ifdef HAVE_X264RGB + AVCodec *codec = avcodec_find_encoder(CODEC_ID_H264); +#else AVCodec *codec = avcodec_find_encoder(CODEC_ID_FFV1); +#endif if (!codec) return false; @@ -136,6 +141,12 @@ static bool init_video(struct video_info *video, struct ffemu_params *param) video->pix_size = sizeof(uint32_t); } +#ifdef HAVE_X264RGB + video->pix_fmt = PIX_FMT_BGR24; +#else + video->pix_fmt = PIX_FMT_RGB32; +#endif + #ifdef HAVE_FFMPEG_ALLOC_CONTEXT3 video->codec = avcodec_alloc_context3(codec); #else @@ -146,12 +157,20 @@ static bool init_video(struct video_info *video, struct ffemu_params *param) video->codec->width = param->out_width; video->codec->height = param->out_height; video->codec->time_base = (AVRational) {param->fps.den, param->fps.num}; - video->codec->pix_fmt = PIX_FMT_RGB32; video->codec->sample_aspect_ratio = av_d2q(param->aspect_ratio * param->out_height / param->out_width, 255); + video->codec->pix_fmt = video->pix_fmt; + + AVDictionary *opts = NULL; + +#ifdef HAVE_X264RGB + video->codec->thread_count = 3; + av_dict_set(&opts, "crf", "0", 0); +#else video->codec->thread_count = 2; +#endif #ifdef HAVE_FFMPEG_AVCODEC_OPEN2 - if (avcodec_open2(video->codec, codec, NULL) != 0) + if (avcodec_open2(video->codec, codec, &opts) != 0) #else if (avcodec_open(video->codec, codec) != 0) #endif @@ -159,14 +178,19 @@ static bool init_video(struct video_info *video, struct ffemu_params *param) return false; } +#ifdef HAVE_FFMPEG_AVCODEC_OPEN2 + if (opts) + av_dict_free(&opts); +#endif + // Allocate a big buffer :p ffmpeg API doesn't seem to give us some clues how big this buffer should be. video->outbuf_size = 1 << 23; video->outbuf = av_malloc(video->outbuf_size); - size_t size = avpicture_get_size(PIX_FMT_RGB32, param->out_width, param->out_height); + size_t size = avpicture_get_size(video->pix_fmt, param->out_width, param->out_height); video->conv_frame_buf = av_malloc(size); video->conv_frame = avcodec_alloc_frame(); - avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf, PIX_FMT_RGB32, param->out_width, param->out_height); + avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf, video->pix_fmt, param->out_width, param->out_height); return true; } @@ -214,6 +238,10 @@ static bool init_muxer(ffemu_t *handle) handle->audio.codec->flags |= CODEC_FLAG_GLOBAL_HEADER; handle->muxer.astream = stream; +#ifdef HAVE_X264RGB // Avoids a warning at end about non-monotonically increasing DTS values. It seems to be harmless to disable this. + ctx->oformat->flags |= AVFMT_TS_NONSTRICT; +#endif + #ifdef HAVE_FFMPEG_AVFORMAT_WRITE_HEADER if (avformat_write_header(ctx, NULL) < 0) #else @@ -435,7 +463,7 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_data *data) { handle->video.sws_ctx = sws_getCachedContext(handle->video.sws_ctx, data->width, data->height, handle->video.fmt, - handle->params.out_width, handle->params.out_height, PIX_FMT_RGB32, SWS_POINT, + handle->params.out_width, handle->params.out_height, handle->video.pix_fmt, SWS_POINT, NULL, NULL, NULL); int linesize = data->pitch; @@ -502,10 +530,7 @@ static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_dat pkt.size = out_size; - if (handle->audio.codec->coded_frame && handle->audio.codec->coded_frame->pts != AV_NOPTS_VALUE) - pkt.pts = av_rescale_q(handle->audio.codec->coded_frame->pts, handle->audio.codec->time_base, handle->muxer.astream->time_base); - else - pkt.pts = av_rescale_q(handle->audio.frame_cnt, handle->audio.codec->time_base, handle->muxer.astream->time_base); + pkt.pts = av_rescale_q(handle->audio.codec->coded_frame->pts, handle->audio.codec->time_base, handle->muxer.astream->time_base); pkt.flags |= AV_PKT_FLAG_KEY; handle->audio.frames_in_buffer = 0; @@ -578,24 +603,22 @@ int ffemu_finalize(ffemu_t *handle) pkt.data = handle->video.outbuf; int out_size = 0; - do + for (;;) { out_size = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, NULL); + if (out_size <= 0) + break; + pkt.pts = av_rescale_q(handle->video.codec->coded_frame->pts, handle->video.codec->time_base, handle->muxer.vstream->time_base); if (handle->video.codec->coded_frame->key_frame) pkt.flags |= AV_PKT_FLAG_KEY; pkt.size = out_size; - - if (pkt.size > 0) - { - int err = av_interleaved_write_frame(handle->muxer.ctx, &pkt); - if (err < 0) - break; - } - - } while (out_size > 0); + int err = av_interleaved_write_frame(handle->muxer.ctx, &pkt); + if (err < 0) + break; + } // Write final data. av_write_trailer(handle->muxer.ctx);