const Graphics::Surface *RlfAnimation::getFrameData(uint frameNumber) { assert(!_stream); assert(frameNumber < _frameCount); // Since this method is so expensive, first check to see if we can use // decodeNextFrame() it's cheap. if ((int)frameNumber == _nextFrame - 1) { return &_currentFrameBuffer; } else if (_nextFrame == (int)frameNumber) { return decodeNextFrame(); } seekToFrame(frameNumber); return decodeNextFrame(); }
void MoviePlayerSMK::nextFrame() { if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) rewind(); if (!endOfVideo()) { decodeNextFrame(); if (_vm->_interactiveVideo == TYPE_OMNITV) { copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth); } else if (_vm->_interactiveVideo == TYPE_LOOPING) { copyFrameToBuffer(_vm->getBackBuf(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth); } } else if (_vm->_interactiveVideo == TYPE_OMNITV) { close(); _vm->_interactiveVideo = 0; _vm->_variableArray[254] = 6747; } }
void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { uint h = getHeight(); uint w = getWidth(); const Graphics::Surface *surface = decodeNextFrame(); byte *src = (byte *)surface->pixels; dst += y * pitch + x; do { memcpy(dst, src, w); dst += pitch; src += w; } while (--h); if (hasDirtyPalette()) setSystemPalette(); }
bool AudioDecoder::decodeNextFrame( Frame& frameBuffer, const size_t subStreamIndex ) { if( ! decodeNextFrame() ) return false; AVCodecContext& avCodecContext = _inputStream->getAudioCodec().getAVCodecContext(); const int output_nbChannels = 1; const int output_align = 1; size_t decodedSize = av_samples_get_buffer_size(NULL, output_nbChannels, _frame->nb_samples, avCodecContext.sample_fmt, output_align); size_t nbSubStreams = avCodecContext.channels; size_t bytePerSample = av_get_bytes_per_sample( (AVSampleFormat)_frame->format ); if( subStreamIndex > nbSubStreams - 1 ) { throw std::runtime_error( "The subStream doesn't exist"); } if( decodedSize == 0 ) return false; AudioFrame& audioBuffer = static_cast<AudioFrame&>( frameBuffer ); audioBuffer.setNbSamples( _frame->nb_samples ); audioBuffer.resize( decodedSize ); // @todo manage cases with data of frame not only on data[0] (use _frame.linesize) unsigned char* src = _frame->data[0]; unsigned char* dst = audioBuffer.getData(); // offset src += subStreamIndex * bytePerSample; for( int sample = 0; sample < _frame->nb_samples; ++sample ) { memcpy( dst, src, bytePerSample ); dst += bytePerSample; src += bytePerSample * nbSubStreams; } return true; }
void AVIDecoder::checkTruemotion1() { AVIVideoTrack *track = 0; for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) { if ((*it)->getTrackType() == Track::kTrackTypeVideo) { if (track) { // Multiple tracks; isn't going to be truemotion 1 return; } track = (AVIVideoTrack *)*it; } } // No track found? if (!track) return; // Ignore non-truemotion tracks if (!track->isTruemotion1()) return; // Search for a non-empty frame const Graphics::Surface *frame = 0; for (int i = 0; i < 10 && !frame; i++) frame = decodeNextFrame(); if (!frame) { // Probably shouldn't happen rewind(); return; } // Fill in the width/height based on the frame's width/height _header.width = frame->w; _header.height = frame->h; track->forceDimensions(frame->w, frame->h); // Rewind us back to the beginning rewind(); }
void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { uint h = getHeight(); uint w = getWidth(); const Graphics::Surface *surface = decodeNextFrame(); if (!surface) return; const byte *src = (const byte *)surface->getPixels(); dst += y * pitch + x; do { memcpy(dst, src, w); dst += pitch; src += w; } while (--h); if (hasDirtyPalette()) g_system->getPaletteManager()->setPalette(getPalette(), 0, 256); }
void QuickTimeDecoder::seekToFrame(uint32 frame) { assert(_videoTrackIndex >= 0); assert(frame < _tracks[_videoTrackIndex]->frameCount); // Stop all audio (for now) stopAudio(); // Track down the keyframe _curFrame = findKeyFrame(frame) - 1; while (_curFrame < (int32)frame - 1) decodeNextFrame(); // Map out the starting point _nextFrameStartTime = 0; uint32 curFrame = 0; for (int32 i = 0; i < _tracks[_videoTrackIndex]->timeToSampleCount && curFrame < frame; i++) { for (int32 j = 0; j < _tracks[_videoTrackIndex]->timeToSample[i].count && curFrame < frame; j++) { curFrame++; _nextFrameStartTime += _tracks[_videoTrackIndex]->timeToSample[i].duration; } } // Adjust the video starting point const Audio::Timestamp curVideoTime(0, _nextFrameStartTime, _tracks[_videoTrackIndex]->timeScale); _startTime = g_system->getMillis() - curVideoTime.msecs(); resetPauseStartTime(); // Adjust the audio starting point if (_audioTrackIndex >= 0) { _audioStartOffset = curVideoTime; // Seek to the new audio location setAudioStreamPos(_audioStartOffset); // Restart the audio startAudio(); } }
void MoviePlayer::copyFrameToBuffer(byte *dst, int dstType, uint x, uint y, uint pitch) { uint h = getHeight(); uint w = getWidth(); const Graphics::Surface *surface = decodeNextFrame(); byte *src = (byte *)surface->pixels; if (hasDirtyPalette()) _vm->setPaletteFromPtr(getPalette(), 256); if (_vm->_game.features & GF_16BIT_COLOR) { dst += y * pitch + x * 2; do { for (uint i = 0; i < w; i++) { uint16 color = READ_LE_UINT16(_vm->_hePalettes + _vm->_hePaletteSlot + 768 + src[i] * 2); switch (dstType) { case kDstScreen: WRITE_UINT16(dst + i * 2, color); break; case kDstResource: WRITE_LE_UINT16(dst + i * 2, color); break; default: error("copyFrameToBuffer: Unknown dstType %d", dstType); } } dst += pitch; src += w; } while (--h); } else { dst += y * pitch + x; do { memcpy(dst, src, w); dst += pitch; src += w; } while (--h); } }
bool AudioDecoder::decodeNextFrame( Frame& frameBuffer ) { if( ! decodeNextFrame() ) return false; AVCodecContext& avCodecContext = _inputStream->getAudioCodec().getAVCodecContext(); size_t decodedSize = av_samples_get_buffer_size( NULL, avCodecContext.channels, _frame->nb_samples, avCodecContext.sample_fmt, 1 ); if( decodedSize == 0 ) return false; AudioFrame& audioBuffer = static_cast<AudioFrame&>( frameBuffer ); audioBuffer.setNbSamples( _frame->nb_samples ); audioBuffer.resize( decodedSize ); // @todo manage cases with data of frame not only on data[0] (use _frame.linesize) unsigned char* const src = _frame->data[0]; unsigned char* dst = audioBuffer.getData(); av_samples_copy( &dst, &src, 0, 0, _frame->nb_samples, avCodecContext.channels, avCodecContext.sample_fmt ); return true; }
const Graphics::Surface *AviDecoder::decodeNextFrame() { uint32 nextTag = _fileStream->readUint32BE(); if (_fileStream->eos()) return NULL; if (_curFrame == -1) _startTime = g_system->getMillis(); if (nextTag == ID_LIST) { // A list of audio/video chunks uint32 listSize = _fileStream->readUint32LE() - 4; int32 startPos = _fileStream->pos(); if (_fileStream->readUint32BE() != ID_REC) error ("Expected 'rec ' LIST"); // Decode chunks in the list and see if we get a frame const Graphics::Surface *frame = NULL; while (_fileStream->pos() < startPos + (int32)listSize) { const Graphics::Surface *temp = decodeNextFrame(); if (temp) frame = temp; } return frame; } else if (getStreamType(nextTag) == 'wb') { // Audio Chunk uint32 chunkSize = _fileStream->readUint32LE(); queueAudioBuffer(chunkSize); _fileStream->skip(chunkSize & 1); // Alignment } else if (getStreamType(nextTag) == 'dc' || getStreamType(nextTag) == 'id' || getStreamType(nextTag) == 'AM' || getStreamType(nextTag) == '32' || getStreamType(nextTag) == 'iv') { // Compressed Frame _curFrame++; uint32 chunkSize = _fileStream->readUint32LE(); if (chunkSize == 0) // Keep last frame on screen return NULL; Common::SeekableReadStream *frameData = _fileStream->readStream(chunkSize); const Graphics::Surface *surface = _videoCodec->decodeImage(frameData); delete frameData; _fileStream->skip(chunkSize & 1); // Alignment return surface; } else if (getStreamType(nextTag) == 'pc') { // Palette Change _fileStream->readUint32LE(); // Chunk size, not needed here byte firstEntry = _fileStream->readByte(); uint16 numEntries = _fileStream->readByte(); _fileStream->readUint16LE(); // Reserved // 0 entries means all colors are going to be changed if (numEntries == 0) numEntries = 256; for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) { _palette[i * 3] = _fileStream->readByte(); _palette[i * 3 + 1] = _fileStream->readByte(); _palette[i * 3 + 2] = _fileStream->readByte(); _fileStream->readByte(); // Flags that don't serve us any purpose } _dirtyPalette = true; // No alignment necessary. It's always even. } else if (nextTag == ID_JUNK) { runHandle(ID_JUNK); } else if (nextTag == ID_IDX1) { runHandle(ID_IDX1); } else error("Tag = \'%s\', %d", tag2str(nextTag), _fileStream->pos()); return NULL; }
static bool mp3_decode_first_frame(struct mp3_data *data, struct tag **tag, struct replay_gain_info **replay_gain_info_r) { struct xing xing; struct lame lame; struct mad_bitptr ptr; int bitlen; enum mp3_action ret; /* stfu gcc */ memset(&xing, 0, sizeof(struct xing)); xing.flags = 0; while (true) { do { ret = decode_next_frame_header(data, tag, replay_gain_info_r); } while (ret == DECODE_CONT); if (ret == DECODE_BREAK) return false; if (ret == DECODE_SKIP) continue; do { ret = decodeNextFrame(data); } while (ret == DECODE_CONT); if (ret == DECODE_BREAK) return false; if (ret == DECODE_OK) break; } ptr = data->stream.anc_ptr; bitlen = data->stream.anc_bitlen; mp3_filesize_to_song_length(data); /* * if an xing tag exists, use that! */ if (parse_xing(&xing, &ptr, &bitlen)) { data->found_xing = true; data->mute_frame = MUTEFRAME_SKIP; if ((xing.flags & XING_FRAMES) && xing.frames) { mad_timer_t duration = data->frame.header.duration; mad_timer_multiply(&duration, xing.frames); data->total_time = ((float)mad_timer_count(duration, MAD_UNITS_MILLISECONDS)) / 1000; data->max_frames = xing.frames; } if (parse_lame(&lame, &ptr, &bitlen)) { if (gapless_playback && data->input_stream->seekable) { data->drop_start_samples = lame.encoder_delay + DECODERDELAY; data->drop_end_samples = lame.encoder_padding; } /* Album gain isn't currently used. See comment in * parse_lame() for details. -- jat */ if (replay_gain_info_r && !*replay_gain_info_r && lame.track_gain) { *replay_gain_info_r = replay_gain_info_new(); (*replay_gain_info_r)->tuples[REPLAY_GAIN_TRACK].gain = lame.track_gain; (*replay_gain_info_r)->tuples[REPLAY_GAIN_TRACK].peak = lame.peak; } } } if (!data->max_frames) return false; if (data->max_frames > 8 * 1024 * 1024) { g_warning("mp3 file header indicates too many frames: %lu\n", data->max_frames); return false; } data->frame_offsets = g_malloc(sizeof(long) * data->max_frames); data->times = g_malloc(sizeof(mad_timer_t) * data->max_frames); return true; }
static bool mp3_read(struct mp3_data *data, struct replay_gain_info **replay_gain_info_r) { struct decoder *decoder = data->decoder; enum mp3_action ret; enum decoder_command cmd; mp3_update_timer_next_frame(data); switch (data->mute_frame) { case MUTEFRAME_SKIP: data->mute_frame = MUTEFRAME_NONE; break; case MUTEFRAME_SEEK: if (data->elapsed_time >= data->seek_where) data->mute_frame = MUTEFRAME_NONE; break; case MUTEFRAME_NONE: cmd = mp3_synth_and_send(data, replay_gain_info_r != NULL ? *replay_gain_info_r : NULL); if (cmd == DECODE_COMMAND_SEEK) { unsigned long j; assert(data->input_stream->seekable); j = mp3_time_to_frame(data, decoder_seek_where(decoder)); if (j < data->highest_frame) { if (mp3_seek(data, data->frame_offsets[j])) { data->current_frame = j; decoder_command_finished(decoder); } else decoder_seek_error(decoder); } else { data->seek_where = decoder_seek_where(decoder); data->mute_frame = MUTEFRAME_SEEK; decoder_command_finished(decoder); } } else if (cmd != DECODE_COMMAND_NONE) return false; } while (true) { bool skip = false; do { struct tag *tag = NULL; ret = decode_next_frame_header(data, &tag, replay_gain_info_r); if (tag != NULL) { decoder_tag(decoder, data->input_stream, tag); tag_free(tag); } } while (ret == DECODE_CONT); if (ret == DECODE_BREAK) return false; else if (ret == DECODE_SKIP) skip = true; if (data->mute_frame == MUTEFRAME_NONE) { do { ret = decodeNextFrame(data); } while (ret == DECODE_CONT); if (ret == DECODE_BREAK) return false; } if (!skip && ret == DECODE_OK) break; } return ret != DECODE_BREAK; }
void QuickTimeDecoder::seekToFrame(uint32 frame) { assert(_videoStreamIndex >= 0); assert(frame < _streams[_videoStreamIndex]->nb_frames); // Stop all audio (for now) stopAudio(); // Track down the keyframe _curFrame = findKeyFrame(frame) - 1; while (_curFrame < (int32)frame - 1) decodeNextFrame(); // Map out the starting point _nextFrameStartTime = 0; uint32 curFrame = 0; for (int32 i = 0; i < _streams[_videoStreamIndex]->stts_count && curFrame < frame; i++) { for (int32 j = 0; j < _streams[_videoStreamIndex]->stts_data[i].count && curFrame < frame; j++) { curFrame++; _nextFrameStartTime += _streams[_videoStreamIndex]->stts_data[i].duration; } } // Adjust the video starting point const Audio::Timestamp curVideoTime(0, _nextFrameStartTime, _streams[_videoStreamIndex]->time_scale); _startTime = g_system->getMillis() - curVideoTime.msecs(); resetPauseStartTime(); // Adjust the audio starting point if (_audioStreamIndex >= 0) { _audioStartOffset = curVideoTime; // Re-create the audio stream STSDEntry *entry = &_streams[_audioStreamIndex]->stsdEntries[0]; _audStream = Audio::makeQueuingAudioStream(entry->sampleRate, entry->channels == 2); // First, we need to track down what audio sample we need Audio::Timestamp curAudioTime(0, _streams[_audioStreamIndex]->time_scale); uint sample = 0; bool done = false; for (int32 i = 0; i < _streams[_audioStreamIndex]->stts_count && !done; i++) { for (int32 j = 0; j < _streams[_audioStreamIndex]->stts_data[i].count; j++) { curAudioTime = curAudioTime.addFrames(_streams[_audioStreamIndex]->stts_data[i].duration); if (curAudioTime > curVideoTime) { done = true; break; } sample++; } } // Now to track down what chunk it's in _curAudioChunk = 0; uint32 totalSamples = 0; for (uint32 i = 0; i < _streams[_audioStreamIndex]->chunk_count; i++, _curAudioChunk++) { int sampleToChunkIndex = -1; for (uint32 j = 0; j < _streams[_audioStreamIndex]->sample_to_chunk_sz; j++) if (i >= _streams[_audioStreamIndex]->sample_to_chunk[j].first) sampleToChunkIndex = j; assert(sampleToChunkIndex >= 0); totalSamples += _streams[_audioStreamIndex]->sample_to_chunk[sampleToChunkIndex].count; if (sample < totalSamples) { totalSamples -= _streams[_audioStreamIndex]->sample_to_chunk[sampleToChunkIndex].count; break; } } // Reposition the audio stream readNextAudioChunk(); if (sample != totalSamples) { // HACK: Skip a certain amount of samples from the stream // (There's got to be a better way to do this!) int16 *tempBuffer = new int16[sample - totalSamples]; _audStream->readBuffer(tempBuffer, sample - totalSamples); delete[] tempBuffer; debug(3, "Skipping %d audio samples", sample - totalSamples); } // Restart the audio startAudio(); } }