xoreos  0.0.5
xmv.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include <cassert>
26 
27 #include "src/common/error.h"
29 #include "src/common/ptrvector.h"
30 #include "src/common/strutil.h"
31 #include "src/common/timestamp.h"
32 
33 #include "src/sound/audiostream.h"
34 #include "src/sound/interleaver.h"
35 
37 #include "src/sound/decoders/pcm.h"
39 
40 #include "src/video/xmv.h"
41 
43 
44 static const int kAudioFlagADPCM51FrontLeftRight = 1;
45 static const int kAudioFlagADPCM51FrontCenterLow = 2;
46 static const int kAudioFlagADPCM51RearLeftRight = 4;
50 
51 namespace Video {
52 
54  _xmv(xmv), _videoTrack(0), _audioTrackCount(0) {
55 
56  assert(_xmv);
57 
58  load();
59 }
60 
62 }
63 
65  // New data available that we should play?
66  if (!audioPacket.newSlice || !audioPacket.track)
67  return;
68 
69  // Seek to it
70  _xmv->seek(audioPacket.dataOffset);
71 
72  // Read and queue it
73  audioPacket.track->queueAudio(_xmv->readStream(audioPacket.dataSize));
74 
75  audioPacket.newSlice = false;
76 }
77 
79  // Go over all audio packets and try to queue their data.
80 
81  for (std::vector<PacketAudio>::iterator audio = packet.audio.begin();
82  audio != packet.audio.end(); ++audio)
83  queueNewAudio(*audio);
84 }
85 
87  // No frame left, nothing to do
88  if (videoPacket.frameCount == 0)
89  return;
90 
91  // Seek
92  _xmv->seek(videoPacket.dataOffset);
93 
94  // Read the frame header
95 
96  uint32 frameHeader = _xmv->readUint32LE();
97 
98  videoPacket.currentFrameSize = (frameHeader & 0x1FFFF) * 4 + 4;
99  videoPacket.currentFrameTimestamp = (frameHeader >> 17) + videoPacket.lastFrameTime;
100 
101  if (videoPacket.currentFrameSize > videoPacket.dataSize)
102  throw Common::Exception("XboxMediaVideo::processNextFrame(): Frame data overrun");
103 
104  // Decode the frame
105 
106  if (videoPacket.currentFrameSize > 0) {
107  Common::SeekableSubReadStream frameData(_xmv.get(), _xmv->pos(), _xmv->pos() + videoPacket.currentFrameSize);
108  _needCopy = _videoTrack->decodeFrame(*_surface, frameData);
109 
110  if (!_needCopy)
111  warning("XboxMediaVideo::processNextFrame(): Video frame without a decoder");
112  }
113 
114  // Update the frame time
115  videoPacket.lastFrameTime = videoPacket.currentFrameTimestamp;
116 
117  // Advance the data
118  videoPacket.dataSize -= videoPacket.currentFrameSize + 4;
119  videoPacket.dataOffset += videoPacket.currentFrameSize + 4;
120 
121  // One less frame to worry about
122  videoPacket.frameCount--;
123 }
124 
126  // Read the XMV header
127 
128  _xmv->skip(4); // Next packet size
129 
130  uint32 thisPacketSize = _xmv->readUint32LE();
131 
132  _xmv->skip(4); // Max packet size
133 
134  uint32 tag = _xmv->readUint32LE();
135  if (tag != MKTAG('X', 'b', 'o', 'x'))
136  throw Common::Exception("XboxMediaVideo::load(): No 'Xbox' tag (%s)", Common::debugTag(tag).c_str());
137 
138  uint32 version = _xmv->readUint32LE();
139 
140  if ((version == 0) || (version > 4))
141  throw Common::Exception("XboxMediaVideo::load(): Unsupported version %d", version);
142 
143  uint32 width = _xmv->readUint32LE();
144  uint32 height = _xmv->readUint32LE();
145 
146  _xmv->skip(4); // Duration in ms
147 
148  _audioTrackCount = _xmv->readUint16LE();
149  std::vector<AudioInfo> audioTrackInfo;
150  audioTrackInfo.resize(_audioTrackCount);
151 
152  _xmv->skip(2); // Unknown
153 
154  // Audio track info
155  for (uint32 i = 0; i < _audioTrackCount; i++) {
156  audioTrackInfo[i].compression = _xmv->readUint16LE();
157  audioTrackInfo[i].channels = _xmv->readUint16LE();
158  audioTrackInfo[i].rate = _xmv->readUint32LE();
159  audioTrackInfo[i].bitsPerSample = _xmv->readUint16LE();
160  audioTrackInfo[i].flags = _xmv->readUint16LE();
161  }
162 
163  // Initialize the audio tracks
165  audioTracks.resize(_audioTrackCount);
166  for (uint16 i = 0; i < _audioTrackCount; i++) {
167  XMVAudioTrack *track;
168 
169  // Try creating the track; if we can't, move on.
170  try {
171  track = new XMVAudioTrack(audioTrackInfo[i]);
172  } catch (Common::Exception &e) {
173  warning("Failed to initialize audio track: %s", e.what());
174  continue;
175  }
176 
177  audioTracks[i] = track;
178  }
179 
180  // Initialize the packet data
181 
183 
186 
189 
191 
193  for (uint32 i = 0; i < _audioTrackCount; i++)
194  _curPacket.audio[i].track = 0;
195 
196 
197  // We can only use the first audio track. Try to create it.
198  for (uint32 i = 0; i < _audioTrackCount; i++) {
199  if (!audioTracks[i])
200  continue;
201 
202  if ((audioTrackInfo[i].flags & kAudioFlagADPCM51) != 0 && (i + 2) < _audioTrackCount) {
203  // Make sure the other tracks are valid
204  if (audioTracks[i + 1] && audioTracks[i + 2]) {
205  _curPacket.audio[i].track = audioTracks[i];
206  _curPacket.audio[i + 1].track = audioTracks[i];
207  _curPacket.audio[i + 2].track = audioTracks[i];
208 
209  addTrack(new XMVAudioTrack51(audioTracks[i], audioTracks[i + 1], audioTracks[i + 2]));
210 
211  audioTracks[i] = 0;
212  audioTracks[i + 1] = 0;
213  audioTracks[i + 2] = 0;
214  } else {
215  warning("Could not create 5.1 track");
216  }
217  } else {
218  _curPacket.audio[i].track = audioTracks[i];
219  addTrack(audioTracks[i]);
220  audioTracks[i] = 0;
221  }
222 
223  break;
224  }
225 
226  // Add the video track
229 
230  // Fetch the first packet
232 
233  // Feed audio
235 
236  // Initialize video
237  initVideo();
238 }
239 
241  // Seek to it
242  packet.thisPacketOffset = packet.nextPacketOffset;
243  _xmv->seek(packet.thisPacketOffset);
244 
245  // Update the size
246  packet.thisPacketSize = packet.nextPacketSize;
247  if (packet.thisPacketSize < (12 + _audioTrackCount * 4))
248  return;
249 
250  // Process the header
251  processPacketHeader(packet);
252 
253  // Update the offset
254  packet.nextPacketOffset = packet.thisPacketOffset + packet.thisPacketSize;
255 }
256 
258  // Next packet size
259  packet.nextPacketSize = _xmv->readUint32LE();
260 
261  // Packet video header
262 
263  byte videoHeaderData[8];
264  _xmv->read(videoHeaderData, 8);
265 
266  packet.video.dataSize = READ_LE_UINT32(videoHeaderData) & 0x007FFFFF;
267  packet.video.frameCount = (READ_LE_UINT32(videoHeaderData) >> 23) & 0xFF;
268 
269  bool hasExtraData = (videoHeaderData[3] & 0x80) != 0;
270 
271  // Adding the audio data sizes and the video data size keeps you 4 bytes short
272  // for every audio track. But as playing around with XMV files with ADPCM audio
273  // showed, taking the extra 4 bytes from the audio data gives you either
274  // completely distorted audio or click (when skipping the remaining 68 bytes of
275  // the ADPCM block). Subtracting _audioTrackCount * 4 bytes from the video
276  // data works at least for the audio. Probably some alignment thing?
277  // The video data has (always?) lots of padding, so it should work out regardless.
278  packet.video.dataSize -= _audioTrackCount * 4;
279 
280  // Packet audio header
281 
282  packet.audio.resize(_audioTrackCount);
283  for (size_t i = 0; i < packet.audio.size(); i++) {
284  PacketAudio &audioHeader = packet.audio[i];
285 
286  byte audioHeaderData[4];
287  _xmv->read(audioHeaderData, 4);
288 
289  audioHeader.dataSize = READ_LE_UINT32(audioHeaderData) & 0x007FFFFF;
290  if ((audioHeader.dataSize == 0) && (i != 0))
291  // This happens when I create an XMV with several identical audio
292  // streams. From the size calculations, duplicating the previous
293  // stream's size works out, but the track data itself is silent.
294  // Maybe this should also redirect the offset to the previous track?
295  audioHeader.dataSize = packet.audio[i - 1].dataSize;
296 
297  audioHeader.newSlice = audioHeader.dataSize > 0;
298  }
299 
300  // Packet data offsets
301 
302  size_t dataOffset = _xmv->pos();
303 
304  packet.video.dataOffset = dataOffset;
305  dataOffset += packet.video.dataSize;
306 
307  for (size_t i = 0; i < packet.audio.size(); i++) {
308  packet.audio[i].dataOffset = dataOffset;
309  dataOffset += packet.audio[i].dataSize;
310  }
311 
312  // If we have extra data, (re)create the video codec
313 
314  if (hasExtraData) {
315  if (packet.video.dataSize >= 4) {
316  Common::SeekableSubReadStream extraData(_xmv.get(), _xmv->pos(), _xmv->pos() + 4);
317 
318  _videoTrack->initCodec(extraData);
319 
320  packet.video.dataSize -= 4;
321  packet.video.dataOffset += 4;
322  } else {
323  warning("XboxMediaVideo::processPacketHeader(): Video extra data doesn't fit");
324  }
325  }
326 }
327 
329  // No frames left => we finished playing
330  if (_curPacket.video.frameCount == 0) {
331  static_cast<XMVVideoTrack &>(track).finish();
332 
333  for (uint32 i = 0; i < _audioTrackCount; i++)
334  if (_curPacket.audio[i].track)
335  _curPacket.audio[i].track->finish();
336 
337  return;
338  }
339 
340  // Process the next frame
342 
343  // Got all frames in the current packet?
344  if (_curPacket.video.frameCount == 0) {
345  // Fetch the next one and queue the audio
346 
349  }
350 }
351 
352 XboxMediaVideo::XMVVideoTrack::XMVVideoTrack(uint32 width, uint32 height, uint32 &timestamp) : _width(width), _height(height), _timestamp(timestamp), _curFrame(-1), _finished(false) {
353 }
354 
356  _curFrame++;
357 
358  if (!_videoCodec)
359  return false;
360 
361  _videoCodec->decodeFrame(surface, frameData);
362  return true;
363 }
364 
366  _videoCodec.reset(new XMVWMV2Codec(_width, _height, extraData));
367 }
368 
370  if (_curFrame < 0)
371  return 0;
372 
373  return Common::Timestamp(0, _timestamp, 1000);
374 }
375 
378 }
379 
381  return !_audioStream->endOfStream();
382 }
383 
385  _audioStream->queuePacket(stream);
386 }
387 
389  _audioStream->finish();
390 }
391 
393  return _audioStream.get();
394 }
395 
397  // Check some parameters
398  if (_info.channels == 0 || _info.channels > 2)
399  throw Common::Exception("Invalid channel count: %d", _info.channels);
400 
401  switch (_info.compression) {
402  case Sound::kWavePCM: {
404 
405  if (_info.bitsPerSample == 16) {
406  flags |= Sound::FLAG_16BITS;
407  } else if (_info.bitsPerSample != 8) {
408  throw Common::Exception("Invalid PCM sample size: %d", _info.bitsPerSample);
409  }
410 
411  return Sound::makePacketizedPCMStream(_info.rate, flags, _info.channels);
412  }
414  if (_info.bitsPerSample != 4)
415  throw Common::Exception("Invalid ADPCM sample size: %d", _info.bitsPerSample);
416 
417  return Sound::makePacketizedADPCMStream(Sound::kADPCMMSIma, _info.rate, _info.channels, _info.channels * 36);
418  default:
419  throw Common::Exception("Unhandled XMV wave format: %d", _info.compression);
420  }
421 }
422 
424  _realTracks[0].reset(track1);
425  _realTracks[1].reset(track2);
426  _realTracks[2].reset(track3);
427 
428  std::vector<Sound::AudioStream *> interleavedStreams;
429 
430  for (int i = 0; i < 3; i++)
431  interleavedStreams.push_back(_realTracks[i]->getAudioStream());
432 
433  _interleaved.reset(Sound::makeInterleaver(interleavedStreams[0]->getRate(), interleavedStreams, false));
434 }
435 
437  return !_interleaved->endOfStream();
438 }
439 
441  return _interleaved.get();
442 }
443 
444 } // End of namespace Video
uint32 currentFrameSize
The size of the current frame.
Definition: xmv.h:138
Common::ScopedPtr< Graphics::Surface > _surface
The video&#39;s surface.
Definition: decoder.h:393
PacketizedAudioStream * makePacketizedADPCMStream(ADPCMTypes type, int rate, int channels, uint32 blockAlign)
Creates a PacketizedAudioStream that will automatically queue packets as individual AudioStreams like...
Definition: adpcm.cpp:673
void load()
Load an XMV file.
Definition: xmv.cpp:125
Common::Timestamp getNextFrameStartTime() const
Get the start time of the next frame since the start of the video.
Definition: xmv.cpp:369
#define MKTAG(a0, a1, a2, a3)
A wrapper macro used around four character constants, like &#39;DATA&#39;, to ensure portability.
Definition: endianness.h:140
static const int kAudioFlagADPCM51FrontCenterLow
Definition: xmv.cpp:45
size_t thisPacketOffset
The current packet&#39;s offset within the XMV stream.
Definition: xmv.h:162
static const int kAudioFlagADPCM51FrontLeftRight
Definition: xmv.cpp:44
Common::ScopedPtr< Common::SeekableReadStream > _xmv
Definition: xmv.h:172
PacketizedAudioStream * makePacketizedPCMStream(int rate, byte flags, int channels)
Creates a PacketizedAudioStream that will automatically queue packets as individual AudioStreams like...
Definition: pcm.cpp:171
Packet _curPacket
The current packet.
Definition: xmv.h:181
void reset(PointerType o=0)
Resets the pointer with the new value.
Definition: scopedptr.h:87
XMVAudioTrack * track
The audio track mapping.
Definition: xmv.h:151
void addTrack(Track *track, bool isExternal=false)
Define a track to be used by this class.
Definition: decoder.cpp:152
bool _needCopy
Is new frame content available that needs to by copied?
Definition: decoder.h:391
uint32 dataOffset
The video data offset within the XMV stream.
Definition: xmv.h:133
An XMV packet.
Definition: xmv.h:158
void resize(typename std::vector< T *>::size_type n, typename std::vector< T *>::value_type val=typename std::vector< T *>::value_type())
Definition: ptrvector.h:76
PacketVideo video
The video part of the packet.
Definition: xmv.h:166
Implementing the reading stream interfaces for plain memory blocks.
XboxMediaVideo(Common::SeekableReadStream *xmv)
Definition: xmv.cpp:53
void processPacketHeader(Packet &packet)
Process a packet&#39;s header.
Definition: xmv.cpp:257
XMVAudioTrack51(XMVAudioTrack *info1, XMVAudioTrack *info2, XMVAudioTrack *info3)
Definition: xmv.cpp:423
void decodeNextTrackFrame(VideoTrack &track)
Decode enough data for the next frame.
Definition: xmv.cpp:328
Sound::AudioStream * getAudioStream() const
Get the AudioStream that is the representation of this AudioTrack.
Definition: xmv.cpp:440
Lossless timestamping.
Decoding PCM (Pulse Code Modulation).
bool decodeFrame(Graphics::Surface &surface, Common::SeekableReadStream &frameData)
Definition: xmv.cpp:355
Compression types in Microsoft&#39;s WAVEFORMAT(EX).
uint32 currentFrameTimestamp
The timestamp of when the current frame should be shown.
Definition: xmv.h:140
Utility templates and functions for working with strings and streams.
void finish()
Mark the stream as finished.
Definition: xmv.cpp:388
Exception that provides a stack of explanations.
Definition: error.h:36
Sound::AudioStream * getAudioStream() const
Get the AudioStream that is the representation of this AudioTrack.
Definition: xmv.cpp:392
void initVideo()
Create a surface for video of these dimensions.
Definition: decoder.cpp:71
XMVVideoTrack * _videoTrack
The current video track.
Definition: xmv.h:175
A vector of pointer to objects, with automatic deletion.
Definition: ptrvector.h:44
uint32 frameCount
Number of frames left in this packet.
Definition: xmv.h:135
static const int kAudioFlagADPCM51
Definition: xmv.cpp:47
AudioStream * makeInterleaver(int rate, const std::vector< AudioStream *> &streams, bool disposeAfterUse)
Takes several input audio streams and interleaves the sample data to create an audio stream with x ch...
Basic exceptions to throw.
Decoding ADPCM (Adaptive Differential Pulse Code Modulation).
Sound::PacketizedAudioStream * createStream() const
Definition: xmv.cpp:396
An audio packet.
Definition: xmv.h:147
bool newSlice
Is a new slice that needs to be queue available?
Definition: xmv.h:154
size_t thisPacketSize
The current packet&#39;s size.
Definition: xmv.h:159
uint16_t uint16
Definition: types.h:202
static const int kAudioFlagADPCM51RearLeftRight
Definition: xmv.cpp:46
An audio track&#39;s information.
Definition: xmv.h:58
An AudioStream designed to work in terms of packets.
Definition: audiostream.h:271
An audio stream interleaving several other audio streams.
Decoding Microsoft Xbox XMV videos.
uint32 dataSize
The audio data size.
Definition: xmv.h:148
void info(const char *s,...)
Definition: util.cpp:69
XMVAudioTrack(const AudioInfo &info)
Definition: xmv.cpp:376
size_t nextPacketSize
The next packet&#39;s size.
Definition: xmv.h:160
bool canBufferData() const
Can more audio data be buffered?
Definition: xmv.cpp:380
uint32 lastFrameTime
The timestamp of when the current last frame was shown.
Definition: xmv.h:143
XMVVideoTrack(uint32 width, uint32 height, uint32 &timestamp)
Definition: xmv.cpp:352
Common::ScopedPtr< Sound::PacketizedAudioStream > _audioStream
Definition: xmv.h:109
void processNextFrame(PacketVideo &videoPacket)
Process the next frame.
Definition: xmv.cpp:86
StackException Exception
Definition: error.h:59
An abstract representation of a video track.
Definition: decoder.h:226
A vector storing pointer to objects, with automatic deletion.
uint32 _audioTrackCount
The audio track count.
Definition: xmv.h:178
void warning(const char *s,...)
Definition: util.cpp:33
void queueAudio(Common::SeekableReadStream *stream)
Queue audio stream data belonging to this track.
Definition: xmv.cpp:384
sound is 16 bits wide (default: 8bit)
Definition: pcm.h:72
Generic audio input stream.
Definition: audiostream.h:70
PointerType get() const
Returns the plain pointer value.
Definition: scopedptr.h:96
std::vector< PacketAudio > audio
The audio part of the packet.
Definition: xmv.h:169
uint32 _height
Definition: h263.cpp:54
size_t nextPacketOffset
The next packet&#39;s offset within the XMV stream.
Definition: xmv.h:163
void queueNewAudio(PacketAudio &audioPacket)
Queue the data from this audio packet.
Definition: xmv.cpp:64
uint32_t uint32
Definition: types.h:204
UString debugTag(uint32 tag, bool trim)
Create an elaborate string from an integer tag, for debugging purposes.
Definition: strutil.cpp:117
An XMV 5.1 audio track.
Definition: xmv.h:114
void initCodec(Common::SeekableReadStream &extraData)
Definition: xmv.cpp:365
Timestamps allow specifying points in time and measuring time intervals with a sub-millisecond granul...
Definition: timestamp.h:108
uint32 dataSize
The video data size.
Definition: xmv.h:132
samples are little endian (default: big endian)
Definition: pcm.h:75
WMV2 video codec, XMV variant.
SeekableSubReadStream provides access to a SeekableReadStream restricted to the range [begin...
Definition: readstream.h:359
const char * what() const
Definition: error.cpp:73
uint32 _width
Definition: h263.cpp:53
Interface for a seekable & readable data stream.
Definition: readstream.h:265
Streaming audio.
bool canBufferData() const
Can more audio data be buffered?
Definition: xmv.cpp:436
uint8 byte
Definition: types.h:209
uint32 dataOffset
The audio data offset within the XMV stream.
Definition: xmv.h:149
void fetchNextPacket(Packet &packet)
Fetch the next packet.
Definition: xmv.cpp:240
An XMV audio track.
Definition: xmv.h:93