[vlc-devel] [PATCH] Added FBS demux
bkurniawan at orecx.com
bkurniawan at orecx.com
Sat Jun 29 02:20:59 CEST 2019
From: budi-kurniawan <budi2020 at gmail.com>
---
modules/demux/Makefile.am | 7 +
modules/demux/fbs.cpp | 527 ++++++++++++++++++++++++++++++++++++
share/vlc.desktop.mimetypes | 1 +
src/input/demux.c | 1 +
src/misc/mime.c | 1 +
5 files changed, 537 insertions(+)
create mode 100644 modules/demux/fbs.cpp
diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am
index 0251da6879..4c13a815f9 100644
--- a/modules/demux/Makefile.am
+++ b/modules/demux/Makefile.am
@@ -134,6 +134,13 @@ libavi_plugin_la_SOURCES = demux/avi/avi.c demux/avi/libavi.c demux/avi/libavi.h
demux/avi/bitmapinfoheader.h
demux_LTLIBRARIES += libavi_plugin.la
+libfbs_plugin_la_SOURCES = demux/fbs.cpp
+libfbs_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(demuxdir)'
+libfbs_plugin_la_LIBADD = $(LIBS_fbs)
+libfbs_plugin_la_LIBADD += -lz
+demux_LTLIBRARIES += libfbs_plugin.la
+demux_LTLIBRARIES += $(LTLIBfbs)
+
libcaf_plugin_la_SOURCES = demux/caf.c
libcaf_plugin_la_LIBADD = $(LIBM)
demux_LTLIBRARIES += libcaf_plugin.la
diff --git a/modules/demux/fbs.cpp b/modules/demux/fbs.cpp
new file mode 100644
index 0000000000..9c6c7935c3
--- /dev/null
+++ b/modules/demux/fbs.cpp
@@ -0,0 +1,527 @@
+/*****************************************************************************
+ * fbs.cpp : fbs demuxer
+ *****************************************************************************
+ * Copyright (C) 2019 OrecX LLC
+ *
+ * Author: Budi Kurniawan <bkurniawan at orecx.com>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <assert.h>
+#include <memory>
+#include <vlc_common.h>
+#include <vlc_demux.h>
+#include <vlc_plugin.h>
+#include "zlib.h"
+
+#define MAX_INFLATE_SIZE_ZLIB 128000
+/** Initial memory alignment of data block.
+ * @note This must be a multiple of sizeof(void*) and a power of two.
+ * libavcodec AVX optimizations require at least 32-bytes. */
+#define BLOCK_ALIGN 32
+
+/** Initial reserved header and footer size. */
+#define BLOCK_PADDING 32
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+static int Open(vlc_object_t *);
+static void Close(vlc_object_t *);
+static int Seek(demux_t *, vlc_tick_t);
+static int Demux(demux_t *);
+static int Control(demux_t *, int, va_list);
+
+vlc_module_begin ()
+ set_shortname( "FBS" )
+ set_description( N_("FBS stream demuxer" ) )
+ set_capability( "demux", 212 )
+ set_callbacks( Open, Close )
+ set_category( CAT_INPUT )
+ set_subcategory( SUBCAT_INPUT_DEMUX )
+vlc_module_end ()
+
+/*****************************************************************************
+ * Definitions of structures used by this plugin
+ *****************************************************************************/
+typedef struct {
+ uint8_t bitsPerPixel;
+ uint8_t depth;
+ uint8_t bigEndianFlag;
+ uint8_t trueColorFlag;
+ uint16_t redMax;
+ uint16_t greenMax;
+ uint16_t blueMax;
+ uint8_t redShift;
+ uint8_t greenShift;
+ uint8_t blueShift;
+} FbsPixelFormat;
+
+typedef struct {
+ uint64_t frameSize;
+ es_out_id_t *p_es_video;
+ es_format_t fmt_video;
+ date_t pcr;
+ uint8_t rfbVersion;
+ uint16_t frameBufferWidth;
+ uint16_t frameBufferHeight;
+ uint8_t framesPerSecond;
+ uint16_t headerPos;
+ vlc_tick_t timestamp;
+ vlc_tick_t lastTimestamp;
+ bool seeking;
+ FbsPixelFormat fbsPixelFormat;
+ uint64_t inflated;
+ z_stream zStream;
+ uint8_t zrleTilePixels24[3 * 64 * 64]; // A tile of 64 x 64 pixels, 3 bytes per pixel
+} demux_sys_t;
+
+void copyTileToCanvas(const demux_sys_t *p_sys, const uint8_t bitsPerPixel, const uint16_t frameBufferWidth,
+ const uint32_t x, const uint32_t y, const uint8_t tileWidth, const uint8_t tileHeight,
+ uint8_t *canvas) {
+ assert(tileWidth <= 64 && tileHeight <= 64);
+ if (bitsPerPixel == 32) {
+ for (uint8_t i = 0; i < tileHeight; i++) {
+ for (uint8_t j = 0; j < tileWidth; j++) {
+ int source = 3 * (i * tileWidth + j);
+ int position = ((y + i) * frameBufferWidth + (x + j)) * 3;
+ canvas[position + 2] = p_sys->zrleTilePixels24[source]; //red;
+ canvas[position + 1] = p_sys->zrleTilePixels24[source + 1]; //green;
+ canvas[position] = p_sys->zrleTilePixels24[source + 2]; //blue;
+ }
+ }
+ }
+}
+
+uint32_t readPixel(const uint8_t bitsPerPixel, const uint8_t* data, uint64_t* pos) {
+ uint32_t pixel = 0;
+ if (bitsPerPixel == 32) {
+ uint8_t red = data[(*pos)++];
+ uint8_t green = data[(*pos)++];
+ uint8_t blue = data[(*pos)++];
+ pixel = red << 16 | green << 8 | blue;
+ }
+ return pixel;
+}
+
+ssize_t inf(demux_sys_t *p_sys, const uint8_t* input, const uint64_t inputLength, uint8_t *buf) {
+ p_sys->zStream.avail_in = inputLength;
+ p_sys->zStream.next_in = (uint8_t*) input;
+ uint8_t chunkData[MAX_INFLATE_SIZE_ZLIB];
+ uint32_t count = 0;
+ while(1) {
+ p_sys->zStream.avail_out = MAX_INFLATE_SIZE_ZLIB;
+ p_sys->zStream.next_out = chunkData;
+ int inflateResult = inflate(&p_sys->zStream, Z_NO_FLUSH);
+ uint32_t numBytesLeft = p_sys->zStream.total_out - p_sys->inflated;
+ for (unsigned int i = 0; i < numBytesLeft; i++) {
+ buf[count++] = chunkData[i];
+ }
+ p_sys->inflated = p_sys->zStream.total_out;
+ if (inflateResult != Z_OK || p_sys->zStream.avail_in == 0
+ || (p_sys->zStream.avail_out != 0 && inflateResult == Z_STREAM_END)) {
+ break;
+ }
+ }
+ return count;
+}
+
+void populateColorArray(const uint8_t bitsPerPixel, const uint8_t* rectData, uint64_t* pos,
+ uint8_t colorArray[], const uint16_t paletteSize) {
+ assert(paletteSize <= 64 * 64);
+ if (bitsPerPixel == 32) {
+ for (uint16_t i = 0; i < paletteSize; i++) {
+ uint16_t index = i * 3;
+ colorArray[index++] = rectData[(*pos)++]; // red
+ colorArray[index++] = rectData[(*pos)++]; // green
+ colorArray[index] = rectData[(*pos)++]; // blue
+ }
+ }
+}
+
+void handlePackedPixels(uint8_t *zrleTilePixels24, const uint8_t* data, uint64_t* pos, const uint8_t tileWidth,
+ const uint8_t tileHeight, const uint8_t colorArray[], const uint16_t paletteSize) {
+ assert(tileWidth <= 64 && tileHeight <= 64);
+ uint8_t shift = 1;
+ if (paletteSize > 16) {
+ shift = 8;
+ } else if (paletteSize > 4) {
+ shift = 4;
+ } else if (paletteSize > 2) {
+ shift = 2;
+ }
+ uint16_t index = 0;
+ for (uint8_t i = 0; i < tileHeight; ++i) {
+ uint16_t end = index + tileWidth;
+ uint8_t counter1;
+ uint16_t counter2 = 0;
+ while (index < end) {
+ if (counter2 == 0) {
+ counter1 = data[(*pos)++];
+ counter2 = 8;
+ }
+ counter2 -= shift;
+ uint16_t colorIndex = 3 * ((counter1 >> counter2) & ((1 << shift) - 1));
+ zrleTilePixels24[index * 3] = colorArray[colorIndex++];
+ zrleTilePixels24[index * 3 + 1] = colorArray[colorIndex++];
+ zrleTilePixels24[index * 3 + 2] = colorArray[colorIndex];
+ index++;
+ }
+ }
+}
+
+void handlePackedRLEPixels(uint8_t *zrleTilePixels24, const uint8_t* data, uint64_t* pos,
+ const uint8_t tileWidth, const uint8_t tileHeight, const uint8_t colorArray[]) {
+ assert(tileWidth <= 64 && tileHeight <= 64);
+ uint16_t index = 0;
+ uint16_t end = tileWidth * tileHeight;
+ while (index < end) {
+ uint8_t flag = data[(*pos)++];
+ uint16_t totalLength = 1;
+ if ((flag & 128) != 0) {
+ uint8_t length;
+ do {
+ length = data[(*pos)++];
+ totalLength += length;
+ } while (length == 255);
+ assert(totalLength <= end - index);
+ }
+ flag &= 127;
+ while (totalLength-- != 0) {
+ zrleTilePixels24[3 * index] = colorArray[3 * flag];
+ zrleTilePixels24[3 * index + 1] = colorArray[3 * flag + 1];
+ zrleTilePixels24[3 * index + 2] = colorArray[3 * flag + 2];
+ index++;
+ }
+ }
+}
+
+void handlePlainRLEPixels(uint8_t *zrleTilePixels24, const uint8_t bitsPerPixel,
+ const uint8_t* data, uint64_t* pos, const uint8_t tileWidth, const uint8_t tileHeight) {
+ assert(tileWidth <= 64 && tileHeight <= 64);
+ uint16_t index = 0;
+ uint16_t end = tileWidth * tileHeight;
+ while (index < end) {
+ uint8_t length;
+ uint32_t pixel = readPixel(bitsPerPixel, data, pos);
+ uint16_t totalLength = 1;
+ do {
+ length = data[(*pos)++];
+ totalLength += length;
+ } while (length == 255);
+ assert(totalLength <= end - index);
+ while (totalLength-- > 0) {
+ zrleTilePixels24[3 * index] = (pixel & 0xFF0000) >> 16;
+ zrleTilePixels24[3 * index + 1] = (pixel & 0xFF00) >> 8;
+ zrleTilePixels24[3 * index + 2] = pixel & 0xFF;
+ index++;
+ }
+ }
+}
+
+void handleFrame(demux_sys_t *p_sys, const uint8_t bitsPerPixel,
+ const uint16_t frameBufferWidth, const uint8_t *data, uint8_t *canvas) {
+ // byte 1 = message type, byte 2 = padding, both can be ignored
+ uint16_t rectCount = U16_AT(&data[2]);
+ uint64_t pos = 4;
+ for (uint16_t i = 0; i < rectCount; i++) {
+ uint16_t rectX = U16_AT(&data[pos]);
+ pos += 2;
+ uint16_t rectY = U16_AT(&data[pos]);
+ pos += 2;
+ uint16_t rectWidth = U16_AT(&data[pos]);
+ pos += 2;
+ uint16_t rectHeight = U16_AT(&data[pos]);
+ pos += 2;
+ pos += 4; // skip encoding
+ const uint32_t rectDataLength = U32_AT(&data[pos]);
+ pos += 4;
+
+ uint8_t* inflatedData = new uint8_t[10 * MAX_INFLATE_SIZE_ZLIB];
+ if (!inflatedData) {
+ return;
+ }
+ inf(p_sys, data + pos, rectDataLength, inflatedData);
+
+ pos += rectDataLength;
+ uint64_t inflatedDataReadPosition = 0;
+ for (int j = rectY; j < rectY + rectHeight; j += 64) {
+ // last row tiles may have height that is < 64
+ int tileHeight = std::min(64, rectY + rectHeight - j);
+ for (int k = rectX; k < rectX + rectWidth; k += 64) {
+ // last column tiles may have width that is < 64
+ int tileWidth = std::min(64, rectX + rectWidth - k);
+ uint8_t subencoding = inflatedData[inflatedDataReadPosition++];
+ bool runLength = (subencoding & 0x80) != 0;
+ uint8_t paletteSize = subencoding & 0x7F;
+ uint8_t colorArray[128 * 3];
+ populateColorArray(bitsPerPixel, inflatedData,
+ &inflatedDataReadPosition, colorArray, paletteSize);
+ // read palette colors
+ if (paletteSize == 1) {
+ for (uint16_t d = j; d < j + tileHeight; d++) {
+ for (uint16_t e = k; e < k + tileWidth; e++) {
+ uint32_t index = (d * frameBufferWidth + e) * 3;
+ canvas[index++] = colorArray[0]; //red;
+ canvas[index++] = colorArray[1]; //green;
+ canvas[index] = colorArray[2]; //blue;
+ }
+ }
+ continue;
+ }
+ if (!runLength) {
+ if (paletteSize == 0) {
+ populateColorArray(bitsPerPixel, inflatedData, &inflatedDataReadPosition,
+ p_sys->zrleTilePixels24, tileWidth * tileHeight);
+ } else {
+ handlePackedPixels(p_sys->zrleTilePixels24, inflatedData, &inflatedDataReadPosition,
+ tileWidth, tileHeight, colorArray, paletteSize);
+ }
+ } else {
+ if (paletteSize == 0) {
+ handlePlainRLEPixels(p_sys->zrleTilePixels24, bitsPerPixel, inflatedData,
+ &inflatedDataReadPosition, tileWidth, tileHeight);
+ } else {
+ handlePackedRLEPixels(p_sys->zrleTilePixels24, inflatedData, &inflatedDataReadPosition, tileWidth,
+ tileHeight, colorArray);
+ }
+ }
+ copyTileToCanvas(p_sys, bitsPerPixel, frameBufferWidth, k, j, tileWidth, tileHeight, canvas);
+ }
+ }
+ delete[] inflatedData;
+ }
+}
+uint32_t readLastTimestamp(stream_t *s) {
+ // The last 4 bytes of the stream contains the last timestamp.
+ // We read the last 4 bytes by repeatedly calling vlc_stream_Read until EOF is reached
+ const uint16_t len = 10000;
+ uint8_t buf[len];
+ uint8_t ts[4];
+ while(!vlc_stream_Eof(s)) {
+ const uint16_t bytesRead = vlc_stream_Read(s, buf, len);
+ if (bytesRead >= 4) {
+ for (int i = 0; i < 4; i++) {
+ ts[i] = buf[bytesRead - 4 + i];
+ }
+ } else {
+ //shift
+ if (bytesRead != 0) {
+ for (int i = 0; i < 4 - bytesRead; i++) {
+ ts[i] = ts[i + bytesRead];
+ }
+ // copy bytesRead
+ for (int i = 0; i < bytesRead; i++) {
+ ts[i + 4 - bytesRead] = buf[i];
+ }
+ }
+ }
+ }
+ return U32_AT(&ts);
+}
+
+void resetZStream(demux_sys_t *p_sys) {
+ p_sys->inflated = p_sys->zStream.avail_in = 0;
+ p_sys->zStream.zalloc = Z_NULL;
+ p_sys->zStream.zfree = Z_NULL;
+ p_sys->zStream.opaque = Z_NULL;
+ p_sys->zStream.next_in = Z_NULL;
+ inflateInit(&p_sys->zStream);
+}
+
+void updateCanvas(const demux_t *p_demux, const uint8_t bitsPerPixel,
+ const uint16_t frameBufferWidth, const uint64_t frameSize, const uint32_t t) {
+ demux_sys_t *p_sys = (demux_sys_t *) p_demux->p_sys;
+ block_t *p_block = block_Alloc(p_sys->frameSize);
+ p_block->i_buffer = p_sys->frameSize;
+ while (p_sys->timestamp <= t) {
+ uint8_t buf[4];
+ if (vlc_stream_Read(p_demux->s, buf, 4) < 4) {
+ return;
+ }
+ uint32_t dataLength = U32_AT(&buf);
+ uint32_t paddedDataLength = 4 * ((dataLength + 3) / 4);
+ block_t *frameData = vlc_stream_Block(p_demux->s, paddedDataLength + /*timestamp*/4);
+ if (!frameData) {
+ return;
+ }
+ frameData->i_size = frameSize + BLOCK_ALIGN + 2 * BLOCK_PADDING;
+ frameData->i_buffer = frameSize;
+ p_sys->timestamp = U32_AT(&frameData->p_buffer[paddedDataLength]);
+ handleFrame(p_sys, bitsPerPixel, frameBufferWidth, frameData->p_buffer, p_block->p_buffer);
+ block_Release(frameData);
+ }
+ p_block->i_dts = p_block->i_pts = p_sys->timestamp * 1000;
+ es_out_Send(p_demux->out, p_sys->p_es_video, p_block);
+}
+
+/*****************************************************************************
+ * Open: initializes FBS demux structures
+ *****************************************************************************/
+static int Open(vlc_object_t *p_this) {
+ demux_t *p_demux = (demux_t*) p_this;
+ demux_sys_t *p_sys;
+ p_demux->p_sys = p_sys = (demux_sys_t*) malloc(sizeof(demux_sys_t));
+ const vlc_fourcc_t i_chroma = VLC_CODEC_RGB24;
+ const uint8_t i_sar_num = 1;
+ const uint8_t i_sar_den = 1;
+ const uint8_t *p_peek;
+
+ p_sys->framesPerSecond = 5;
+ p_sys->timestamp = 0;
+ p_sys->seeking = false;
+ if (vlc_stream_Peek(p_demux->s, &p_peek, 2000) < 2000) {
+ return VLC_EGENERIC;
+ }
+
+ if (memcmp((char*) p_peek, "FBS 001.000", 11)) {
+ return VLC_EGENERIC; // file invalid
+ }
+
+ uint64_t pos = 12;
+ for (uint8_t frameNo = 1; frameNo < 6; frameNo++) {
+ uint32_t dataLength = U32_AT(&p_peek[pos]);
+ uint32_t paddedDataLength = 4 * ((dataLength + 3) / 4);
+ if (frameNo == 1) {
+ p_sys->rfbVersion = p_peek[pos + 14] - 48; // rfbVersion is either 3 or 8
+ }
+ if (frameNo == 3 && p_sys->rfbVersion == 3) {
+ // no security result for RFB 3.3, skip
+ continue;
+ }
+ if (frameNo == 4) {
+ p_sys->frameBufferWidth = U16_AT(&p_peek[pos + 4]);
+ p_sys->frameBufferHeight = U16_AT(&p_peek[pos + 6]);
+ p_sys->fbsPixelFormat.bitsPerPixel = p_peek[pos + 8];
+ p_sys->fbsPixelFormat.depth = p_peek[pos + 9];
+ p_sys->fbsPixelFormat.bigEndianFlag = p_peek[pos + 10];
+ p_sys->fbsPixelFormat.trueColorFlag = p_peek[pos + 11];
+ p_sys->fbsPixelFormat.redMax = U16_AT(&p_peek[pos + 12]);
+ p_sys->fbsPixelFormat.greenMax = U16_AT(&p_peek[pos + 14]);
+ p_sys->fbsPixelFormat.blueMax = U16_AT(&p_peek[pos + 16]);
+ p_sys->fbsPixelFormat.redShift = p_peek[pos + 18];
+ p_sys->fbsPixelFormat.greenShift = p_peek[pos + 19];
+ p_sys->fbsPixelFormat.blueShift = p_peek[pos + 20];
+ }
+ pos += 4 + paddedDataLength + /* timestamp */4;
+ p_sys->headerPos = pos; // used by SEEK
+ }
+
+ /* Set the demux function */
+ es_format_Init(&p_sys->fmt_video, VIDEO_ES, i_chroma);
+ video_format_Setup(&p_sys->fmt_video.video, i_chroma,
+ p_sys->frameBufferWidth, p_sys->frameBufferHeight,
+ p_sys->frameBufferWidth, p_sys->frameBufferHeight,
+ i_sar_num, i_sar_den);
+
+ date_Init(&p_sys->pcr, p_sys->fmt_video.video.i_frame_rate,
+ p_sys->fmt_video.video.i_frame_rate_base);
+ date_Set(&p_sys->pcr, VLC_TICK_0);
+
+ const vlc_chroma_description_t *dsc = vlc_fourcc_GetChromaDescription(
+ p_sys->fmt_video.video.i_chroma);
+ p_sys->frameSize = p_sys->frameBufferWidth * p_sys->frameBufferHeight * dsc->pixel_size; // dsc->pixel_size = 3
+ p_sys->p_es_video = es_out_Add(p_demux->out, &p_sys->fmt_video);
+ p_demux->pf_demux = Demux;
+ p_demux->pf_control = Control;
+ resetZStream(p_sys);
+ // skip to position of data
+ p_sys->lastTimestamp = readLastTimestamp(p_demux->s);
+ if (vlc_stream_Seek(p_demux->s, pos)) {
+ return VLC_EGENERIC;
+ }
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Close: frees unused data
+ *****************************************************************************/
+static void Close(vlc_object_t *p_this) {
+ demux_t *p_demux = (demux_t*) p_this;
+ demux_sys_t *p_sys = (demux_sys_t*) p_demux->p_sys;
+ free(p_sys);
+}
+
+/*****************************************************************************
+ * Control:
+ *****************************************************************************/
+static int Control(demux_t *p_demux, int i_query, va_list args) {
+ demux_sys_t *p_sys = (demux_sys_t*) p_demux->p_sys;
+ const int64_t i_bps = 8LL * p_sys->frameSize;
+ switch (i_query) {
+ case DEMUX_GET_LENGTH:
+ //assign a number in microseconds: timestamp (in ms) * 1000
+ *va_arg(args, vlc_tick_t *) = p_sys->lastTimestamp * 1000;
+ return VLC_SUCCESS;
+ case DEMUX_CAN_SEEK:
+ *va_arg(args, bool *) = true;
+ return VLC_SUCCESS;
+ case DEMUX_GET_TIME:
+ *va_arg(args, vlc_tick_t *) = p_sys->timestamp * 1000;
+ return VLC_SUCCESS;
+ case DEMUX_GET_POSITION:
+ double *pf;
+ pf = va_arg(args, double *);
+ *pf = (double) p_sys->timestamp / (double) p_sys->lastTimestamp;
+ return VLC_SUCCESS;
+ case DEMUX_SET_POSITION:
+ double f = va_arg(args, double);
+ vlc_tick_t i64 = f * 1000;
+ return Seek(p_demux, i64);
+ }
+ return demux_vaControlHelper(p_demux->s, 0, -1, i_bps,
+ p_sys->frameSize, i_query, args);
+}
+
+/*****************************************************************************
+ * Demux: reads and demuxes data packets
+ *****************************************************************************
+ * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
+ *****************************************************************************/
+static int Demux(demux_t *p_demux) {
+ demux_sys_t *p_sys = (demux_sys_t *) p_demux->p_sys;
+ vlc_tick_t pcr = date_Get(&p_sys->pcr);
+ es_out_SetPCR(p_demux->out, pcr);
+ if (!p_sys->seeking) {
+ updateCanvas(p_demux, p_sys->fbsPixelFormat.bitsPerPixel,
+ p_sys->frameBufferWidth, p_sys->frameSize, pcr / 1000);
+ }
+ p_sys->pcr.i_divider_num = p_sys->framesPerSecond; //how many times in a second Demux() is called
+ p_sys->pcr.i_divider_den = 1;
+ date_Increment(&p_sys->pcr, 1);
+ return VLC_DEMUXER_SUCCESS;
+}
+
+static int Seek(demux_t *p_demux, vlc_tick_t i_date) {
+ demux_sys_t *p_sys = (demux_sys_t *) p_demux->p_sys;
+ uint32_t soughtTimestamp = i_date * p_sys->lastTimestamp / 1000;
+ // Rewind to right after the header
+ if (vlc_stream_Seek(p_demux->s, p_sys->headerPos)) {
+ return VLC_DEMUXER_EOF;
+ }
+
+ p_sys->seeking = true;
+ p_sys->pcr.i_divider_num = 0;
+ resetZStream(p_sys);
+
+ p_sys->timestamp = 0;
+ updateCanvas(p_demux, p_sys->fbsPixelFormat.bitsPerPixel,
+ p_sys->frameBufferWidth, p_sys->frameSize, soughtTimestamp);
+ p_sys->seeking = false;
+ p_sys->pcr.date = soughtTimestamp * 1000;
+ return VLC_SUCCESS;
+}
diff --git a/share/vlc.desktop.mimetypes b/share/vlc.desktop.mimetypes
index 0d866f7da8..88d1215bdd 100644
--- a/share/vlc.desktop.mimetypes
+++ b/share/vlc.desktop.mimetypes
@@ -42,6 +42,7 @@ video/msvideo
video/vnd.divx
video/avi
video/x-avi
+video/fbs
# Real audio and video
application/vnd.rn-realmedia # RM
diff --git a/src/input/demux.c b/src/input/demux.c
index d7b5ce3711..7ebe58a8ef 100644
--- a/src/input/demux.c
+++ b/src/input/demux.c
@@ -88,6 +88,7 @@ static const char* DemuxNameFromExtension( char const* ext,
{ "avi", "avi" },
{ "drc", "dirac" },
{ "dv", "dv" },
+ { "fbs", "fbs" },
{ "flac", "flac" },
{ "h264", "h264" },
{ "kar", "smf" },
diff --git a/src/misc/mime.c b/src/misc/mime.c
index 03a400662a..272ac67d1f 100644
--- a/src/misc/mime.c
+++ b/src/misc/mime.c
@@ -58,6 +58,7 @@ static const struct
/* media mime */
{ ".avi", "video/avi" },
{ ".asf", "video/x-ms-asf" },
+ { ".fbs", "video/fbs" },
{ ".m1a", "audio/mpeg" },
{ ".m2a", "audio/mpeg" },
{ ".m1v", "video/mpeg" },
--
2.17.1
More information about the vlc-devel
mailing list