aboutsummaryrefslogtreecommitdiff
path: root/video/bink_decoder.h
blob: 8a67913a0333317e7d2e8d6d5fbee95964fccd09 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU 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.
 *
 */

// Based on eos' Bink decoder which is in turn
// based quite heavily on the Bink decoder found in FFmpeg.
// Many thanks to Kostya Shishkov for doing the hard work.

#include "common/scummsys.h"

#ifdef USE_BINK

#ifndef VIDEO_BINK_DECODER_H
#define VIDEO_BINK_DECODER_H

#include "common/array.h"
#include "common/bitstream.h"
#include "common/rational.h"

#include "video/video_decoder.h"

#include "graphics/surface.h"

namespace Audio {
class AudioStream;
class QueuingAudioStream;
}

namespace Common {
class SeekableReadStream;
class Huffman;

class RDFT;
class DCT;
}

namespace Graphics {
struct Surface;
}

namespace Video {

/**
 * Decoder for Bink videos.
 *
 * Video decoder used in engines:
 *  - scumm (he)
 */
class BinkDecoder : public VideoDecoder {
public:
	BinkDecoder();
	~BinkDecoder();

	bool loadStream(Common::SeekableReadStream *stream);
	void close();

protected:
	void readNextPacket();
	bool supportsAudioTrackSwitching() const { return true; }
	AudioTrack *getAudioTrack(int index);

private:
	static const int kAudioChannelsMax  = 2;
	static const int kAudioBlockSizeMax = (kAudioChannelsMax << 11);

	enum AudioCodec {
		kAudioCodecDCT,
		kAudioCodecRDFT
	};

	/** An audio track. */
	struct AudioInfo {
		uint16 flags;

		uint32 sampleRate;
		uint8  channels;

		uint32 outSampleRate;
		uint8  outChannels;

		AudioCodec codec;

		uint32 sampleCount;

		Common::BitStream32LELSB *bits;

		bool first;

		uint32 frameLen;
		uint32 overlapLen;

		uint32 blockSize;

		uint32  bandCount;
		uint32 *bands;

		float root;

		float coeffs[16 * kAudioBlockSizeMax];
		int16 prevCoeffs[kAudioBlockSizeMax];

		float *coeffsPtr[kAudioChannelsMax];

		Common::RDFT *rdft;
		Common::DCT  *dct;

		AudioInfo();
		~AudioInfo();
	};

	/** A video frame. */
	struct VideoFrame {
		bool keyFrame;

		uint32 offset;
		uint32 size;

		Common::BitStream32LELSB *bits;

		VideoFrame();
		~VideoFrame();
	};

	class BinkVideoTrack : public FixedRateVideoTrack {
	public:
		BinkVideoTrack(uint32 width, uint32 height, const Graphics::PixelFormat &format, uint32 frameCount, const Common::Rational &frameRate, bool swapPlanes, bool hasAlpha, uint32 id);
		~BinkVideoTrack();

		uint16 getWidth() const { return _surface.w; }
		uint16 getHeight() const { return _surface.h; }
		Graphics::PixelFormat getPixelFormat() const { return _surface.format; }
		int getCurFrame() const { return _curFrame; }
		int getFrameCount() const { return _frameCount; }
		const Graphics::Surface *decodeNextFrame() { return &_surface; }

		/** Decode a video packet. */
		void decodePacket(VideoFrame &frame);

	protected:
		Common::Rational getFrameRate() const { return _frameRate; }

	private:
		/** A decoder state. */
		struct DecodeContext {
			VideoFrame *video;

			uint32 planeIdx;

			uint32 blockX;
			uint32 blockY;

			byte *dest;
			byte *prev;

			byte *destStart, *destEnd;
			byte *prevStart, *prevEnd;

			uint32 pitch;

			int coordMap[64];
			int coordScaledMap1[64];
			int coordScaledMap2[64];
			int coordScaledMap3[64];
			int coordScaledMap4[64];
		};

		/** IDs for different data types used in Bink video codec. */
		enum Source {
			kSourceBlockTypes    = 0, ///< 8x8 block types.
			kSourceSubBlockTypes    , ///< 16x16 block types (a subset of 8x8 block types).
			kSourceColors           , ///< Pixel values used for different block types.
			kSourcePattern          , ///< 8-bit values for 2-color pattern fill.
			kSourceXOff             , ///< X components of motion value.
			kSourceYOff             , ///< Y components of motion value.
			kSourceIntraDC          , ///< DC values for intrablocks with DCT.
			kSourceInterDC          , ///< DC values for interblocks with DCT.
			kSourceRun              , ///< Run lengths for special fill block.

			kSourceMAX
		};

		/** Bink video block types. */
		enum BlockType {
			kBlockSkip    = 0,  ///< Skipped block.
			kBlockScaled     ,  ///< Block has size 16x16.
			kBlockMotion     ,  ///< Block is copied from previous frame with some offset.
			kBlockRun        ,  ///< Block is composed from runs of colors with custom scan order.
			kBlockResidue    ,  ///< Motion block with some difference added.
			kBlockIntra      ,  ///< Intra DCT block.
			kBlockFill       ,  ///< Block is filled with single color.
			kBlockInter      ,  ///< Motion block with DCT applied to the difference.
			kBlockPattern    ,  ///< Block is filled with two colors following custom pattern.
			kBlockRaw           ///< Uncoded 8x8 block.
		};

		/** Data structure for decoding and tranlating Huffman'd data. */
		struct Huffman {
			int  index;       ///< Index of the Huffman codebook to use.
			byte symbols[16]; ///< Huffman symbol => Bink symbol tranlation list.
		};

