Created
August 18, 2011 18:12
-
-
Save Themaister/1154721 to your computer and use it in GitHub Desktop.
BSV demuxer for FFmpeg
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/configure b/configure | |
index 26f6d21..06279f5 100755 | |
--- a/configure | |
+++ b/configure | |
@@ -160,6 +160,7 @@ Configuration options: | |
External library support: | |
--enable-avisynth enable reading of AVISynth script files [no] | |
+ --enable-bsv enable reading of BSNES movie files [no] | |
--enable-bzlib enable bzlib [autodetect] | |
--enable-libcelt enable CELT/Opus decoding via libcelt [no] | |
--enable-frei0r enable frei0r video filtering | |
@@ -910,6 +911,7 @@ CONFIG_LIST=" | |
avfilter | |
avformat | |
avisynth | |
+ bsv | |
bzlib | |
crystalhd | |
dct | |
@@ -1438,6 +1440,8 @@ libxvid_encoder_deps="libxvid" | |
ac3_demuxer_select="ac3_parser" | |
asf_stream_muxer_select="asf_muxer" | |
avisynth_demuxer_deps="avisynth" | |
+bsv_demuxer_deps="bsv" | |
+bsv_demuxer_extralibs="-lsnes" | |
dirac_demuxer_select="dirac_parser" | |
eac3_demuxer_select="ac3_parser" | |
flac_demuxer_select="flac_parser" | |
@@ -2906,6 +2910,7 @@ check_mathfunc truncf | |
# these are off by default, so fail if requested and not available | |
enabled avisynth && require2 vfw32 "windows.h vfw.h" AVIFileInit -lavifil32 | |
+enabled bsv && { check_header libsnes.hpp || die "ERROR: libsnes was not found!"; } | |
enabled libcelt && require libcelt celt/celt.h celt_decode -lcelt0 | |
enabled frei0r && { check_header frei0r.h || die "ERROR: frei0r.h header not found"; } | |
enabled libdc1394 && require_pkg_config libdc1394-2 dc1394/dc1394.h dc1394_new | |
@@ -3220,6 +3225,7 @@ echo "libdxva2 enabled ${dxva2-no}" | |
echo "libva enabled ${vaapi-no}" | |
echo "libvdpau enabled ${vdpau-no}" | |
echo "AVISynth enabled ${avisynth-no}" | |
+echo "BSV enabled ${bsv-no}" | |
echo "libcelt enabled ${libcelt-no}" | |
echo "frei0r enabled ${frei0r-no}" | |
echo "libdc1394 support ${libdc1394-no}" | |
diff --git a/libavformat/Makefile b/libavformat/Makefile | |
index d597c62..8e48fd6 100644 | |
--- a/libavformat/Makefile | |
+++ b/libavformat/Makefile | |
@@ -41,6 +41,7 @@ OBJS-$(CONFIG_AU_MUXER) += au.o | |
OBJS-$(CONFIG_AVI_DEMUXER) += avidec.o riff.o avi.o | |
OBJS-$(CONFIG_AVI_MUXER) += avienc.o riff.o avi.o | |
OBJS-$(CONFIG_AVISYNTH) += avisynth.o | |
+OBJS-$(CONFIG_BSV) += bsv.o | |
OBJS-$(CONFIG_AVM2_MUXER) += swfenc.o | |
OBJS-$(CONFIG_AVS_DEMUXER) += avs.o vocdec.o voc.o | |
OBJS-$(CONFIG_BETHSOFTVID_DEMUXER) += bethsoftvid.o | |
diff --git a/libavformat/allformats.c b/libavformat/allformats.c | |
index c64c355..d6a5713 100644 | |
--- a/libavformat/allformats.c | |
+++ b/libavformat/allformats.c | |
@@ -70,6 +70,7 @@ void av_register_all(void) | |
REGISTER_DEMUXER (BETHSOFTVID, bethsoftvid); | |
REGISTER_DEMUXER (BFI, bfi); | |
REGISTER_DEMUXER (BINK, bink); | |
+ REGISTER_DEMUXER (BSV, bsv); | |
REGISTER_DEMUXER (C93, c93); | |
REGISTER_MUXDEMUX (CAF, caf); | |
REGISTER_MUXDEMUX (CAVSVIDEO, cavsvideo); | |
diff --git a/libavformat/bsv.c b/libavformat/bsv.c | |
new file mode 100644 | |
index 0000000..e9d8e46 | |
--- /dev/null | |
+++ b/libavformat/bsv.c | |
@@ -0,0 +1,482 @@ | |
+/* | |
+ * BSNES movie demuxer | |
+ * Copyright (C) 2011 Hans-Kristian Arntzen | |
+ * | |
+ * This file is part of FFmpeg. | |
+ * | |
+ * FFmpeg is free software; you can redistribute it and/or | |
+ * modify it under the terms of the GNU Lesser General Public | |
+ * License as published by the Free Software Foundation; either | |
+ * version 2.1 of the License, or (at your option) any later version. | |
+ * | |
+ * FFmpeg is distributed in the hope that it will be useful, | |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
+ * Lesser General Public License for more details. | |
+ * | |
+ * You should have received a copy of the GNU Lesser General Public | |
+ * License along with FFmpeg; if not, write to the Free Software | |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
+ */ | |
+ | |
+#include "avformat.h" | |
+#include <libavutil/avstring.h> | |
+#include <libavutil/log.h> | |
+#include <stdio.h> | |
+#include <stdbool.h> | |
+#include <libsnes.hpp> | |
+#include <string.h> | |
+ | |
+// CRC32 implementation taken from BSNES source :) | |
+ | |
+static const uint32_t crc32_table[256] = { | |
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, | |
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, | |
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, | |
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, | |
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, | |
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, | |
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, | |
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, | |
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, | |
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, | |
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, | |
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, | |
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, | |
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, | |
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, | |
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, | |
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, | |
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, | |
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, | |
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, | |
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, | |
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, | |
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, | |
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, | |
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, | |
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, | |
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, | |
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, | |
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, | |
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, | |
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, | |
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, | |
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, | |
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, | |
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, | |
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, | |
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, | |
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, | |
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, | |
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, | |
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, | |
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, | |
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d | |
+}; | |
+ | |
+static uint32_t crc32_adjust(uint32_t crc32, uint8_t input) | |
+{ | |
+ return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; | |
+} | |
+ | |
+static uint32_t crc32_calculate(const uint8_t *data, unsigned length) | |
+{ | |
+ unsigned i; | |
+ uint32_t crc32 = ~0; | |
+ for(i = 0; i < length; i++) | |
+ crc32 = crc32_adjust(crc32, data[i]); | |
+ return ~crc32; | |
+} | |
+ | |
+#define SNES_WIDTH 512 | |
+#define SNES_HEIGHT 448 | |
+#define SNES_ASPECT_RATIO ((double)SNES_WIDTH / (double)SNES_HEIGHT) | |
+#define SNES_PITCH 1024 // These are measured in pixels, not bytes. | |
+#define SNES_INTERLACED_PITCH 512 | |
+ | |
+// libsnes allows only one instance of itself. | |
+typedef struct | |
+{ | |
+ AVIOContext *ioctx; | |
+ | |
+ bool video_ready; | |
+ uint16_t *video_buf; | |
+ unsigned frame_cnt; | |
+ | |
+ bool audio_ready; | |
+ int16_t *audio_buf; | |
+ unsigned buf_cnt; | |
+ unsigned pts_cnt; | |
+} BSVContext; | |
+ | |
+static bool g_is_instanced = false; | |
+static BSVContext *g_ctx = NULL; | |
+ | |
+// Double every pixel as they come along ... | |
+static void stretch_line(void * restrict out_, const uint16_t * restrict in) | |
+{ | |
+ unsigned i; | |
+ uint32_t *out = out_; | |
+ | |
+ for (i = 0; i < SNES_WIDTH / 2; i++) | |
+ { | |
+ uint32_t pix = in[i]; | |
+ pix |= pix << 16; | |
+ out[i] = pix; | |
+ } | |
+} | |
+ | |
+static void video_frame(const uint16_t *data, unsigned width, | |
+ unsigned height) // TODO | |
+{ | |
+ unsigned y; | |
+ | |
+ // Crop, so we can scale at perfect 2x. SNES frames can be one of these resolutions: | |
+ // Widths: 256, 512 | |
+ // Heights: 224, 239, 448, 478 | |
+ if (height == 239) | |
+ { | |
+ data += 7 * SNES_PITCH; // Skip 7 top scanlines. | |
+ height = 224; | |
+ } | |
+ else if (height == SNES_INTERLACED_PITCH) | |
+ { | |
+ data += 15 * 512; // Skip 15 top scanlines. | |
+ height = 448; | |
+ } | |
+ | |
+ if (width == 256 && height == 224) // Regular frame. | |
+ { | |
+ for (y = 0; y < height; y++) | |
+ { | |
+ stretch_line(g_ctx->video_buf + 2 * y * SNES_WIDTH, data + y * SNES_PITCH); | |
+ memcpy(g_ctx->video_buf + (2 * y + 1) * SNES_WIDTH, g_ctx->video_buf + 2 * y * SNES_WIDTH, SNES_WIDTH * sizeof(uint16_t)); | |
+ } | |
+ } | |
+ else if (width == 256 && height == 448) // Interlaced, do not dupe scanlines. | |
+ { | |
+ for (y = 0; y < height; y++) | |
+ stretch_line(g_ctx->video_buf + y * SNES_WIDTH, data + y * SNES_INTERLACED_PITCH); | |
+ } | |
+ else if (width == 512 && height == 224) // Pseudo-hires. | |
+ { | |
+ for (y = 0; y < height; y++) | |
+ { | |
+ memcpy(g_ctx->video_buf + (2 * y + 0) * SNES_WIDTH, data + y * SNES_PITCH, SNES_WIDTH * sizeof(uint16_t)); | |
+ memcpy(g_ctx->video_buf + (2 * y + 1) * SNES_WIDTH, data + y * SNES_PITCH, SNES_WIDTH * sizeof(uint16_t)); | |
+ } | |
+ } | |
+ else if (width == 512 && height == 448) // Pseudo-hires interlaced. | |
+ { | |
+ for (y = 0; y < height; y++) | |
+ memcpy(g_ctx->video_buf + y * SNES_WIDTH, data + y * SNES_INTERLACED_PITCH, SNES_WIDTH * sizeof(uint16_t)); | |
+ } | |
+ else // Some odd frame. Just set everything to black. | |
+ memset(g_ctx->video_buf, 0, SNES_WIDTH * SNES_HEIGHT * sizeof(uint16_t)); | |
+ | |
+ g_ctx->video_ready = true; | |
+ g_ctx->frame_cnt++; | |
+} | |
+ | |
+static void audio_sample(uint16_t left, uint16_t right) | |
+{ | |
+ g_ctx->audio_buf[g_ctx->buf_cnt++] = left; | |
+ g_ctx->audio_buf[g_ctx->buf_cnt++] = right; | |
+ g_ctx->pts_cnt++; | |
+ g_ctx->audio_ready = true; | |
+} | |
+ | |
+static void input_poll(void) | |
+{} | |
+ | |
+// EOF-handling is performed per-frame basis. | |
+static int16_t input_state(bool port, unsigned device, unsigned index, unsigned id) | |
+{ | |
+ (void)port; | |
+ (void)device; | |
+ (void)index; | |
+ (void)id; | |
+ return avio_rl16(g_ctx->ioctx); | |
+} | |
+ | |
+#define BSV_MAGIC 0x42535631 | |
+#define MAGIC_INDEX 0 | |
+#define SERIALIZER_INDEX 1 | |
+#define CRC_INDEX 2 | |
+#define STATE_SIZE_INDEX 3 | |
+ | |
+// Somewhat hacky, we need to find the corresponding *.sfc file ... | |
+static char g_game_path[1024]; | |
+ | |
+static int bsv_probe(AVProbeData *p) | |
+{ | |
+ char *ptr; | |
+ uint32_t magic; | |
+ | |
+ if (!g_is_instanced) | |
+ { | |
+ av_strlcpy(g_game_path, p->filename, sizeof(g_game_path)); | |
+ ptr = strrchr(g_game_path, '.'); | |
+ if (ptr) | |
+ *ptr = '\0'; | |
+ | |
+ av_strlcat(g_game_path, ".sfc", sizeof(g_game_path)); | |
+ } | |
+ | |
+ magic = 0; | |
+ for (unsigned i = 0; i < 4; i++) | |
+ magic |= p->buf[i] << (i << 3); | |
+ | |
+ if (magic == BSV_MAGIC) | |
+ return AVPROBE_SCORE_MAX; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static bool init_video_stream(AVStream *st) | |
+{ | |
+ AVCodecContext *ctx = st->codec; | |
+ | |
+ ctx->codec_type = AVMEDIA_TYPE_VIDEO; | |
+ ctx->width = SNES_WIDTH; | |
+ ctx->height = SNES_HEIGHT; | |
+ ctx->codec_id = CODEC_ID_RAWVIDEO; | |
+#if HAVE_BIGENDIAN | |
+ ctx->pix_fmt = PIX_FMT_RGB555BE; | |
+#else | |
+ ctx->pix_fmt = PIX_FMT_RGB555LE; | |
+#endif | |
+ st->sample_aspect_ratio = av_d2q((4.0 / 3.0) / (SNES_ASPECT_RATIO), 255); | |
+ av_set_pts_info(st, 64, 1, snes_get_region() == SNES_REGION_NTSC ? 60 : 50); | |
+ | |
+ return true; | |
+} | |
+ | |
+// Init the audio stream for now ... | |
+static bool init_audio_stream(AVStream *st) | |
+{ | |
+ AVCodecContext *ctx = st->codec; | |
+ st->start_time = 0; | |
+ | |
+ ctx->sample_rate = 32000; | |
+ ctx->codec_type = AVMEDIA_TYPE_AUDIO; | |
+#if HAVE_BIGENDIAN | |
+ ctx->codec_id = CODEC_ID_PCM_S16BE; | |
+#else | |
+ ctx->codec_id = CODEC_ID_PCM_S16LE; | |
+#endif | |
+ ctx->channels = 2; | |
+ ctx->bits_per_coded_sample = 16; | |
+ ctx->frame_size = 4; | |
+ ctx->block_align = 4; | |
+ ctx->bit_rate = ctx->sample_rate * ctx->frame_size * 8; | |
+ | |
+ av_set_pts_info(st, 64, 1, ctx->sample_rate); | |
+ return true; | |
+} | |
+ | |
+static int bsv_header(AVFormatContext *s, | |
+ AVFormatParameters *ap) | |
+{ | |
+ BSVContext *ctx; | |
+ AVStream *audio_stream, *video_stream; | |
+ AVIOContext *pb; | |
+ FILE *file; | |
+ long len; | |
+ void *buf; | |
+ void *state_buf; | |
+ uint32_t magic, state_size; | |
+ uint32_t bsv_crc, rom_crc; | |
+ | |
+ if (g_is_instanced) | |
+ return AVERROR(ENOMEM); | |
+ | |
+ ctx = s->priv_data; | |
+ g_ctx = ctx; | |
+ g_is_instanced = true; | |
+ | |
+ video_stream = av_new_stream(s, 0); | |
+ if (!video_stream) | |
+ return AVERROR(ENOMEM); | |
+ | |
+ audio_stream = av_new_stream(s, 1); | |
+ if (!audio_stream) | |
+ return AVERROR(ENOMEM); | |
+ | |
+ pb = s->pb; | |
+ ctx->ioctx = pb; | |
+ | |
+ avio_seek(pb, 0, SEEK_SET); | |
+ magic = avio_rl32(pb); | |
+ // TODO: Should check serializer, but libsnes doesn't deal with this yet ... Dummy read :) | |
+ avio_rl32(pb); | |
+ bsv_crc = avio_rl32(pb); | |
+ state_size = avio_rl32(pb); | |
+ | |
+ if (magic != BSV_MAGIC) | |
+ { | |
+ av_log(s, AV_LOG_ERROR, "BSV header is invalid! Cannot continue ...\n"); | |
+ return AVERROR_INVALIDDATA; | |
+ } | |
+ | |
+ ctx->audio_buf = av_malloc(4096); | |
+ if (!ctx->audio_buf) | |
+ return AVERROR(ENOMEM); | |
+ ctx->video_buf = av_malloc(SNES_WIDTH * SNES_HEIGHT * sizeof(uint16_t) * 2); | |
+ if (!ctx->video_buf) | |
+ return AVERROR(ENOMEM); | |
+ | |
+ snes_init(); | |
+ snes_set_video_refresh(video_frame); | |
+ snes_set_audio_sample(audio_sample); | |
+ snes_set_input_poll(input_poll); | |
+ snes_set_input_state(input_state); | |
+ | |
+ // TODO: Should we use avio for something like this? | |
+ file = fopen(g_game_path, "rb"); // Should be implied by BSV path or something. | |
+ if (!file) | |
+ { | |
+ av_log(s, AV_LOG_ERROR, "Looking for SNES ROM \"%s\", but could not find it. Aborting ...\n", g_game_path); | |
+ return AVERROR_INVALIDDATA; | |
+ } | |
+ | |
+ fseek(file, 0, SEEK_END); | |
+ len = ftell(file); | |
+ rewind(file); | |
+ buf = av_malloc(len); | |
+ if (!buf) | |
+ return AVERROR(ENOMEM); | |
+ | |
+ if (fread(buf, 1, len, file) < len) | |
+ { | |
+ av_free(buf); | |
+ fclose(file); | |
+ return AVERROR(ENOMEM); | |
+ } | |
+ | |
+ fclose(file); | |
+ | |
+ rom_crc = crc32_calculate(buf, len); | |
+ if (rom_crc != bsv_crc) | |
+ { | |
+ av_log(s, AV_LOG_ERROR, "CRC32 for ROM does not correspond with BSV: (ROM = %x, BSV = %x). Cannot continue.\n", | |
+ (unsigned)rom_crc, (unsigned)bsv_crc); | |
+ av_free(buf); | |
+ return AVERROR_INVALIDDATA; | |
+ } | |
+ | |
+ if (!snes_load_cartridge_normal(NULL, buf, len)) | |
+ { | |
+ av_free(buf); | |
+ return AVERROR(ENOMEM); | |
+ } | |
+ | |
+ av_free(buf); | |
+ | |
+ if (state_size != snes_serialize_size()) | |
+ { | |
+ av_log(s, AV_LOG_ERROR, "Serialization sizes differ. Cannot continue ...\n"); | |
+ return AVERROR_INVALIDDATA; | |
+ } | |
+ | |
+ state_buf = av_malloc(state_size); | |
+ if (!state_buf) | |
+ return AVERROR(ENOMEM); | |
+ | |
+ if (avio_read(pb, state_buf, state_size) < state_size) | |
+ { | |
+ av_free(state_buf); | |
+ return AVERROR(EIO); | |
+ } | |
+ | |
+ if (!snes_unserialize(state_buf, state_size)) | |
+ { | |
+ av_log(s, AV_LOG_ERROR, "Unserialization failed. Cannot continue ...\n"); | |
+ av_free(state_buf); | |
+ return AVERROR_INVALIDDATA; | |
+ } | |
+ | |
+ av_free(state_buf); | |
+ | |
+ if (!init_audio_stream(audio_stream)) | |
+ return AVERROR_INVALIDDATA; | |
+ if (!init_video_stream(video_stream)) | |
+ return AVERROR_INVALIDDATA; | |
+ | |
+ ctx->pts_cnt = 0; | |
+ ctx->buf_cnt = 0; | |
+ ctx->frame_cnt = 0; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int bsv_seek(AVFormatContext *s, | |
+ int stream_index, int64_t timestamp, int flags) | |
+{ | |
+ (void)s; | |
+ (void)stream_index; | |
+ (void)timestamp; | |
+ (void)flags; | |
+ return -1; // BSV isn't exactly random access :D | |
+} | |
+ | |
+static void deinit_bsv(BSVContext *ctx) | |
+{ | |
+ if (g_is_instanced) | |
+ { | |
+ snes_unload_cartridge(); | |
+ snes_term(); | |
+ av_freep(&ctx->audio_buf); | |
+ av_freep(&ctx->video_buf); | |
+ } | |
+ g_is_instanced = false; | |
+} | |
+ | |
+static int bsv_packet(AVFormatContext *s, AVPacket *pkt) | |
+{ | |
+ BSVContext *ctx = s->priv_data; | |
+ | |
+ if (url_feof(s->pb)) | |
+ { | |
+ deinit_bsv(ctx); | |
+ return -1; | |
+ } | |
+ | |
+ if (!ctx->video_ready && !ctx->audio_ready) | |
+ snes_run(); // Emulate one frame for new data. | |
+ | |
+ if (ctx->audio_ready) | |
+ { | |
+ pkt->size = ctx->buf_cnt * sizeof(int16_t); | |
+ pkt->data = (uint8_t*)ctx->audio_buf; | |
+ pkt->stream_index = 1; | |
+ | |
+ pkt->dts = pkt->pts = ctx->pts_cnt; | |
+ ctx->buf_cnt = 0; | |
+ ctx->audio_ready = false; | |
+ } | |
+ else if (ctx->video_ready) | |
+ { | |
+ pkt->size = SNES_WIDTH * SNES_HEIGHT * sizeof(uint16_t); | |
+ pkt->data = (uint8_t*)ctx->video_buf; | |
+ pkt->stream_index = 0; | |
+ | |
+ pkt->dts = pkt->pts = ctx->frame_cnt; | |
+ ctx->video_ready = false; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+AVInputFormat ff_bsv_demuxer = { | |
+ .name = "bsv", | |
+ .long_name = NULL_IF_CONFIG_SMALL("BSNES movie"), | |
+ .priv_data_size = sizeof(BSVContext), | |
+ .read_probe = bsv_probe, | |
+ .read_header = bsv_header, | |
+ .read_packet = bsv_packet, | |
+ .read_seek = bsv_seek, | |
+ .codec_tag = NULL, | |
+}; | |
+ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment