diff --git a/cores/libretro-ffmpeg/ffmpeg_core.c b/cores/libretro-ffmpeg/ffmpeg_core.c index 3a117c8566..7c4124fcc1 100644 --- a/cores/libretro-ffmpeg/ffmpeg_core.c +++ b/cores/libretro-ffmpeg/ffmpeg_core.c @@ -19,6 +19,7 @@ extern "C" { #include #include #include +#include #include #include #include @@ -76,17 +77,20 @@ static retro_environment_t CORE_PREFIX(environ_cb); static retro_input_poll_t CORE_PREFIX(input_poll_cb); static retro_input_state_t CORE_PREFIX(input_state_cb); -#define LOG_ERR(msg) do { \ - log_cb(RETRO_LOG_ERROR, "[FFmpeg]: " msg "\n"); \ -} while(0) - /* FFmpeg context data. */ static AVFormatContext *fctx; static AVCodecContext *vctx; -static int video_stream; - +static int video_stream_index; static enum AVColorSpace colorspace; +static enum AVPixelFormat pix_fmt; +#define PIX_FMT_NOT_CONFIGURED -2 + +static enum AVHWDeviceType hw_decoder; +static bool force_sw_decoder; +static int sw_decoder_threads; +static bool hw_decoding_enabled; + #define MAX_STREAMS 8 static AVCodecContext *actx[MAX_STREAMS]; static AVCodecContext *sctx[MAX_STREAMS]; @@ -99,7 +103,6 @@ static int subtitle_streams_ptr; #ifdef HAVE_SSA /* AAS/SSA subtitles. */ - static ASS_Library *ass; static ASS_Renderer *ass_render; static ASS_Track *ass_track[MAX_STREAMS]; @@ -176,7 +179,7 @@ static struct { unsigned width; unsigned height; - + double interpolate_fps; unsigned sample_rate; @@ -192,7 +195,7 @@ static void ass_msg_cb(int level, const char *fmt, va_list args, void *data) if (level < 6) { vsnprintf(buffer, sizeof(buffer), fmt, args); - log_cb(RETRO_LOG_INFO, "%s\n", buffer); + log_cb(RETRO_LOG_INFO, "[FFMPEG] %s\n", buffer); } } #endif @@ -254,7 +257,7 @@ void CORE_PREFIX(retro_get_system_av_info)(struct retro_system_av_info *info) info->timing.sample_rate = actx[0] ? media.sample_rate : 32000.0; #ifdef HAVE_GL_FFT - if (audio_streams_num > 0 && video_stream < 0) + if (audio_streams_num > 0 && video_stream_index < 0) { width = fft_width; height = fft_height; @@ -272,6 +275,9 @@ void CORE_PREFIX(retro_get_system_av_info)(struct retro_system_av_info *info) void CORE_PREFIX(retro_set_environment)(retro_environment_t cb) { static const struct retro_variable vars[] = { + { "ffmpeg_hw_decoder", "Use Hardware decoder (restart); auto|off|" + "cuda|d3d11va|drm|dxva2|mediacodec|opencl|qsv|vaapi|vdpau|videotoolbox" }, + { "ffmpeg_sw_decoder_threads", "Software decoder thread count (restart); 1|2|4|8" }, #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) { "ffmpeg_temporal_interp", "Temporal Interpolation; disabled|enabled" }, #ifdef HAVE_GL_FFT @@ -326,6 +332,8 @@ void CORE_PREFIX(retro_reset)(void) static void check_variables(void) { + struct retro_variable hw_var = {0}; + struct retro_variable sw_threads_var = {0}; struct retro_variable color_var = {0}; #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) struct retro_variable var = {0}; @@ -386,6 +394,45 @@ static void check_variables(void) colorspace = AVCOL_SPC_UNSPECIFIED; slock_unlock(decode_thread_lock); } + + hw_var.key = "ffmpeg_hw_decoder"; + + force_sw_decoder = false; + hw_decoder = AV_HWDEVICE_TYPE_NONE; + + if (CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_GET_VARIABLE, &hw_var) && hw_var.value) + { + if (string_is_equal(hw_var.value, "off")) + force_sw_decoder = true; + else if (string_is_equal(hw_var.value, "cuda")) + hw_decoder = AV_HWDEVICE_TYPE_CUDA; + else if (string_is_equal(hw_var.value, "d3d11va")) + hw_decoder = AV_HWDEVICE_TYPE_D3D11VA; + else if (string_is_equal(hw_var.value, "drm")) + hw_decoder = AV_HWDEVICE_TYPE_DRM; + else if (string_is_equal(hw_var.value, "dxva2")) + hw_decoder = AV_HWDEVICE_TYPE_DXVA2; + else if (string_is_equal(hw_var.value, "mediacodec")) + hw_decoder = AV_HWDEVICE_TYPE_MEDIACODEC; + else if (string_is_equal(hw_var.value, "opencl")) + hw_decoder = AV_HWDEVICE_TYPE_OPENCL; + else if (string_is_equal(hw_var.value, "qsv")) + hw_decoder = AV_HWDEVICE_TYPE_QSV; + else if (string_is_equal(hw_var.value, "vaapi")) + hw_decoder = AV_HWDEVICE_TYPE_VAAPI; + else if (string_is_equal(hw_var.value, "vdpau")) + hw_decoder = AV_HWDEVICE_TYPE_VDPAU; + else if (string_is_equal(hw_var.value, "videotoolbox")) + hw_decoder = AV_HWDEVICE_TYPE_VIDEOTOOLBOX; + } + + sw_threads_var.key = "ffmpeg_sw_decoder_threads"; + if (CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_GET_VARIABLE, &sw_threads_var) && sw_threads_var.value) + { + slock_lock(decode_thread_lock); + sw_decoder_threads = strtoul(sw_threads_var.value, NULL, 0); + slock_unlock(decode_thread_lock); + } } static void seek_frame(int seek_frames) @@ -410,7 +457,7 @@ static void seek_frame(int seek_frames) if (seek_frames < 0) { - log_cb(RETRO_LOG_INFO, "Resetting PTS.\n"); + log_cb(RETRO_LOG_INFO, "[FFMPEG] Resetting PTS.\n"); frames[0].pts = 0.0; frames[1].pts = 0.0; } @@ -581,7 +628,7 @@ void CORE_PREFIX(retro_run)(void) if (pts_bias < old_pts_bias - 1.0) { - log_cb(RETRO_LOG_INFO, "Resetting PTS (bias).\n"); + log_cb(RETRO_LOG_INFO, "[FFMPEG] Resetting PTS (bias).\n"); frames[0].pts = 0.0; frames[1].pts = 0.0; } @@ -596,7 +643,7 @@ void CORE_PREFIX(retro_run)(void) min_pts = frame_cnt / media.interpolate_fps + pts_bias; - if (video_stream >= 0) + if (video_stream_index >= 0) { bool dupe = true; /* unused if GL enabled */ @@ -670,7 +717,7 @@ void CORE_PREFIX(retro_run)(void) scond_signal(fifo_decode_cond); slock_unlock(fifo_lock); - frames[1].pts = av_q2d(fctx->streams[video_stream]->time_base) * pts; + frames[1].pts = av_q2d(fctx->streams[video_stream_index]->time_base) * pts; } #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) @@ -753,19 +800,147 @@ void CORE_PREFIX(retro_run)(void) CORE_PREFIX(audio_batch_cb)(audio_buffer, to_read_frames); } +/* Try to initialize a specific HW decoder defined by type */ +static enum AVPixelFormat init_hw_decoder(struct AVCodecContext *ctx, + const enum AVPixelFormat *pix_fmts, + const enum AVHWDeviceType type) +{ + int ret; + enum AVPixelFormat decoder_pix_fmt = AV_PIX_FMT_NONE; + struct AVCodec *codec = avcodec_find_decoder(fctx->streams[video_stream_index]->codec->codec_id); + + for (int i = 0;; i++) + { + const AVCodecHWConfig *config = avcodec_get_hw_config(codec, i); + if (!config) + { + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Codec %s is not supported by HW video decoder %s.\n", + codec->name, av_hwdevice_get_type_name(type)); + break; + } + if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && + config->device_type == type) + { + log_cb(RETRO_LOG_INFO, "[FFMPEG] Selected HW decoder %s.\n", + av_hwdevice_get_type_name(type)); + log_cb(RETRO_LOG_INFO, "[FFMPEG] Selected HW pixel format %s.\n", + av_get_pix_fmt_name(config->pix_fmt)); + + enum AVPixelFormat device_pix_fmt = config->pix_fmt; + + /* Look if codec can supports the pix format of the device */ + const enum AVPixelFormat *p; + for (p = pix_fmts; *p != -1; p++) + { + if (*p == device_pix_fmt) + { + decoder_pix_fmt = *p; + goto exit; + } + } + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Codec %s does not support device pixel format %s.\n", + codec->name, av_get_pix_fmt_name(config->pix_fmt)); + } + } + +exit: + if (decoder_pix_fmt != AV_PIX_FMT_NONE) + { + AVBufferRef *hw_device_ctx; + if ((ret = av_hwdevice_ctx_create(&hw_device_ctx, + type, NULL, NULL, 0)) < 0) + { + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Failed to create specified HW device: %s\n", av_err2str(ret)); + decoder_pix_fmt = AV_PIX_FMT_NONE; + } else { + ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); + } + } + + return decoder_pix_fmt; +} + +/* Automatically try to find a suitable HW decoder */ +static enum AVPixelFormat auto_hw_decoder(AVCodecContext *ctx, + const enum AVPixelFormat *pix_fmts) +{ + int ret; + enum AVPixelFormat decoder_pix_fmt = AV_PIX_FMT_NONE; + enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE; + + while((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) + { + decoder_pix_fmt = init_hw_decoder(ctx, pix_fmts, type); + if (decoder_pix_fmt != AV_PIX_FMT_NONE) + break; + } + + return decoder_pix_fmt; +} + +/* Callback used by ffmpeg to configure the pixelformat to use. + * Used to initialize hw decoding if configured and accessible. + */ +static enum AVPixelFormat get_format(AVCodecContext *ctx, + const enum AVPixelFormat *pix_fmts) +{ + /* + * We only need to update the pixel format once new content has been loaded. + * This also means that you need to reload the core for changing the sw thread + * core count and decoder backend. + */ + if (pix_fmt == PIX_FMT_NOT_CONFIGURED) + { + pix_fmt = AV_PIX_FMT_NONE; + + if (!force_sw_decoder) + { + if (hw_decoder == AV_HWDEVICE_TYPE_NONE) + { + pix_fmt = auto_hw_decoder(ctx, pix_fmts); + } + else + { + pix_fmt = init_hw_decoder(ctx, pix_fmts, hw_decoder); + } + } + + /* Fallback to SW rendering */ + if (pix_fmt == AV_PIX_FMT_NONE) + { + log_cb(RETRO_LOG_INFO, "[FFMPEG] Using SW decoding.\n"); + + ctx->thread_type = FF_THREAD_FRAME; + ctx->thread_count = sw_decoder_threads; + + pix_fmt = fctx->streams[video_stream_index]->codec->pix_fmt; + hw_decoding_enabled = false; + } + else + hw_decoding_enabled = true; + } + + return pix_fmt; +} + static bool open_codec(AVCodecContext **ctx, unsigned index) { - AVCodec *codec = avcodec_find_decoder(fctx->streams[index]->codec->codec_id); + int ret; + AVCodec *codec = avcodec_find_decoder(fctx->streams[index]->codec->codec_id); if (!codec) { - log_cb(RETRO_LOG_ERROR, "Couldn't find suitable decoder, exiting ... \n"); + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Couldn't find suitable decoder\n"); return false; } *ctx = fctx->streams[index]->codec; - if (avcodec_open2(*ctx, codec, NULL) < 0) + + if ((ret = avcodec_open2(*ctx, codec, NULL)) < 0) + { + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Could not open codec: %s\n", av_err2str(ret)); return false; + } return true; } @@ -831,10 +1006,9 @@ static bool codec_id_is_ass(enum AVCodecID id) static bool open_codecs(void) { unsigned i; - decode_thread_lock = slock_new(); - video_stream = -1; + video_stream_index = -1; audio_streams_num = 0; subtitle_streams_num = 0; @@ -866,7 +1040,9 @@ static bool open_codecs(void) { if (!open_codec(&vctx, i)) return false; - video_stream = i; + pix_fmt = PIX_FMT_NOT_CONFIGURED; + vctx->get_format = get_format; + video_stream_index = i; } break; @@ -994,118 +1170,6 @@ static void set_colorspace(struct SwsContext *sws, } } -static bool decode_video(AVPacket *pkt, AVFrame *frame, - AVFrame *conv, struct SwsContext *sws) -{ - int got_ptr = 0; - int ret = avcodec_decode_video2(vctx, frame, &got_ptr, pkt); - - if (ret < 0) - return false; - - if (got_ptr) - { - set_colorspace(sws, media.width, media.height, - av_frame_get_colorspace(frame), av_frame_get_color_range(frame)); - sws_scale(sws, (const uint8_t * const*)frame->data, - frame->linesize, 0, media.height, - conv->data, conv->linesize); - return true; - } - - return false; -} - -static int16_t *decode_audio(AVCodecContext *ctx, AVPacket *pkt, - AVFrame *frame, int16_t *buffer, size_t *buffer_cap, - SwrContext *swr) -{ - AVPacket pkt_tmp = *pkt; - int got_ptr = 0; - - for (;;) - { - int64_t pts; - size_t required_buffer; - int ret = avcodec_decode_audio4(ctx, frame, &got_ptr, &pkt_tmp); - - if (ret < 0) - return buffer; - - pkt_tmp.data += ret; - pkt_tmp.size -= ret; - - if (!got_ptr) - break; - - required_buffer = frame->nb_samples * sizeof(int16_t) * 2; - if (required_buffer > *buffer_cap) - { - buffer = (int16_t*)av_realloc(buffer, required_buffer); - *buffer_cap = required_buffer; - } - - swr_convert(swr, - (uint8_t**)&buffer, - frame->nb_samples, - (const uint8_t**)frame->data, - frame->nb_samples); - - pts = av_frame_get_best_effort_timestamp(frame); - slock_lock(fifo_lock); - - while (!decode_thread_dead && fifo_write_avail(audio_decode_fifo) < required_buffer) - { - if (!main_sleeping) - scond_wait(fifo_decode_cond, fifo_lock); - else - { - log_cb(RETRO_LOG_ERROR, "Thread: Audio deadlock detected ...\n"); - fifo_clear(audio_decode_fifo); - break; - } - } - - decode_last_audio_time = pts * av_q2d( - fctx->streams[audio_streams[audio_streams_ptr]]->time_base); - - if (!decode_thread_dead) - fifo_write(audio_decode_fifo, buffer, required_buffer); - - scond_signal(fifo_cond); - slock_unlock(fifo_lock); - } - - return buffer; -} - -static void decode_thread_seek(double time) -{ - int ret; - int64_t seek_to = time * AV_TIME_BASE; - - if (seek_to < 0) - seek_to = 0; - - decode_last_video_time = time; - decode_last_audio_time = time; - - ret = avformat_seek_file(fctx, -1, INT64_MIN, seek_to, INT64_MAX, 0); - if (ret < 0) - log_cb(RETRO_LOG_ERROR, "av_seek_frame() failed.\n"); - - if (actx[audio_streams_ptr]) - avcodec_flush_buffers(actx[audio_streams_ptr]); - if (vctx) - avcodec_flush_buffers(vctx); - if (sctx[subtitle_streams_ptr]) - avcodec_flush_buffers(sctx[subtitle_streams_ptr]); -#ifdef HAVE_SSA - if (ass_track[subtitle_streams_ptr]) - ass_flush_events(ass_track[subtitle_streams_ptr]); -#endif -} - #ifdef HAVE_SSA /* Straight CPU alpha blending. * Should probably do in GL. */ @@ -1157,27 +1221,237 @@ static void render_ass_img(AVFrame *conv_frame, ASS_Image *img) } #endif +static void decode_video(AVCodecContext *ctx, AVPacket *pkt, AVFrame *conv_frame, size_t frame_size, struct SwsContext **sws, ASS_Track *ass_track_active) +{ + int ret; + AVFrame *frame = NULL; + AVFrame *sw_frame = NULL; + AVFrame *tmp_frame = NULL; + + if ((ret = avcodec_send_packet(ctx, pkt)) < 0) + { + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Can't decode video packet: %s\n", av_err2str(ret)); + return; + } + + while(true) + { + if (!(frame = av_frame_alloc()) || !(sw_frame = av_frame_alloc())) + { + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Can not alloc frames\n"); + return; + } + + ret = avcodec_receive_frame(ctx, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + { + av_frame_free(&frame); + av_frame_free(&sw_frame); + break; + } + else if (ret < 0) + { + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Error while reading video frame: %s\n", av_err2str(ret)); + goto fail; + } + + if (hw_decoding_enabled) + { + /* Copy data from VRAM to RAM */ + if ((ret = av_hwframe_transfer_data(sw_frame, frame, 0)) < 0) + { + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Error transferring the data to system memory: %s\n", av_err2str(ret)); + goto fail; + } + tmp_frame = sw_frame; + } + else + tmp_frame = frame; + + *sws = sws_getCachedContext(*sws, + media.width, media.height, tmp_frame->format, + media.width, media.height, PIX_FMT_RGB32, + SWS_BICUBIC, NULL, NULL, NULL); + + set_colorspace(*sws, media.width, media.height, + av_frame_get_colorspace(tmp_frame), av_frame_get_color_range(tmp_frame)); + + if ((ret = sws_scale(*sws, (const uint8_t * const*)tmp_frame->data, + tmp_frame->linesize, 0, media.height, + conv_frame->data, conv_frame->linesize)) < 0) + { + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Error while scaling image: %s\n", av_err2str(ret)); + goto fail; + } + + size_t decoded_size; + int64_t pts = av_frame_get_best_effort_timestamp(frame); + double video_time = pts * av_q2d(fctx->streams[video_stream_index]->time_base); + +#ifdef HAVE_SSA + if (ass_render && ass_track_active) + { + int change = 0; + ASS_Image *img = ass_render_frame(ass_render, ass_track_active, + 1000 * video_time, &change); + + /* Do it on CPU for now. + * We're in a thread anyways, so shouldn't really matter. */ + render_ass_img(conv_frame, img); + } +#endif + + decoded_size = frame_size + sizeof(pts); + slock_lock(fifo_lock); + + while (!decode_thread_dead && (video_decode_fifo != NULL) + && fifo_write_avail(video_decode_fifo) < decoded_size) + { + if (!main_sleeping) + scond_wait(fifo_decode_cond, fifo_lock); + else + { + fifo_clear(video_decode_fifo); + break; + } + } + + decode_last_video_time = video_time; + if (!decode_thread_dead) + { + int stride; + unsigned y; + const uint8_t *src = NULL; + + fifo_write(video_decode_fifo, &pts, sizeof(pts)); + src = conv_frame->data[0]; + stride = conv_frame->linesize[0]; + + for (y = 0; y < media.height; y++, src += stride) + fifo_write(video_decode_fifo, src, media.width * sizeof(uint32_t)); + } + scond_signal(fifo_cond); + slock_unlock(fifo_lock); + + fail: + av_frame_free(&frame); + av_frame_free(&sw_frame); + if (ret < 0) + break; + } + + return; +} + +static int16_t *decode_audio(AVCodecContext *ctx, AVPacket *pkt, + AVFrame *frame, int16_t *buffer, size_t *buffer_cap, + SwrContext *swr) +{ + int ret; + int64_t pts; + size_t required_buffer; + + if ((ret = avcodec_send_packet(ctx, pkt)) < 0) + { + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Can't decode audio packet: %s\n", av_err2str(ret)); + return buffer; + } + + while(true) + { + ret = avcodec_receive_frame(ctx, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + { + break; + } + else if (ret < 0) + { + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Error while reading audio frame: %s\n", av_err2str(ret)); + break; + } + + required_buffer = frame->nb_samples * sizeof(int16_t) * 2; + if (required_buffer > *buffer_cap) + { + buffer = (int16_t*)av_realloc(buffer, required_buffer); + *buffer_cap = required_buffer; + } + + swr_convert(swr, + (uint8_t**)&buffer, + frame->nb_samples, + (const uint8_t**)frame->data, + frame->nb_samples); + + pts = av_frame_get_best_effort_timestamp(frame); + slock_lock(fifo_lock); + + while (!decode_thread_dead && fifo_write_avail(audio_decode_fifo) < required_buffer) + { + if (!main_sleeping) + scond_wait(fifo_decode_cond, fifo_lock); + else + { + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Thread: Audio deadlock detected.\n"); + fifo_clear(audio_decode_fifo); + break; + } + } + + decode_last_audio_time = pts * av_q2d( + fctx->streams[audio_streams[audio_streams_ptr]]->time_base); + + if (!decode_thread_dead) + fifo_write(audio_decode_fifo, buffer, required_buffer); + + scond_signal(fifo_cond); + slock_unlock(fifo_lock); + } + + return buffer; +} + + +static void decode_thread_seek(double time) +{ + int64_t seek_to = time * AV_TIME_BASE; + + if (seek_to < 0) + seek_to = 0; + + decode_last_video_time = time; + decode_last_audio_time = time; + + if(avformat_seek_file(fctx, -1, INT64_MIN, seek_to, INT64_MAX, 0) < 0) + log_cb(RETRO_LOG_ERROR, "[FFMPEG] av_seek_frame() failed.\n"); + + if (actx[audio_streams_ptr]) + avcodec_flush_buffers(actx[audio_streams_ptr]); + if (vctx) + avcodec_flush_buffers(vctx); + if (sctx[subtitle_streams_ptr]) + avcodec_flush_buffers(sctx[subtitle_streams_ptr]); +#ifdef HAVE_SSA + if (ass_track[subtitle_streams_ptr]) + ass_flush_events(ass_track[subtitle_streams_ptr]); +#endif +} + static void decode_thread(void *data) { unsigned i; - SwrContext *swr[audio_streams_num]; + struct SwrContext *swr[audio_streams_num]; + struct SwsContext *sws = NULL; AVFrame *aud_frame = NULL; - AVFrame *vid_frame = NULL; void *conv_frame_buf = NULL; size_t frame_size = 0; int16_t *audio_buffer = NULL; size_t audio_buffer_cap = 0; AVFrame *conv_frame = NULL; - struct SwsContext *sws = NULL; + (void)data; - if (video_stream >= 0) - sws = sws_getCachedContext(NULL, - media.width, media.height, vctx->pix_fmt, - media.width, media.height, PIX_FMT_RGB32, - SWS_POINT, NULL, NULL, NULL); - for (i = 0; (int)i < audio_streams_num; i++) { swr[i] = swr_alloc(); @@ -1192,9 +1466,8 @@ static void decode_thread(void *data) } aud_frame = av_frame_alloc(); - vid_frame = av_frame_alloc(); - if (video_stream >= 0) + if (video_stream_index >= 0) { frame_size = avpicture_get_size(PIX_FMT_RGB32, media.width, media.height); conv_frame = av_frame_alloc(); @@ -1253,65 +1526,13 @@ static void decode_thread(void *data) #endif slock_unlock(decode_thread_lock); - if (pkt.stream_index == video_stream) - { - if (decode_video(&pkt, vid_frame, conv_frame, sws)) - { - size_t decoded_size; - int64_t pts = av_frame_get_best_effort_timestamp(vid_frame); - double video_time = pts * av_q2d(fctx->streams[video_stream]->time_base); - -#ifdef HAVE_SSA - if (ass_render && ass_track_active) - { - int change = 0; - ASS_Image *img = ass_render_frame(ass_render, ass_track_active, - 1000 * video_time, &change); - - /* Do it on CPU for now. - * We're in a thread anyways, so shouldn't really matter. */ - render_ass_img(conv_frame, img); - } -#endif - - decoded_size = frame_size + sizeof(pts); - slock_lock(fifo_lock); - - while (!decode_thread_dead && (video_decode_fifo != NULL) - && fifo_write_avail(video_decode_fifo) < decoded_size) - { - if (!main_sleeping) - scond_wait(fifo_decode_cond, fifo_lock); - else - { - fifo_clear(video_decode_fifo); - break; - } - } - - decode_last_video_time = video_time; - if (!decode_thread_dead) - { - int stride; - unsigned y; - const uint8_t *src = NULL; - - fifo_write(video_decode_fifo, &pts, sizeof(pts)); - src = conv_frame->data[0]; - stride = conv_frame->linesize[0]; - - for (y = 0; y < media.height; y++, src += stride) - fifo_write(video_decode_fifo, src, media.width * sizeof(uint32_t)); - } - scond_signal(fifo_cond); - slock_unlock(fifo_lock); - } - } + if (pkt.stream_index == video_stream_index) + decode_video(vctx, &pkt, conv_frame, frame_size, &sws, ass_track_active); else if (pkt.stream_index == audio_stream && actx_active) { audio_buffer = decode_audio(actx_active, &pkt, aud_frame, - audio_buffer, &audio_buffer_cap, - swr[audio_stream_ptr]); + audio_buffer, &audio_buffer_cap, + swr[audio_stream_ptr]); } else if (pkt.stream_index == subtitle_stream && sctx_active) { @@ -1324,7 +1545,7 @@ static void decode_thread(void *data) { if (avcodec_decode_subtitle2(sctx_active, &sub, &finished, &pkt) < 0) { - log_cb(RETRO_LOG_ERROR, "Decode subtitles failed.\n"); + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Decode subtitles failed.\n"); break; } } @@ -1344,15 +1565,15 @@ static void decode_thread(void *data) av_free_packet(&pkt); } - if (sws) - sws_freeContext(sws); - sws = NULL; + sws_freeContext(sws); for (i = 0; (int)i < audio_streams_num; i++) swr_free(&swr[i]); + if (vctx->hw_device_ctx) + av_buffer_unref(&vctx->hw_device_ctx); + av_frame_free(&aud_frame); - av_frame_free(&vid_frame); av_frame_free(&conv_frame); av_freep(&conv_frame_buf); av_freep(&audio_buffer); @@ -1398,7 +1619,7 @@ static void context_reset(void) unsigned i; #ifdef HAVE_GL_FFT - if (audio_streams_num > 0 && video_stream < 0) + if (audio_streams_num > 0 && video_stream_index < 0) { fft = fft_new(11, hw_render.get_proc_address); if (fft) @@ -1555,8 +1776,10 @@ void CORE_PREFIX(retro_unload_game)(void) bool CORE_PREFIX(retro_load_game)(const struct retro_game_info *info) { + int ret; bool is_fft = false; enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888; + struct retro_input_descriptor desc[] = { { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Seek -10 seconds" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Seek +60 seconds" }, @@ -1575,41 +1798,42 @@ bool CORE_PREFIX(retro_load_game)(const struct retro_game_info *info) if (!CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) { - LOG_ERR("Cannot set pixel format."); + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Cannot set pixel format."); goto error; } - if (avformat_open_input(&fctx, info->path, NULL, NULL) < 0) + if ((ret = avformat_open_input(&fctx, info->path, NULL, NULL)) < 0) { - LOG_ERR("Failed to open input."); + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Failed to open input: %s\n", av_err2str(ret)); goto error; } - if (avformat_find_stream_info(fctx, NULL) < 0) + if ((ret = avformat_find_stream_info(fctx, NULL)) < 0) { - LOG_ERR("Failed to find stream info."); + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Failed to find stream info: %s\n", av_err2str(ret)); goto error; } + log_cb(RETRO_LOG_INFO, "[FFMPEG] Media information:\n"); av_dump_format(fctx, 0, info->path, 0); if (!open_codecs()) { - LOG_ERR("Failed to find codec."); + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Failed to find codec."); goto error; } if (!init_media_info()) { - LOG_ERR("Failed to init media info."); + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Failed to init media info."); goto error; } #ifdef HAVE_GL_FFT - is_fft = video_stream < 0 && audio_streams_num > 0; + is_fft = video_stream_index < 0 && audio_streams_num > 0; #endif - if (video_stream >= 0 || is_fft) + if (video_stream_index >= 0 || is_fft) { video_decode_fifo = fifo_new(media.width * media.height * sizeof(uint32_t) * 32); @@ -1629,13 +1853,13 @@ bool CORE_PREFIX(retro_load_game)(const struct retro_game_info *info) if (!CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render)) { use_gl = false; - LOG_ERR("Cannot initialize HW render."); + log_cb(RETRO_LOG_ERROR, "[FFMPEG] Cannot initialize HW render."); } #endif } if (audio_streams_num > 0) { - unsigned buffer_seconds = video_stream >= 0 ? 20 : 1; + unsigned buffer_seconds = video_stream_index >= 0 ? 20 : 1; audio_decode_fifo = fifo_new(buffer_seconds * media.sample_rate * sizeof(int16_t) * 2); }