mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-22 20:21:06 +00:00
MTROPOLIS: Add movie loops
This commit is contained in:
parent
508610ad22
commit
562feebd3c
@ -603,8 +603,18 @@ MiniscriptInstructionOutcome MovieElement::writeRefAttribute(MiniscriptThread *t
|
|||||||
}
|
}
|
||||||
|
|
||||||
VThreadState MovieElement::consumeCommand(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
|
VThreadState MovieElement::consumeCommand(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
|
||||||
|
// The reaction to the Play command should be to fire Unpaused and then fire Played.
|
||||||
|
// At First Cel is NOT fired by Play commands for some reason.
|
||||||
|
|
||||||
if (Event::create(EventIDs::kPlay, 0).respondsTo(msg->getEvent())) {
|
if (Event::create(EventIDs::kPlay, 0).respondsTo(msg->getEvent())) {
|
||||||
// These reverse order
|
if (_paused)
|
||||||
|
{
|
||||||
|
_paused = false;
|
||||||
|
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kUnpause, 0), DynamicValue(), getSelfReference()));
|
||||||
|
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||||
|
runtime->sendMessageOnVThread(dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kPlay, 0), DynamicValue(), getSelfReference()));
|
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kPlay, 0), DynamicValue(), getSelfReference()));
|
||||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||||
@ -690,6 +700,17 @@ bool MovieElement::canAutoPlay() const {
|
|||||||
return _visible && !_paused;
|
return _visible && !_paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MovieElement::queueAutoPlayEvents(Runtime *runtime, bool isAutoPlaying) {
|
||||||
|
// At First Cel event fires even if the movie isn't playing, and it fires before Played
|
||||||
|
if (_visible) {
|
||||||
|
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kAtFirstCel, 0), DynamicValue(), getSelfReference()));
|
||||||
|
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||||
|
runtime->queueMessage(dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
VisualElement::queueAutoPlayEvents(runtime, isAutoPlaying);
|
||||||
|
}
|
||||||
|
|
||||||
void MovieElement::render(Window *window) {
|
void MovieElement::render(Window *window) {
|
||||||
if (_needsReset) {
|
if (_needsReset) {
|
||||||
_videoDecoder->setReverse(_reversed);
|
_videoDecoder->setReverse(_reversed);
|
||||||
@ -731,7 +752,8 @@ void MovieElement::playMedia(Runtime *runtime, Project *project) {
|
|||||||
if (_videoDecoder->isPaused())
|
if (_videoDecoder->isPaused())
|
||||||
_videoDecoder->pauseVideo(false);
|
_videoDecoder->pauseVideo(false);
|
||||||
|
|
||||||
_currentPlayState = kMediaStatePlaying;
|
if (_currentPlayState != kMediaStatePlayingLastFrame)
|
||||||
|
_currentPlayState = kMediaStatePlaying;
|
||||||
checkContinuously = true;
|
checkContinuously = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -747,25 +769,25 @@ void MovieElement::playMedia(Runtime *runtime, Project *project) {
|
|||||||
uint32 targetTS = _currentTimestamp;
|
uint32 targetTS = _currentTimestamp;
|
||||||
|
|
||||||
int framesDecodedThisFrame = 0;
|
int framesDecodedThisFrame = 0;
|
||||||
while (_videoDecoder->needsUpdate()) {
|
|
||||||
if (_playEveryFrame && framesDecodedThisFrame > 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
const Graphics::Surface *decodedFrame = _videoDecoder->decodeNextFrame();
|
|
||||||
|
|
||||||
// GNARLY HACK: QuickTimeDecoder doesn't return true for endOfVideo or false for needsUpdate until it
|
|
||||||
// tries decoding past the end, so we're assuming that the decoded frame memory stays valid until we
|
|
||||||
// actually have a new frame and continuing to use it.
|
|
||||||
if (decodedFrame) {
|
|
||||||
_contentsDirty = true;
|
|
||||||
framesDecodedThisFrame++;
|
|
||||||
_displayFrame = decodedFrame;
|
|
||||||
if (_playEveryFrame)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentPlayState == kMediaStatePlaying) {
|
if (_currentPlayState == kMediaStatePlaying) {
|
||||||
|
while (_videoDecoder->needsUpdate()) {
|
||||||
|
if (_playEveryFrame && framesDecodedThisFrame > 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
const Graphics::Surface *decodedFrame = _videoDecoder->decodeNextFrame();
|
||||||
|
|
||||||
|
// QuickTimeDecoder doesn't return true for endOfVideo or false for needsUpdate until it
|
||||||
|
// tries decoding past the end, so we're assuming that the decoded frame memory stays valid until we
|
||||||
|
// actually have a new frame and continuing to use it.
|
||||||
|
if (decodedFrame) {
|
||||||
|
_contentsDirty = true;
|
||||||
|
framesDecodedThisFrame++;
|
||||||
|
_displayFrame = decodedFrame;
|
||||||
|
if (_playEveryFrame)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_videoDecoder->endOfVideo())
|
if (_videoDecoder->endOfVideo())
|
||||||
targetTS = _reversed ? _playRange.min : _playRange.max;
|
targetTS = _reversed ? _playRange.min : _playRange.max;
|
||||||
else
|
else
|
||||||
@ -782,10 +804,14 @@ void MovieElement::playMedia(Runtime *runtime, Project *project) {
|
|||||||
|
|
||||||
// Sync TS to the end of video if we hit the end
|
// Sync TS to the end of video if we hit the end
|
||||||
|
|
||||||
|
bool triggerEndEvents = false;
|
||||||
|
|
||||||
|
if (_currentPlayState == kMediaStatePlayingLastFrame)
|
||||||
|
triggerEndEvents = true;
|
||||||
|
|
||||||
if (targetTS != _currentTimestamp) {
|
if (targetTS != _currentTimestamp) {
|
||||||
assert(!_paused);
|
assert(!_paused);
|
||||||
|
|
||||||
|
|
||||||
// Check media cues
|
// Check media cues
|
||||||
for (MediaCueState *mediaCue : _mediaCues)
|
for (MediaCueState *mediaCue : _mediaCues)
|
||||||
mediaCue->checkTimestampChange(runtime, _currentTimestamp, targetTS, checkContinuously, true);
|
mediaCue->checkTimestampChange(runtime, _currentTimestamp, targetTS, checkContinuously, true);
|
||||||
@ -793,33 +819,38 @@ void MovieElement::playMedia(Runtime *runtime, Project *project) {
|
|||||||
_currentTimestamp = targetTS;
|
_currentTimestamp = targetTS;
|
||||||
|
|
||||||
if (_currentTimestamp == maxTS) {
|
if (_currentTimestamp == maxTS) {
|
||||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kAtLastCel, 0), DynamicValue(), getSelfReference()));
|
if (maxTS == _maxTimestamp) {
|
||||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
// If this play range plays through to the end, then delay end events 1 frame so it has a chance to render
|
||||||
runtime->queueMessage(dispatch);
|
_currentPlayState = kMediaStatePlayingLastFrame;
|
||||||
}
|
} else
|
||||||
|
triggerEndEvents = true;
|
||||||
if (_currentTimestamp == minTS) {
|
|
||||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kAtFirstCel, 0), DynamicValue(), getSelfReference()));
|
|
||||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
|
||||||
runtime->queueMessage(dispatch);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentPlayState == kMediaStatePlaying && _videoDecoder->endOfVideo()) {
|
if (triggerEndEvents) {
|
||||||
if (_alternate) {
|
if (!_loop) {
|
||||||
_reversed = !_reversed;
|
_paused = true;
|
||||||
if (!_videoDecoder->setReverse(_reversed)) {
|
|
||||||
warning("Failed to reverse video decoder, disabling it");
|
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kPause, 0), DynamicValue(), getSelfReference()));
|
||||||
_videoDecoder.reset();
|
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||||
}
|
runtime->queueMessage(dispatch);
|
||||||
|
|
||||||
uint32 endTS = _reversed ? _playRange.min : _playRange.max;
|
|
||||||
_videoDecoder->setEndTime(Audio::Timestamp(0, _timeScale).addFrames(endTS));
|
|
||||||
} else {
|
|
||||||
// It doesn't look like movies fire any events upon reaching the end, just At Last Cel and At First Cel
|
|
||||||
_videoDecoder->stop();
|
|
||||||
_currentPlayState = kMediaStateStopped;
|
_currentPlayState = kMediaStateStopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kAtLastCel, 0), DynamicValue(), getSelfReference()));
|
||||||
|
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||||
|
runtime->queueMessage(dispatch);
|
||||||
|
|
||||||
|
// For some reason, At First Cel isn't fired for movies, even when they loop or are set to timevalue 0
|
||||||
|
_videoDecoder->stop();
|
||||||
|
_currentPlayState = kMediaStateStopped;
|
||||||
|
|
||||||
|
if (_loop) {
|
||||||
|
_needsReset = true;
|
||||||
|
_currentTimestamp = _reversed ? _playRange.max : _playRange.min;
|
||||||
|
_contentsDirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -951,6 +982,7 @@ VThreadState MovieElement::startPlayingTask(const StartPlayingTaskData &taskData
|
|||||||
_videoDecoder->stop();
|
_videoDecoder->stop();
|
||||||
_currentPlayState = kMediaStateStopped;
|
_currentPlayState = kMediaStateStopped;
|
||||||
_needsReset = true;
|
_needsReset = true;
|
||||||
|
_contentsDirty = true;
|
||||||
|
|
||||||
_shouldPlayIfNotPaused = true;
|
_shouldPlayIfNotPaused = true;
|
||||||
_paused = false;
|
_paused = false;
|
||||||
|
@ -44,6 +44,7 @@ struct MToonMetadata;
|
|||||||
|
|
||||||
enum MediaState {
|
enum MediaState {
|
||||||
kMediaStatePlaying,
|
kMediaStatePlaying,
|
||||||
|
kMediaStatePlayingLastFrame,
|
||||||
kMediaStateStopped,
|
kMediaStateStopped,
|
||||||
kMediaStatePaused,
|
kMediaStatePaused,
|
||||||
};
|
};
|
||||||
@ -87,6 +88,7 @@ public:
|
|||||||
void deactivate() override;
|
void deactivate() override;
|
||||||
|
|
||||||
bool canAutoPlay() const override;
|
bool canAutoPlay() const override;
|
||||||
|
void queueAutoPlayEvents(Runtime *runtime, bool isAutoPlaying) override;
|
||||||
|
|
||||||
void render(Window *window) override;
|
void render(Window *window) override;
|
||||||
void playMedia(Runtime *runtime, Project *project) override;
|
void playMedia(Runtime *runtime, Project *project) override;
|
||||||
|
@ -2963,8 +2963,7 @@ MiniscriptInstructionOutcome Structural::scriptSetPaused(MiniscriptThread *threa
|
|||||||
// while at the Bureau light carousel, since the lever isn't flagged as paused but is set paused
|
// while at the Bureau light carousel, since the lever isn't flagged as paused but is set paused
|
||||||
// via an init script, and the lever trigger is detected via the pause event.
|
// via an init script, and the lever trigger is detected via the pause event.
|
||||||
//
|
//
|
||||||
// (It's possible that this is actually yet another case of the event simply not being sent when the
|
// The event does, however, need to be sent immediately.
|
||||||
// property is set from script... need to verify and update this comment.)
|
|
||||||
if (!thread->getRuntime()->isAwaitingSceneTransition()) {
|
if (!thread->getRuntime()->isAwaitingSceneTransition()) {
|
||||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(targetValue ? EventIDs::kPause : EventIDs::kUnpause, 0), DynamicValue(), getSelfReference()));
|
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(targetValue ? EventIDs::kPause : EventIDs::kUnpause, 0), DynamicValue(), getSelfReference()));
|
||||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||||
@ -6435,6 +6434,14 @@ bool Element::canAutoPlay() const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Element::queueAutoPlayEvents(Runtime *runtime, bool isAutoPlaying) {
|
||||||
|
if (isAutoPlaying) {
|
||||||
|
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kPlay, 0), DynamicValue(), getSelfReference()));
|
||||||
|
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, false, true));
|
||||||
|
runtime->queueMessage(dispatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Element::isElement() const {
|
bool Element::isElement() const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -6462,11 +6469,7 @@ void Element::triggerAutoPlay(Runtime *runtime) {
|
|||||||
|
|
||||||
_haveCheckedAutoPlay = true;
|
_haveCheckedAutoPlay = true;
|
||||||
|
|
||||||
if (canAutoPlay()) {
|
queueAutoPlayEvents(runtime, canAutoPlay());
|
||||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kPlay, 0), DynamicValue(), getSelfReference()));
|
|
||||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, false, true));
|
|
||||||
runtime->queueMessage(dispatch);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Element::resolveMediaMarkerLabel(const Label& label, int32 &outResolution) const {
|
bool Element::resolveMediaMarkerLabel(const Label& label, int32 &outResolution) const {
|
||||||
|
@ -2263,6 +2263,8 @@ public:
|
|||||||
|
|
||||||
virtual bool isVisual() const = 0;
|
virtual bool isVisual() const = 0;
|
||||||
virtual bool canAutoPlay() const;
|
virtual bool canAutoPlay() const;
|
||||||
|
virtual void queueAutoPlayEvents(Runtime *runtime, bool isAutoPlaying);
|
||||||
|
|
||||||
bool isElement() const override;
|
bool isElement() const override;
|
||||||
|
|
||||||
uint32 getStreamLocator() const;
|
uint32 getStreamLocator() const;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user