		/** Data structure used for decoding a single Bink data type. */
		struct Bundle {
			int countLengths[2]; ///< Lengths of number of entries to decode (in bits).
			int countLength;     ///< Length of number of entries to decode (in bits) for the current plane.

			Huffman huffman; ///< Huffman codebook.

			byte *data;    ///< Buffer for decoded symbols.
			byte *dataEnd; ///< Buffer end.

			byte *curDec; ///< Pointer to the data that wasn't yet decoded.
			byte *curPtr; ///< Pointer to the data that wasn't yet read.
		};

		int _curFrame;
		int _frameCount;

		Graphics::Surface _surface;
		int _surfaceWidth; ///< The actual surface width
		int _surfaceHeight; ///< The actual surface height

		uint32 _id; ///< The BIK FourCC.

		bool _hasAlpha;   ///< Do video frames have alpha?
		bool _swapPlanes; ///< Are the planes ordered (A)YVU instead of (A)YUV?

		Common::Rational _frameRate;

		Bundle _bundles[kSourceMAX]; ///< Bundles for decoding all data types.

		Common::Huffman *_huffman[16]; ///< The 16 Huffman codebooks used in Bink decoding.

		/** Huffman codebooks to use for decoding high nibbles in color data types. */
		Huffman _colHighHuffman[16];
		/** Value of the last decoded high nibble in color data types. */
		int _colLastVal;

		byte *_curPlanes[4]; ///< The 4 color planes, YUVA, current frame.
		byte *_oldPlanes[4]; ///< The 4 color planes, YUVA, last frame.

		/** Initialize the bundles. */
		void initBundles();
		/** Deinitialize the bundles. */
		void deinitBundles();

		/** Initialize the Huffman decoders. */
		void initHuffman();

		/** Decode a plane. */
		void decodePlane(VideoFrame &video, int planeIdx, bool isChroma);

		/** Read/Initialize a bundle for decoding a plane. */
		void readBundle(VideoFrame &video, Source source);

		/** Read the symbols for a Huffman code. */
		void readHuffman(VideoFrame &video, Huffman &huffman);
		/** Merge two Huffman symbol lists. */
		void mergeHuffmanSymbols(VideoFrame &video, byte *dst, const byte *src, int size);

		/** Read and translate a symbol out of a Huffman code. */
		byte getHuffmanSymbol(VideoFrame &video, Huffman &huffman);

		/** Get a direct value out of a bundle. */
		int32 getBundleValue(Source source);
		/** Read a count value out of a bundle. */
		uint32 readBundleCount(VideoFrame &video, Bundle &bundle);

		// Handle the block types
		void blockSkip         (DecodeContext &ctx);
		void blockScaledSkip   (DecodeContext &ctx);
		void blockScaledRun    (DecodeContext &ctx);
		void blockScaledIntra  (DecodeContext &ctx);
		void blockScaledFill   (DecodeContext &ctx);
		void blockScaledPattern(DecodeContext &ctx);
		void blockScaledRaw    (DecodeContext &ctx);
		void blockScaled       (DecodeContext &ctx);
		void blockMotion       (DecodeContext &ctx);
		void blockRun          (DecodeContext &ctx);
		void blockResidue      (DecodeContext &ctx);
		void blockIntra        (DecodeContext &ctx);
		void blockFill         (DecodeContext &ctx);
		void blockInter        (DecodeContext &ctx);
		void blockPattern      (DecodeContext &ctx);
		void blockRaw          (DecodeContext &ctx);

		// Read the bundles
		void readRuns        (VideoFrame &video, Bundle &bundle);
		void readMotionValues(VideoFrame &video, Bundle &bundle);
		void readBlockTypes  (VideoFrame &video, Bundle &bundle);
		void readPatterns    (VideoFrame &video, Bundle &bundle);
		void readColors      (VideoFrame &video, Bundle &bundle);
		void readDCS         (VideoFrame &video, Bundle &bundle, int startBits, bool hasSign);
		void readDCTCoeffs   (VideoFrame &video, int16 *block, bool isIntra);
		void readResidue     (VideoFrame &video, int16 *block, int masksCount);

		// Bink video IDCT
		void IDCT(int16 *block);
		void IDCTPut(DecodeContext &ctx, int16 *block);
		void IDCTAdd(DecodeContext &ctx, int16 *block);
	};

	class BinkAudioTrack : public AudioTrack {
	public:
		BinkAudioTrack(AudioInfo &audio, Audio::Mixer::SoundType soundType);
		~BinkAudioTrack();

		/** Decode an audio packet. */
		void decodePacket();

	protected:
		Audio::AudioStream *getAudioStream() const;

	private:
		AudioInfo *_audioInfo;
		Audio::QueuingAudioStream *_audioStream;

		float getFloat();

		/** Decode an audio block. */
		void audioBlock(int16 *out);
		/** Decode a DCT'd audio block. */
		void audioBlockDCT();
		/** Decode a RDFT'd audio block. */
		void audioBlockRDFT();

		void readAudioCoeffs(float *coeffs);

		static void floatToInt16Interleave(int16 *dst, const float **src, uint32 length, uint8 channels);
	};

	Common::SeekableReadStream *_bink;

	Common::Array<AudioInfo> _audioTracks; ///< All audio tracks.
	Common::Array<VideoFrame> _frames;      ///< All video frames.

	void initAudioTrack(AudioInfo &audio);
};

} // End of namespace Video

#endif // VIDEO_BINK_DECODER_H

#endif // USE_BINK