Skip to content

Instantly share code, notes, and snippets.

@Themaister
Created August 18, 2011 18:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Themaister/1154721 to your computer and use it in GitHub Desktop.
Save Themaister/1154721 to your computer and use it in GitHub Desktop.
BSV demuxer for FFmpeg
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