* Make PlaybackManager::CurrentFrame() return int64, like

what it's internally using. Adapt NotifyCurrentFrameChanged()
   implementors.
 * Controller::SetTo() does not need to set the current frame,
   since either Init() or FormatChanged() will have taken care
   of it now.
 * Reset the seeking request info in Controller::SetTo().
 * Changed the parameter passed to VideoSupplier::FillBuffer()
   from media_format to media_raw_video_format, so the
   VideoProducer doesn't have to generate a media_format for
   each frame...
 * ProxyVideoSupplier caches the last produced frame, which
   avoids a situation that the VideoProducer asks to generate
   the same frame twice sometimes.
 * In PlaybackManager::_PushState(), make sure we really
   schedule the next current_frame if asked. This avoids
   one situation in which the VideoSupplier was asked to generate
   the same frame again and fixes playback after pausing (was
   showing black video until the next keyframe before.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@38553 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2010-09-06 19:27:25 +00:00
parent d859486b15
commit 818577b203
11 changed files with 100 additions and 45 deletions
+4 -2
View File
@@ -261,6 +261,9 @@ Controller::SetTo(const PlaylistItemRef& item)
fDuration = 0; fDuration = 0;
fVideoFrameRate = 25.0; fVideoFrameRate = 25.0;
fSeekFrame = -1;
fSeekRequested = false;
if (fItem.Get() == NULL) if (fItem.Get() == NULL)
return B_BAD_VALUE; return B_BAD_VALUE;
@@ -369,7 +372,6 @@ Controller::SetTo(const PlaylistItemRef& item)
_NotifyFileChanged(item.Get(), B_OK); _NotifyFileChanged(item.Get(), B_OK);
SetPosition(0.0);
if (fAutoplay) if (fAutoplay)
StartPlaying(true); StartPlaying(true);
@@ -1080,7 +1082,7 @@ Controller::NotifyFPSChanged(float fps) const
void void
Controller::NotifyCurrentFrameChanged(int32 frame) const Controller::NotifyCurrentFrameChanged(int64 frame) const
{ {
// check if we are still waiting to reach the seekframe, // check if we are still waiting to reach the seekframe,
// don't pass the event on to the listeners in that case // don't pass the event on to the listeners in that case
+1 -1
View File
@@ -168,7 +168,7 @@ private:
bool enabled) const; bool enabled) const;
virtual void NotifyVideoBoundsChanged(BRect bounds) const; virtual void NotifyVideoBoundsChanged(BRect bounds) const;
virtual void NotifyFPSChanged(float fps) const; virtual void NotifyFPSChanged(float fps) const;
virtual void NotifyCurrentFrameChanged(int32 frame) const; virtual void NotifyCurrentFrameChanged(int64 frame) const;
virtual void NotifySpeedChanged(float speed) const; virtual void NotifySpeedChanged(float speed) const;
virtual void NotifyFrameDropped() const; virtual void NotifyFrameDropped() const;
virtual void NotifyStopFrameReached() const; virtual void NotifyStopFrameReached() const;
@@ -323,7 +323,7 @@ PlaybackManager::IsLoopingEnabled() const
} }
int32 int64
PlaybackManager::CurrentFrame() const PlaybackManager::CurrentFrame() const
{ {
return PlaylistFrameAtFrame(FrameForTime(fPerformanceTime)); return PlaylistFrameAtFrame(FrameForTime(fPerformanceTime));
@@ -940,11 +940,11 @@ PlaybackManager::SetPerformanceFrame(int64 frame)
void void
PlaybackManager::SetPerformanceTime(bigtime_t time) PlaybackManager::SetPerformanceTime(bigtime_t time)
{ {
int32 oldCurrentFrame = CurrentFrame(); int64 oldCurrentFrame = CurrentFrame();
fPerformanceTime = time; fPerformanceTime = time;
_UpdateStates(); _UpdateStates();
_UpdateSpeedInfos(); _UpdateSpeedInfos();
int32 currentFrame = CurrentFrame(); int64 currentFrame = CurrentFrame();
//printf("PlaybackManager::SetPerformanceTime(%lld): %ld -> %ld\n", //printf("PlaybackManager::SetPerformanceTime(%lld): %ld -> %ld\n",
// time, oldCurrentFrame, currentFrame); // time, oldCurrentFrame, currentFrame);
@@ -1049,7 +1049,7 @@ PlaybackManager::NotifyFPSChanged(float fps) const
void void
PlaybackManager::NotifyCurrentFrameChanged(int32 frame) const PlaybackManager::NotifyCurrentFrameChanged(int64 frame) const
{ {
for (int32 i = 0; for (int32 i = 0;
PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i); PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
@@ -1123,6 +1123,9 @@ PlaybackManager::PrintStateAtFrame(int64 frame)
} }
// #pragma mark -
/*! Appends the supplied state to the list of states. If the state would /*! Appends the supplied state to the list of states. If the state would
become active at the same time as _LastState() the latter is removed become active at the same time as _LastState() the latter is removed
and deleted. However, the activation time for the new state is adjusted, and deleted. However, the activation time for the new state is adjusted,
@@ -1150,18 +1153,23 @@ TRACE("PlaybackManager::_PushState()\n");
int64 activationFrame = max(max(state->activation_frame, int64 activationFrame = max(max(state->activation_frame,
lastState->activation_frame), lastState->activation_frame),
NextFrame()); NextFrame());
TRACE(" state activation frame: %lld, last state activation frame: %lld, "
"NextFrame(): %lld\n", state->activation_frame, lastState->activation_frame,
NextFrame());
int64 currentFrame = 0; int64 currentFrame = 0;
// remember the current frame, if necessary // remember the current frame, if necessary
if (adjustCurrentFrame) if (adjustCurrentFrame) {
currentFrame = PlaylistFrameAtFrame(activationFrame); currentFrame = PlaylistFrameAtFrame(activationFrame);
// check whether it is active if (currentFrame == CurrentFrame()) {
// Seems to be paused, force using the next frame
currentFrame++;
}
}
// Check whether the last state has already become active
// (NOTE: We may want to keep the last state, if it is not active, // (NOTE: We may want to keep the last state, if it is not active,
// but then the new state should become active after the last one. // but then the new state should become active after the last one.
// Thus we had to replace `NextFrame()' with `activationFrame'.) // Thus we had to replace `NextFrame()' with `activationFrame'.)
TRACE(" state activation frame: %lld, last state activation frame: %lld, "
"NextFrame(): %lld, currentFrame: %lld, next currentFrame: %lld\n",
state->activation_frame, lastState->activation_frame, NextFrame(),
CurrentFrame(), currentFrame);
if (lastState->activation_frame >= NextFrame()) { if (lastState->activation_frame >= NextFrame()) {
// it isn't -- remove it // it isn't -- remove it
fStates.RemoveItem(fStates.CountItems() - 1); fStates.RemoveItem(fStates.CountItems() - 1);
@@ -71,7 +71,7 @@ public:
int32 PlayMode() const; int32 PlayMode() const;
int32 LoopMode() const; int32 LoopMode() const;
bool IsLoopingEnabled() const; bool IsLoopingEnabled() const;
int32 CurrentFrame() const; int64 CurrentFrame() const;
float Speed() const; float Speed() const;
virtual void SetFramesPerSecond(float framesPerSecond); virtual void SetFramesPerSecond(float framesPerSecond);
@@ -165,7 +165,7 @@ public:
bool enabled) const; bool enabled) const;
virtual void NotifyVideoBoundsChanged(BRect bounds) const; virtual void NotifyVideoBoundsChanged(BRect bounds) const;
virtual void NotifyFPSChanged(float fps) const; virtual void NotifyFPSChanged(float fps) const;
virtual void NotifyCurrentFrameChanged(int32 frame) const; virtual void NotifyCurrentFrameChanged(int64 frame) const;
virtual void NotifySpeedChanged(float speed) const; virtual void NotifySpeedChanged(float speed) const;
virtual void NotifyFrameDropped() const; virtual void NotifyFrameDropped() const;
virtual void NotifyStopFrameReached() const; virtual void NotifyStopFrameReached() const;
@@ -797,15 +797,13 @@ h->start_time = 0;
h->u.raw_video.line_count h->u.raw_video.line_count
= fConnectedFormat.display.line_count; = fConnectedFormat.display.line_count;
// Fill in a frame // Fill in a frame
media_format mf;
mf.type = B_MEDIA_RAW_VIDEO;
mf.u.raw_video = fConnectedFormat;
TRACE("_FrameGeneratorThread: frame: %Ld, " TRACE("_FrameGeneratorThread: frame: %Ld, "
"playlistFrame: %Ld\n", fFrame, playlistFrame); "playlistFrame: %Ld\n", fFrame, playlistFrame);
bool forceOrWasCached = forceSendingBuffer; bool forceOrWasCached = forceSendingBuffer;
err = fSupplier->FillBuffer(playlistFrame, err = fSupplier->FillBuffer(playlistFrame,
buffer->Data(), &mf, forceOrWasCached); buffer->Data(), fConnectedFormat,
forceOrWasCached);
// clean the buffer if something went wrong // clean the buffer if something went wrong
if (err != B_OK) { if (err != B_OK) {
// TODO: should use "back value" according // TODO: should use "back value" according
@@ -10,7 +10,7 @@
#include <SupportDefs.h> #include <SupportDefs.h>
struct media_format; struct media_raw_video_format;
class VideoSupplier { class VideoSupplier {
@@ -19,7 +19,7 @@ class VideoSupplier {
virtual ~VideoSupplier(); virtual ~VideoSupplier();
virtual status_t FillBuffer(int64 startFrame, void* buffer, virtual status_t FillBuffer(int64 startFrame, void* buffer,
const media_format* format, const media_raw_video_format& format,
bool& wasCached) = 0; bool& wasCached) = 0;
virtual void DeleteCaches(); virtual void DeleteCaches();
@@ -85,7 +85,7 @@ MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const
status_t status_t
MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime, MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime,
const media_format* format, bool& wasCached) const media_raw_video_format& format, bool& wasCached)
{ {
if (!fVideoTrack) if (!fVideoTrack)
return B_NO_INIT; return B_NO_INIT;
@@ -93,12 +93,12 @@ MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime,
return B_BAD_VALUE; return B_BAD_VALUE;
status_t ret = B_OK; status_t ret = B_OK;
if (format->u.raw_video.display.format if (format.display.format
!= fFormat.u.raw_video.display.format != fFormat.u.raw_video.display.format
|| fFormat.u.raw_video.display.bytes_per_row || fFormat.u.raw_video.display.bytes_per_row
!= format->u.raw_video.display.bytes_per_row) { != format.display.bytes_per_row) {
ret = _SwitchFormat(format->u.raw_video.display.format, ret = _SwitchFormat(format.display.format,
format->u.raw_video.display.bytes_per_row); format.display.bytes_per_row);
if (ret < B_OK) { if (ret < B_OK) {
fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - " fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
"unable to switch media format: %s\n", strerror(ret)); "unable to switch media format: %s\n", strerror(ret));
@@ -200,8 +200,10 @@ MediaTrackVideoSupplier::SeekToFrame(int64* frame)
int64 nextWantFrame = wantFrame + 1; int64 nextWantFrame = wantFrame + 1;
if (fVideoTrack->FindKeyFrameForFrame(&nextWantFrame, if (fVideoTrack->FindKeyFrameForFrame(&nextWantFrame,
B_MEDIA_SEEK_CLOSEST_BACKWARD) == B_OK) { B_MEDIA_SEEK_CLOSEST_BACKWARD) == B_OK) {
if (nextWantFrame == wantFrame) if (nextWantFrame == wantFrame) {
*frame = wantFrame + 1; wantFrame++;
*frame = wantFrame;
}
} }
} }
@@ -209,7 +211,7 @@ MediaTrackVideoSupplier::SeekToFrame(int64* frame)
// printf("keyframe for frame: %lld -> %lld\n", wantFrame, *frame); // printf("keyframe for frame: %lld -> %lld\n", wantFrame, *frame);
//} //}
if (*frame <= fCurrentFrame && wantFrame > fCurrentFrame) { if (*frame <= fCurrentFrame && wantFrame >= fCurrentFrame) {
// The current frame is already closer to the wanted frame // The current frame is already closer to the wanted frame
// than the next keyframe before it. // than the next keyframe before it.
*frame = fCurrentFrame; *frame = fCurrentFrame;
@@ -26,7 +26,7 @@ class MediaTrackVideoSupplier : public VideoTrackSupplier {
virtual status_t ReadFrame(void* buffer, virtual status_t ReadFrame(void* buffer,
bigtime_t* performanceTime, bigtime_t* performanceTime,
const media_format* format, const media_raw_video_format& format,
bool& wasCached); bool& wasCached);
virtual status_t FindKeyFrameForFrame(int64* frame); virtual status_t FindKeyFrameForFrame(int64* frame);
virtual status_t SeekToTime(bigtime_t* performanceTime); virtual status_t SeekToTime(bigtime_t* performanceTime);
@@ -1,10 +1,14 @@
/* /*
* Copyright © 2008 Stephan Aßmus <superstippi@gmx.de> * Copyright 2008-2010 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT licensce. * All rights reserved. Distributed under the terms of the MIT licensce.
*/ */
#include "ProxyVideoSupplier.h" #include "ProxyVideoSupplier.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Autolock.h> #include <Autolock.h>
@@ -12,34 +16,66 @@
ProxyVideoSupplier::ProxyVideoSupplier() ProxyVideoSupplier::ProxyVideoSupplier()
: fSupplierLock("video supplier lock") :
, fSupplier(NULL) fSupplierLock("video supplier lock"),
fSupplier(NULL),
fCachedFrame(NULL),
fCachedFrameSize(0),
fCachedFrameValid(false),
fUseFrameCaching(true)
{ {
} }
ProxyVideoSupplier::~ProxyVideoSupplier() ProxyVideoSupplier::~ProxyVideoSupplier()
{ {
free(fCachedFrame);
} }
status_t status_t
ProxyVideoSupplier::FillBuffer(int64 startFrame, void* buffer, ProxyVideoSupplier::FillBuffer(int64 startFrame, void* buffer,
const media_format* format, bool& wasCached) const media_raw_video_format& format, bool& wasCached)
{ {
BAutolock _(fSupplierLock); BAutolock _(fSupplierLock);
//printf("ProxyVideoSupplier::FillBuffer(%lld)\n", startFrame); //printf("ProxyVideoSupplier::FillBuffer(%lld)\n", startFrame);
if (fSupplier == NULL) if (fSupplier == NULL)
return B_NO_INIT; return B_NO_INIT;
bigtime_t performanceTime = 0; if (fUseFrameCaching) {
if (fSupplier->CurrentFrame() == startFrame + 1) { size_t bufferSize = format.display.bytes_per_row
printf("ProxyVideoSupplier::FillBuffer(%lld) - Could re-use previous " * format.display.line_count;
"buffer!\n", startFrame); if (fCachedFrame == NULL || fCachedFrameSize != bufferSize) {
// realloc cached frame
fCachedFrameValid = false;
void* cachedFrame = realloc(fCachedFrame, bufferSize);
if (cachedFrame != NULL) {
fCachedFrame = cachedFrame,
fCachedFrameSize = bufferSize;
} else
fUseFrameCaching = false;
fCachedFrameValid = false;
}
} }
if (fSupplier->CurrentFrame() == startFrame + 1) {
if (fCachedFrameValid) {
memcpy(buffer, fCachedFrame, fCachedFrameSize);
wasCached = true;
return B_OK;
}
// TODO: The problem here is hidden in PlaybackManager::_PushState()
// not computing the correct current_frame for the new PlayingState.
printf("ProxyVideoSupplier::FillBuffer(%lld) - TODO: Avoid "
"asking for the same frame twice (%lld)!\n", startFrame,
fSupplier->CurrentFrame());
}
status_t ret = B_OK;
bigtime_t performanceTime = 0;
if (fSupplier->CurrentFrame() != startFrame) { if (fSupplier->CurrentFrame() != startFrame) {
int64 frame = startFrame; int64 frame = startFrame;
status_t ret = fSupplier->SeekToFrame(&frame); ret = fSupplier->SeekToFrame(&frame);
if (ret != B_OK) if (ret != B_OK)
return ret; return ret;
// Read frames until we reach the frame before the one we want to read. // Read frames until we reach the frame before the one we want to read.
@@ -57,10 +93,14 @@ ProxyVideoSupplier::FillBuffer(int64 startFrame, void* buffer,
} }
} }
// TODO: cache into intermediate buffer to handle the ret = fSupplier->ReadFrame(buffer, &performanceTime, format, wasCached);
// currentFrame = startFrame + 1 case!
return fSupplier->ReadFrame(buffer, &performanceTime, format, wasCached); if (fUseFrameCaching && ret == B_OK) {
memcpy(fCachedFrame, buffer, fCachedFrameSize);
fCachedFrameValid = true;
}
return ret;
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2008 Stephan Aßmus <superstippi@gmx.de> * Copyright 2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT licensce. * All rights reserved. Distributed under the terms of the MIT licensce.
*/ */
#ifndef PROXY_VIDEO_SUPPLIER_H #ifndef PROXY_VIDEO_SUPPLIER_H
@@ -19,7 +19,7 @@ public:
virtual ~ProxyVideoSupplier(); virtual ~ProxyVideoSupplier();
virtual status_t FillBuffer(int64 startFrame, void* buffer, virtual status_t FillBuffer(int64 startFrame, void* buffer,
const media_format* format, const media_raw_video_format& format,
bool& wasCached); bool& wasCached);
virtual void DeleteCaches(); virtual void DeleteCaches();
@@ -30,6 +30,11 @@ private:
BLocker fSupplierLock; BLocker fSupplierLock;
VideoTrackSupplier* fSupplier; VideoTrackSupplier* fSupplier;
void* fCachedFrame;
size_t fCachedFrameSize;
bool fCachedFrameValid;
bool fUseFrameCaching;
}; };
#endif // PROXY_VIDEO_SUPPLIER_H #endif // PROXY_VIDEO_SUPPLIER_H
@@ -23,7 +23,7 @@ class VideoTrackSupplier {
virtual status_t GetCodecInfo(media_codec_info* info) const = 0; virtual status_t GetCodecInfo(media_codec_info* info) const = 0;
virtual status_t ReadFrame(void* buffer, virtual status_t ReadFrame(void* buffer,
bigtime_t* performanceTime, bigtime_t* performanceTime,
const media_format* format, const media_raw_video_format& format,
bool& wasCached) = 0; bool& wasCached) = 0;
virtual status_t FindKeyFrameForFrame(int64* frame) = 0; virtual status_t FindKeyFrameForFrame(int64* frame) = 0;
virtual status_t SeekToTime(bigtime_t* performanceTime) = 0; virtual status_t SeekToTime(bigtime_t* performanceTime) = 0;