* Moved a bunch of non-primary interface classes into a new subfolder
"interface" * Complete reimplementation of the playback engine using Media Nodes: - Seeking video files does not appear to lockup the playback anymore, but works on a frame accurate level even for keyframe based streams. There is currently a problem with certain container formats, the audio track reports a "Device Seek Error" in certain conditions. In that case audio goes silent, and can be restarted by going back to the beginnings of the stream. - Video overlays are now supported. - It would be possible to connect the output of the MediaPlayer to other applications or dormant media nodes. * Known regressions: - The volume slider has currently no effect anymore. - Switching the audio track during playback has a known race condition and can crash the player. - The new engine is not as "light weight" as the old one. I tagged the previous implementation in tags/components/mediaplayer-engine-v1. It does not seem to have any noticable effect though. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25725 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+257
-759
File diff suppressed because it is too large
Load Diff
+110
-161
@@ -2,7 +2,7 @@
|
||||
* Controller.h - Media Player for the Haiku Operating System
|
||||
*
|
||||
* Copyright (C) 2006 Marcus Overhagen <[email protected]>
|
||||
* Copyright (C) 2007 Stephan Aßmus <[email protected]>
|
||||
* Copyright (C) 2007-2008 Stephan Aßmus <[email protected]> (MIT Ok)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -29,207 +29,156 @@
|
||||
#include <Locker.h>
|
||||
#include <String.h>
|
||||
|
||||
class AudioSupplier;
|
||||
#include "NodeManager.h"
|
||||
|
||||
class AudioTrackSupplier;
|
||||
class BBitmap;
|
||||
class BMediaFile;
|
||||
class BMediaTrack;
|
||||
class ProxyAudioSupplier;
|
||||
class ProxyVideoSupplier;
|
||||
class SoundOutput;
|
||||
class VideoSupplier;
|
||||
class VideoTrackSupplier;
|
||||
class VideoView;
|
||||
|
||||
class Controller {
|
||||
class Controller : public NodeManager {
|
||||
public:
|
||||
|
||||
class Listener {
|
||||
public:
|
||||
Listener();
|
||||
virtual ~Listener();
|
||||
Listener();
|
||||
virtual ~Listener();
|
||||
|
||||
virtual void FileFinished();
|
||||
virtual void FileChanged();
|
||||
virtual void FileFinished();
|
||||
virtual void FileChanged();
|
||||
|
||||
virtual void VideoTrackChanged(int32 index);
|
||||
virtual void AudioTrackChanged(int32 index);
|
||||
virtual void VideoTrackChanged(int32 index);
|
||||
virtual void AudioTrackChanged(int32 index);
|
||||
|
||||
virtual void VideoStatsChanged();
|
||||
virtual void AudioStatsChanged();
|
||||
virtual void VideoStatsChanged();
|
||||
virtual void AudioStatsChanged();
|
||||
|
||||
virtual void PlaybackStateChanged(uint32 state);
|
||||
virtual void PositionChanged(float position);
|
||||
virtual void VolumeChanged(float volume);
|
||||
virtual void MutedChanged(bool muted);
|
||||
virtual void PlaybackStateChanged(uint32 state);
|
||||
virtual void PositionChanged(float position);
|
||||
virtual void VolumeChanged(float volume);
|
||||
virtual void MutedChanged(bool muted);
|
||||
};
|
||||
|
||||
Controller();
|
||||
virtual ~Controller();
|
||||
Controller();
|
||||
virtual ~Controller();
|
||||
|
||||
bool Lock();
|
||||
status_t LockWithTimeout(bigtime_t timeout);
|
||||
void Unlock();
|
||||
// PlaybackManager interface
|
||||
virtual int64 Duration();
|
||||
|
||||
status_t SetTo(const entry_ref &ref);
|
||||
void GetSize(int *width, int *height);
|
||||
// NodeManager interface
|
||||
virtual VideoTarget* CreateVideoTarget();
|
||||
virtual VideoSupplier* CreateVideoSupplier();
|
||||
virtual AudioSupplier* CreateAudioSupplier();
|
||||
|
||||
int AudioTrackCount();
|
||||
int VideoTrackCount();
|
||||
// Controller
|
||||
status_t SetTo(const entry_ref &ref);
|
||||
void GetSize(int *width, int *height);
|
||||
|
||||
int AudioTrackCount();
|
||||
int VideoTrackCount();
|
||||
|
||||
status_t SelectAudioTrack(int n);
|
||||
status_t SelectVideoTrack(int n);
|
||||
status_t SelectAudioTrack(int n);
|
||||
status_t SelectVideoTrack(int n);
|
||||
|
||||
void Stop();
|
||||
void Play();
|
||||
void Pause();
|
||||
void TogglePlaying();
|
||||
void Stop();
|
||||
void Play();
|
||||
void Pause();
|
||||
void TogglePlaying();
|
||||
|
||||
bool IsPaused() const;
|
||||
bool IsStopped() const;
|
||||
uint32 PlaybackState() const;
|
||||
uint32 PlaybackState();
|
||||
|
||||
bigtime_t Duration();
|
||||
bigtime_t Position();
|
||||
bigtime_t TimeDuration();
|
||||
bigtime_t TimePosition();
|
||||
|
||||
void SetVolume(float value);
|
||||
float Volume() const;
|
||||
void VolumeUp();
|
||||
void VolumeDown();
|
||||
void ToggleMute();
|
||||
void SetPosition(float value);
|
||||
virtual void SetVolume(float percent);
|
||||
float Volume();
|
||||
void VolumeUp();
|
||||
void VolumeDown();
|
||||
void ToggleMute();
|
||||
void SetPosition(float value);
|
||||
|
||||
bool HasFile();
|
||||
status_t GetFileFormatInfo(
|
||||
media_file_format* fileFormat);
|
||||
status_t GetCopyright(BString* copyright);
|
||||
status_t GetLocation(BString* location);
|
||||
status_t GetName(BString* name);
|
||||
status_t GetEncodedVideoFormat(media_format* format);
|
||||
status_t GetVideoCodecInfo(media_codec_info* info);
|
||||
status_t GetEncodedAudioFormat(media_format* format);
|
||||
status_t GetAudioCodecInfo(media_codec_info* info);
|
||||
|
||||
bool HasFile();
|
||||
status_t GetFileFormatInfo(media_file_format* fileFormat);
|
||||
status_t GetCopyright(BString* copyright);
|
||||
status_t GetLocation(BString* location);
|
||||
status_t GetName(BString* name);
|
||||
status_t GetEncodedVideoFormat(media_format* format);
|
||||
status_t GetVideoCodecInfo(media_codec_info* info);
|
||||
status_t GetEncodedAudioFormat(media_format* format);
|
||||
status_t GetAudioCodecInfo(media_codec_info* info);
|
||||
|
||||
// video view
|
||||
void SetVideoView(VideoView *view);
|
||||
void SetVideoView(VideoView *view);
|
||||
|
||||
bool IsOverlayActive();
|
||||
bool IsOverlayActive();
|
||||
|
||||
bool LockBitmap();
|
||||
void UnlockBitmap();
|
||||
BBitmap * Bitmap();
|
||||
|
||||
// notification support
|
||||
bool AddListener(Listener* listener);
|
||||
void RemoveListener(Listener* listener);
|
||||
bool AddListener(Listener* listener);
|
||||
void RemoveListener(Listener* listener);
|
||||
|
||||
private:
|
||||
void _AudioDecodeThread();
|
||||
void _AudioPlayThread();
|
||||
uint32 _PlaybackState(int32 playingMode) const;
|
||||
void _EndOfStreamReached(bool isVideo = false);
|
||||
|
||||
void _VideoDecodeThread();
|
||||
void _VideoPlayThread();
|
||||
void _NotifyFileChanged() const;
|
||||
void _NotifyFileFinished() const;
|
||||
void _NotifyVideoTrackChanged(int32 index) const;
|
||||
void _NotifyAudioTrackChanged(int32 index) const;
|
||||
|
||||
void _StartThreads();
|
||||
void _StopThreads();
|
||||
void _NotifyVideoStatsChanged() const;
|
||||
void _NotifyAudioStatsChanged() const;
|
||||
|
||||
void _EndOfStreamReached(bool isVideo = false);
|
||||
void _UpdatePosition(bigtime_t position,
|
||||
bool isVideoPosition = false,
|
||||
bool force = false);
|
||||
void _NotifyPlaybackStateChanged(uint32 state) const;
|
||||
void _NotifyPositionChanged(float position) const;
|
||||
void _NotifyVolumeChanged(float volume) const;
|
||||
void _NotifyMutedChanged(bool muted) const;
|
||||
|
||||
static int32 _VideoDecodeThreadEntry(void *self);
|
||||
static int32 _VideoPlayThreadEntry(void *self);
|
||||
static int32 _AudioDecodeThreadEntry(void *self);
|
||||
static int32 _AudioPlayThreadEntry(void *self);
|
||||
// overridden from PlaybackManager so that we
|
||||
// can use our own Listener mechanism
|
||||
virtual void NotifyPlayModeChanged(int32 mode) const;
|
||||
virtual void NotifyLoopModeChanged(int32 mode) const;
|
||||
virtual void NotifyLoopingEnabledChanged(
|
||||
bool enabled) const;
|
||||
virtual void NotifyVideoBoundsChanged(BRect bounds) const;
|
||||
virtual void NotifyFPSChanged(float fps) const;
|
||||
virtual void NotifyCurrentFrameChanged(int32 frame) const;
|
||||
virtual void NotifySpeedChanged(float speed) const;
|
||||
virtual void NotifyFrameDropped() const;
|
||||
virtual void NotifyStopFrameReached() const;
|
||||
|
||||
private:
|
||||
void _NotifyFileChanged();
|
||||
void _NotifyFileFinished();
|
||||
void _NotifyVideoTrackChanged(int32 index);
|
||||
void _NotifyAudioTrackChanged(int32 index);
|
||||
|
||||
void _NotifyVideoStatsChanged();
|
||||
void _NotifyAudioStatsChanged();
|
||||
VideoView* fVideoView;
|
||||
volatile bool fPaused;
|
||||
volatile bool fStopped;
|
||||
float fVolume;
|
||||
bool fMuted;
|
||||
|
||||
void _NotifyPlaybackStateChanged();
|
||||
void _NotifyPositionChanged(float position);
|
||||
void _NotifyVolumeChanged(float volume);
|
||||
void _NotifyMutedChanged(bool muted);
|
||||
entry_ref fRef;
|
||||
BMediaFile* fMediaFile;
|
||||
|
||||
friend class InfoWin;
|
||||
ProxyVideoSupplier* fVideoSupplier;
|
||||
ProxyAudioSupplier* fAudioSupplier;
|
||||
VideoTrackSupplier* fVideoTrackSupplier;
|
||||
AudioTrackSupplier* fAudioTrackSupplier;
|
||||
|
||||
enum {
|
||||
MAX_AUDIO_BUFFERS = 8,
|
||||
MAX_VIDEO_BUFFERS = 3,
|
||||
};
|
||||
BList fAudioTrackList;
|
||||
BList fVideoTrackList;
|
||||
|
||||
mutable bigtime_t fPosition;
|
||||
bigtime_t fDuration;
|
||||
float fVideoFrameRate;
|
||||
|
||||
bool fAutoplay;
|
||||
volatile bool fPauseAtEndOfStream;
|
||||
volatile bool fSeekToStartAfterPause;
|
||||
|
||||
struct buffer_info {
|
||||
char * buffer;
|
||||
BBitmap * bitmap;
|
||||
size_t sizeUsed;
|
||||
size_t sizeMax;
|
||||
bigtime_t startTime;
|
||||
bool formatChanged;
|
||||
bool endOfStream;
|
||||
media_format mediaFormat;
|
||||
};
|
||||
|
||||
VideoView * fVideoView;
|
||||
volatile bool fPaused;
|
||||
volatile bool fStopped;
|
||||
float fVolume;
|
||||
bool fMuted;
|
||||
|
||||
entry_ref fRef;
|
||||
BMediaFile * fMediaFile;
|
||||
mutable BLocker fDataLock;
|
||||
|
||||
VideoSupplier* fVideoSupplier;
|
||||
AudioSupplier* fAudioSupplier;
|
||||
|
||||
BLocker fVideoSupplierLock;
|
||||
BLocker fAudioSupplierLock;
|
||||
|
||||
BList * fAudioTrackList;
|
||||
BList * fVideoTrackList;
|
||||
media_format fAudioFormat;
|
||||
media_format fVideoFormat;
|
||||
|
||||
sem_id fAudioDecodeSem;
|
||||
sem_id fVideoDecodeSem;
|
||||
sem_id fAudioPlaySem;
|
||||
sem_id fVideoPlaySem;
|
||||
sem_id fAudioWaitSem;
|
||||
sem_id fVideoWaitSem;
|
||||
thread_id fAudioDecodeThread;
|
||||
thread_id fVideoDecodeThread;
|
||||
thread_id fAudioPlayThread;
|
||||
thread_id fVideoPlayThread;
|
||||
|
||||
SoundOutput * fSoundOutput;
|
||||
volatile bool fSeekAudio;
|
||||
volatile bool fSeekVideo;
|
||||
volatile bigtime_t fSeekPosition;
|
||||
bigtime_t fPosition;
|
||||
bigtime_t fDuration;
|
||||
|
||||
int32 fAudioBufferCount;
|
||||
int32 fAudioBufferReadIndex;
|
||||
int32 fAudioBufferWriteIndex;
|
||||
int32 fVideoBufferCount;
|
||||
int32 fVideoBufferReadIndex;
|
||||
int32 fVideoBufferWriteIndex;
|
||||
|
||||
buffer_info fAudioBuffer[MAX_AUDIO_BUFFERS];
|
||||
buffer_info fVideoBuffer[MAX_VIDEO_BUFFERS];
|
||||
|
||||
BLocker fTimeSourceLock;
|
||||
bigtime_t fTimeSourceSysTime;
|
||||
bigtime_t fTimeSourcePerfTime;
|
||||
bool fAutoplay;
|
||||
volatile bool fPauseAtEndOfStream;
|
||||
volatile bool fSeekToStartAfterPause;
|
||||
|
||||
BBitmap * fCurrentBitmap;
|
||||
BLocker fBitmapLock;
|
||||
|
||||
BList fListeners;
|
||||
BList fListeners;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -227,6 +227,9 @@ printf("InfoWin::Update(0x%08lx)\n", which);
|
||||
fContentsView->SetFontAndColor(be_plain_font, B_FONT_ALL);
|
||||
// fContentsView->Insert("");
|
||||
|
||||
if (!fController->Lock())
|
||||
return;
|
||||
|
||||
fLabelsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kRed);
|
||||
|
||||
status_t err;
|
||||
|
||||
@@ -2,13 +2,16 @@ SubDir HAIKU_TOP src apps mediaplayer ;
|
||||
|
||||
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||
|
||||
AddSubDirSupportedPlatforms libbe_test ;
|
||||
|
||||
# for BRecentItems
|
||||
UsePublicHeaders [ FDirName be_apps Tracker ] ;
|
||||
UsePrivateHeaders shared ;
|
||||
|
||||
# source directories
|
||||
local sourceDirs =
|
||||
interface
|
||||
media_node_framework
|
||||
media_node_framework/audio
|
||||
media_node_framework/video
|
||||
playlist
|
||||
supplier
|
||||
support
|
||||
@@ -21,6 +24,33 @@ for sourceDir in $(sourceDirs) {
|
||||
}
|
||||
|
||||
Application MediaPlayer :
|
||||
# interface
|
||||
DrawingTidbits.cpp
|
||||
SeekSlider.cpp
|
||||
TransportButton.cpp
|
||||
VolumeSlider.cpp
|
||||
|
||||
# media_node_framework
|
||||
NodeManager.cpp
|
||||
PlaybackListener.cpp
|
||||
PlaybackLOAdapter.cpp
|
||||
PlaybackManager.cpp
|
||||
|
||||
# media_node_framework/audio
|
||||
AudioAdapter.cpp
|
||||
AudioChannelConverter.cpp
|
||||
AudioFormatConverter.cpp
|
||||
AudioProducer.cpp
|
||||
AudioReader.cpp
|
||||
AudioResampler.cpp
|
||||
AudioSupplier.cpp
|
||||
|
||||
# media_node_framework/video
|
||||
VideoConsumer.cpp
|
||||
VideoProducer.cpp
|
||||
VideoSupplier.cpp
|
||||
VideoTarget.cpp
|
||||
|
||||
# playlist
|
||||
CopyPLItemsCommand.cpp
|
||||
ImportPLItemsCommand.cpp
|
||||
@@ -32,22 +62,27 @@ Application MediaPlayer :
|
||||
PlaylistWindow.cpp
|
||||
RemovePLItemsCommand.cpp
|
||||
|
||||
# supplier
|
||||
# settings
|
||||
Settings.cpp
|
||||
SettingsWindow.cpp
|
||||
|
||||
# supplier
|
||||
AudioSupplier.cpp
|
||||
AudioTrackSupplier.cpp
|
||||
MediaTrackAudioSupplier.cpp
|
||||
MediaTrackVideoSupplier.cpp
|
||||
VideoSupplier.cpp
|
||||
ProxyAudioSupplier.cpp
|
||||
ProxyVideoSupplier.cpp
|
||||
VideoTrackSupplier.cpp
|
||||
|
||||
# support
|
||||
AbstractLOAdapter.cpp
|
||||
Command.cpp
|
||||
CommandStack.cpp
|
||||
Event.cpp
|
||||
EventQueue.cpp
|
||||
Listener.cpp
|
||||
ListenerAdapter.cpp
|
||||
MessageEvent.cpp
|
||||
Notifier.cpp
|
||||
RWLocker.cpp
|
||||
FileReadWrite.cpp
|
||||
@@ -57,23 +92,13 @@ Application MediaPlayer :
|
||||
Controller.cpp
|
||||
ControllerObserver.cpp
|
||||
ControllerView.cpp
|
||||
DrawingTidbits.cpp
|
||||
InfoWin.cpp
|
||||
MainApp.cpp
|
||||
MainWin.cpp
|
||||
SoundOutput.cpp
|
||||
TransportButton.cpp
|
||||
TransportControlGroup.cpp
|
||||
SeekSlider.cpp
|
||||
VideoNode.cpp
|
||||
VideoView.cpp
|
||||
VolumeSlider.cpp
|
||||
|
||||
: be media tracker translation textencoding $(TARGET_LIBSTDC++)
|
||||
: MediaPlayer.rdef
|
||||
;
|
||||
|
||||
if $(TARGET_PLATFORM) = libbe_test {
|
||||
HaikuInstall install-test-apps : $(HAIKU_APP_TEST_DIR) : MediaPlayer
|
||||
: tests!apps ;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "EventQueue.h"
|
||||
|
||||
|
||||
MainApp *gMainApp;
|
||||
const char* kAppSig = "application/x-vnd.Haiku-MediaPlayer";
|
||||
@@ -221,8 +223,13 @@ MainApp::_BroadcastMessage(const BMessage& _message)
|
||||
int
|
||||
main()
|
||||
{
|
||||
EventQueue::CreateDefault();
|
||||
|
||||
gMainApp = new MainApp;
|
||||
gMainApp->Run();
|
||||
delete gMainApp;
|
||||
|
||||
EventQueue::DeleteDefault();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -150,8 +150,7 @@ MainWin::MainWin()
|
||||
// video view
|
||||
rect = BRect(0, fMenuBarHeight, fBackground->Bounds().right,
|
||||
fMenuBarHeight + 10);
|
||||
fVideoView = new VideoView(rect, "video display", B_FOLLOW_NONE,
|
||||
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
|
||||
fVideoView = new VideoView(rect, "video display", B_FOLLOW_NONE);
|
||||
fBackground->AddChild(fVideoView);
|
||||
|
||||
// controls
|
||||
@@ -171,7 +170,6 @@ MainWin::MainWin()
|
||||
fPlaylist->AddListener(fPlaylistObserver);
|
||||
fController->SetVideoView(fVideoView);
|
||||
fController->AddListener(fControllerObserver);
|
||||
fVideoView->IsOverlaySupported();
|
||||
|
||||
// printf("fMenuBarHeight %d\n", fMenuBarHeight);
|
||||
// printf("fControlsHeight %d\n", fControlsHeight);
|
||||
@@ -224,8 +222,13 @@ MainWin::~MainWin()
|
||||
}
|
||||
|
||||
delete fPlaylist;
|
||||
delete fController;
|
||||
delete fFilePanel;
|
||||
|
||||
// quit the Controller looper thread
|
||||
thread_id controllerThread = fController->Thread();
|
||||
fController->PostMessage(B_QUIT_REQUESTED);
|
||||
status_t exitValue;
|
||||
wait_for_thread(controllerThread, &exitValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -1237,10 +1240,7 @@ MainWin::_KeyDown(BMessage *msg)
|
||||
|
||||
switch (raw_char) {
|
||||
case B_SPACE:
|
||||
if (fController->IsPaused() || fController->IsStopped())
|
||||
fController->Play();
|
||||
else
|
||||
fController->Pause();
|
||||
fController->TogglePlaying();
|
||||
return B_OK;
|
||||
|
||||
case B_ESCAPE:
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* SoundOutput.cpp - Media Player for the Haiku Operating System
|
||||
*
|
||||
* Copyright (C) 2006 Marcus Overhagen <[email protected]>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "SoundOutput.h"
|
||||
|
||||
#include <OS.h>
|
||||
#include <SoundPlayer.h>
|
||||
#include <Debug.h>
|
||||
|
||||
#include <new>
|
||||
#include <string.h>
|
||||
|
||||
using std::nothrow;
|
||||
|
||||
|
||||
SoundOutput::SoundOutput(const char *name, const media_multi_audio_format &format)
|
||||
: fSoundPlayer(new(nothrow) BSoundPlayer(static_cast<const media_raw_audio_format *>(&format), name, play_buffer, NULL, this))
|
||||
, fBuffer(new(nothrow) uint8[format.buffer_size])
|
||||
, fBufferSize(format.buffer_size)
|
||||
, fBufferWriteable(create_sem(1, "SoundOutput writeable"))
|
||||
, fBufferReadable(create_sem(0, "SoundOutput readable"))
|
||||
, fBufferDuration(0)
|
||||
, fIsPlaying(false)
|
||||
{
|
||||
if (format.channel_count > 0 && format.frame_rate > 0 && (format.format & 0xf) != 0)
|
||||
fBufferDuration = bigtime_t(1000000.0 * (format.buffer_size / (format.channel_count * (format.format & 0xf))) / format.frame_rate);
|
||||
printf("SoundOutput: buffer duration is %Ld\n", fBufferDuration);
|
||||
}
|
||||
|
||||
|
||||
SoundOutput::~SoundOutput()
|
||||
{
|
||||
delete fSoundPlayer;
|
||||
delete_sem(fBufferWriteable);
|
||||
delete_sem(fBufferReadable);
|
||||
delete [] fBuffer;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SoundOutput::InitCheck()
|
||||
{
|
||||
if (!fSoundPlayer || !fBuffer || fBufferSize <= 0 || fBufferWriteable < B_OK || fBufferReadable < B_OK)
|
||||
return B_ERROR;
|
||||
return fSoundPlayer->InitCheck();
|
||||
}
|
||||
|
||||
|
||||
media_raw_audio_format
|
||||
SoundOutput::Format() const
|
||||
{
|
||||
return fSoundPlayer->Format();
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
SoundOutput::Latency()
|
||||
{
|
||||
bigtime_t latency = 0;
|
||||
if (InitCheck() >= B_OK)
|
||||
latency += fSoundPlayer->Latency();
|
||||
// Because of buffering, latency of SoundOutput is
|
||||
// slightly higher then the BSoundPlayer latency.
|
||||
return latency + min_c(1000, fBufferDuration / 4);
|
||||
}
|
||||
|
||||
|
||||
float
|
||||
SoundOutput::Volume()
|
||||
{
|
||||
return fSoundPlayer->Volume();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SoundOutput::SetVolume(float new_volume)
|
||||
{
|
||||
fSoundPlayer->SetVolume(new_volume);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SoundOutput::Play(const void *data, size_t size)
|
||||
{
|
||||
ASSERT(size > 0 && size <= fBufferSize);
|
||||
acquire_sem(fBufferWriteable);
|
||||
memcpy(fBuffer, data, size);
|
||||
|
||||
size_t fillsize = fBufferSize - size;
|
||||
if (fillsize)
|
||||
memset(fBuffer + size, 0, fillsize);
|
||||
release_sem(fBufferReadable);
|
||||
|
||||
if (!fIsPlaying) {
|
||||
fSoundPlayer->SetHasData(true);
|
||||
fSoundPlayer->Start();
|
||||
fIsPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SoundOutput::PlayBuffer(void *buffer)
|
||||
{
|
||||
if (acquire_sem_etc(fBufferReadable, 1, B_RELATIVE_TIMEOUT, fBufferDuration / 2) != B_OK) {
|
||||
// printf("SoundOutput: buffer not ready, playing silence\n");
|
||||
memset(buffer, 0, fBufferSize);
|
||||
return;
|
||||
}
|
||||
memcpy(buffer, fBuffer, fBufferSize);
|
||||
release_sem(fBufferWriteable);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SoundOutput::play_buffer(void *cookie, void *buffer, size_t size, const media_raw_audio_format & format)
|
||||
{
|
||||
ASSERT(size == static_cast<SoundOutput *>(cookie)->fBufferSize);
|
||||
ASSERT(size == format.buffer_size);
|
||||
static_cast<SoundOutput *>(cookie)->PlayBuffer(buffer);
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* SoundOutput.h - Media Player for the Haiku Operating System
|
||||
*
|
||||
* Copyright (C) 2006 Marcus Overhagen <[email protected]>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef __SOUND_OUTPUT_H
|
||||
#define __SOUND_OUTPUT_H
|
||||
|
||||
#include <MediaDefs.h>
|
||||
|
||||
class BSoundPlayer;
|
||||
|
||||
class SoundOutput
|
||||
{
|
||||
public:
|
||||
SoundOutput(const char *name, const media_multi_audio_format &format);
|
||||
~SoundOutput();
|
||||
|
||||
status_t InitCheck();
|
||||
|
||||
media_raw_audio_format Format() const;
|
||||
|
||||
bigtime_t Latency();
|
||||
|
||||
float Volume();
|
||||
void SetVolume(float new_volume);
|
||||
|
||||
void Play(const void *data, size_t size);
|
||||
|
||||
private:
|
||||
static void play_buffer(void *cookie, void *buffer, size_t size, const media_raw_audio_format & format);
|
||||
void PlayBuffer(void *buffer);
|
||||
|
||||
private:
|
||||
BSoundPlayer * fSoundPlayer;
|
||||
uint8 * fBuffer;
|
||||
size_t fBufferSize;
|
||||
sem_id fBufferWriteable;
|
||||
sem_id fBufferReadable;
|
||||
bigtime_t fBufferDuration;
|
||||
bool fIsPlaying;
|
||||
};
|
||||
|
||||
#endif
|
||||
+124
-224
@@ -1,269 +1,169 @@
|
||||
/*
|
||||
* VideoView.cpp - Media Player for the Haiku Operating System
|
||||
*
|
||||
* Copyright (C) 2006 Marcus Overhagen <[email protected]>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Copyright © 2006-2008 Stephan Aßmus <supersti[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include <Message.h>
|
||||
#include <Bitmap.h>
|
||||
#include "VideoView.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
VideoView::VideoView(BRect frame, const char *name, uint32 resizeMask, uint32 flags)
|
||||
: BView(frame, name, resizeMask, flags)
|
||||
, fController(NULL)
|
||||
, fOverlayActive(false)
|
||||
#include <Bitmap.h>
|
||||
|
||||
|
||||
VideoView::VideoView(BRect frame, const char* name, uint32 resizeMask)
|
||||
: BView(frame, name, resizeMask, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
|
||||
fOverlayMode(false)
|
||||
{
|
||||
SetViewColor(B_TRANSPARENT_COLOR);
|
||||
// SetViewColor(127,255,127);
|
||||
rgb_color r = {255, 0, 0, 255};
|
||||
fOverlayKeyColor = r;
|
||||
// might be reset to overlay key color if overlays are used
|
||||
SetHighColor(0, 0, 0);
|
||||
|
||||
// create some hopefully sensible default overlay restrictions
|
||||
fOverlayRestrictions.min_width_scale = 0.25;
|
||||
fOverlayRestrictions.max_width_scale = 8.0;
|
||||
fOverlayRestrictions.min_height_scale = 0.25;
|
||||
fOverlayRestrictions.max_height_scale = 8.0;
|
||||
}
|
||||
|
||||
|
||||
VideoView::~VideoView()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::SetController(Controller *controller)
|
||||
{
|
||||
fController = controller;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::AttachedToWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
VideoView::OverlayLockAcquire()
|
||||
{
|
||||
printf("VideoView::OverlayLockAcquire\n");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::OverlayLockRelease()
|
||||
{
|
||||
printf("VideoView::OverlayLockRelease\n");
|
||||
// overlaybitmap->UnlockBits
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::OverlayScreenshotPrepare()
|
||||
{
|
||||
printf("OverlayScreenshotPrepare enter\n");
|
||||
/*
|
||||
fController->LockBitmap();
|
||||
if (fOverlayActive) {
|
||||
BBitmap *bmp = fController->Bitmap();
|
||||
if (bmp) {
|
||||
// Window()->UpdateIfNeeded();
|
||||
// Sync();
|
||||
BBitmap *tmp = new BBitmap(bmp->Bounds(), 0, B_RGB32);
|
||||
// ConvertBitmap(tmp, bmp);
|
||||
ClearViewOverlay();
|
||||
DrawBitmap(tmp, Bounds());
|
||||
delete tmp;
|
||||
// Sync();
|
||||
}
|
||||
}
|
||||
fController->UnlockBitmap();
|
||||
*/
|
||||
printf("OverlayScreenshotPrepare leave\n");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::OverlayScreenshotCleanup()
|
||||
{
|
||||
printf("OverlayScreenshotCleanup enter\n");
|
||||
/*
|
||||
snooze(50000); // give app server some time to take the screenshot
|
||||
fController->LockBitmap();
|
||||
if (fOverlayActive) {
|
||||
BBitmap *bmp = fController->Bitmap();
|
||||
if (bmp) {
|
||||
DrawBitmap(bmp, Bounds());
|
||||
SetViewOverlay(bmp, bmp->Bounds(), Bounds(), &fOverlayKeyColor,
|
||||
B_FOLLOW_ALL, B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL);
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
fController->UnlockBitmap();
|
||||
*/
|
||||
printf("OverlayScreenshotCleanup leave\n");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::RemoveVideoDisplay()
|
||||
{
|
||||
printf("VideoView::RemoveVideoDisplay\n");
|
||||
|
||||
if (fOverlayActive) {
|
||||
ClearViewOverlay();
|
||||
fOverlayActive = false;
|
||||
}
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::RemoveOverlay()
|
||||
{
|
||||
printf("VideoView::RemoveOverlay\n");
|
||||
if (LockLooperWithTimeout(50000) == B_OK) {
|
||||
ClearViewOverlay();
|
||||
fOverlayActive = false;
|
||||
UnlockLooper();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::Draw(BRect updateRect)
|
||||
{
|
||||
if (fOverlayActive) {
|
||||
SetHighColor(fOverlayKeyColor);
|
||||
bool fillBlack = true;
|
||||
|
||||
if (LockBitmap()) {
|
||||
BRect r(Bounds());
|
||||
if (const BBitmap* bitmap = GetBitmap()) {
|
||||
fillBlack = false;
|
||||
if (!fOverlayMode)
|
||||
DrawBitmap(bitmap, bitmap->Bounds(), r);
|
||||
}
|
||||
UnlockBitmap();
|
||||
}
|
||||
|
||||
if (fillBlack)
|
||||
FillRect(updateRect);
|
||||
} else {
|
||||
fController->LockBitmap();
|
||||
BBitmap *bmp = fController->Bitmap();
|
||||
if (bmp)
|
||||
DrawBitmap(bmp, Bounds());
|
||||
fController->UnlockBitmap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::DrawFrame()
|
||||
VideoView::SetBitmap(const BBitmap* bitmap)
|
||||
{
|
||||
// printf("VideoView::DrawFrame\n");
|
||||
|
||||
if (LockLooperWithTimeout(50000) != B_OK)
|
||||
return;
|
||||
VideoTarget::SetBitmap(bitmap);
|
||||
// Attention: Don't lock the window, if the bitmap is NULL. Otherwise
|
||||
// we're going to deadlock when the window tells the node manager to
|
||||
// stop the nodes (Window -> NodeManager -> VideoConsumer -> VideoView
|
||||
// -> Window).
|
||||
if (bitmap && LockLooperWithTimeout(10000) == B_OK) {
|
||||
if (LockBitmap()) {
|
||||
// if (fOverlayMode || bitmap->Flags() & B_BITMAP_WILL_OVERLAY) {
|
||||
if (fOverlayMode || bitmap->ColorSpace() == B_YCbCr422) {
|
||||
if (!fOverlayMode) {
|
||||
// init overlay
|
||||
rgb_color key;
|
||||
status_t ret = SetViewOverlay(bitmap, bitmap->Bounds(),
|
||||
Bounds(), &key, B_FOLLOW_ALL,
|
||||
B_OVERLAY_FILTER_HORIZONTAL
|
||||
| B_OVERLAY_FILTER_VERTICAL);
|
||||
if (ret == B_OK) {
|
||||
fOverlayKeyColor = key;
|
||||
SetViewColor(key);
|
||||
SetLowColor(key);
|
||||
snooze(20000);
|
||||
FillRect(Bounds(), B_SOLID_LOW);
|
||||
Sync();
|
||||
// use overlay from here on
|
||||
fOverlayMode = true;
|
||||
|
||||
fController->LockBitmap();
|
||||
BBitmap *bmp = fController->Bitmap();
|
||||
|
||||
if (bmp) {
|
||||
bool want_overlay = bmp->ColorSpace() == B_YCbCr422;
|
||||
|
||||
if (!want_overlay && fOverlayActive) {
|
||||
if (LockLooperWithTimeout(50000) == B_OK) {
|
||||
// update restrictions
|
||||
overlay_restrictions restrictions;
|
||||
if (bitmap->GetOverlayRestrictions(&restrictions)
|
||||
== B_OK)
|
||||
fOverlayRestrictions = restrictions;
|
||||
} else {
|
||||
// try again next time
|
||||
// synchronous draw
|
||||
FillRect(Bounds());
|
||||
Sync();
|
||||
}
|
||||
} else {
|
||||
// transfer overlay channel
|
||||
rgb_color key;
|
||||
SetViewOverlay(bitmap, bitmap->Bounds(), Bounds(),
|
||||
&key, B_FOLLOW_ALL, B_OVERLAY_FILTER_HORIZONTAL
|
||||
| B_OVERLAY_FILTER_VERTICAL
|
||||
| B_OVERLAY_TRANSFER_CHANNEL);
|
||||
}
|
||||
} else if (fOverlayMode && bitmap->ColorSpace() != B_YCbCr422) {
|
||||
fOverlayMode = false;
|
||||
ClearViewOverlay();
|
||||
UnlockLooper();
|
||||
fOverlayActive = false;
|
||||
} else {
|
||||
printf("can't ClearViewOverlay, as LockLooperWithTimeout failed\n");
|
||||
return;
|
||||
SetViewColor(B_TRANSPARENT_COLOR);
|
||||
}
|
||||
}
|
||||
if (!fOverlayMode)
|
||||
DrawBitmap(bitmap, bitmap->Bounds(), Bounds());
|
||||
|
||||
if (want_overlay && !fOverlayActive ) {
|
||||
printf("trying to activate overlay...");
|
||||
// reserve overlay channel
|
||||
status_t ret = SetViewOverlay(bmp, bmp->Bounds(), Bounds(),
|
||||
&fOverlayKeyColor, B_FOLLOW_ALL,
|
||||
B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL);
|
||||
if (ret == B_OK) {
|
||||
printf("success\n");
|
||||
fOverlayActive = true;
|
||||
Invalidate();
|
||||
} else {
|
||||
printf("failed: %s\n", strerror(ret));
|
||||
}
|
||||
} else if (fOverlayActive) {
|
||||
// transfer overlay channel
|
||||
rgb_color overlayKey;
|
||||
SetViewOverlay(bmp, bmp->Bounds(), Bounds(), &overlayKey,
|
||||
B_FOLLOW_ALL, B_OVERLAY_TRANSFER_CHANNEL
|
||||
| B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL);
|
||||
} else {
|
||||
// no overlay
|
||||
DrawBitmap(bmp, Bounds());
|
||||
UnlockBitmap();
|
||||
}
|
||||
UnlockLooper();
|
||||
}
|
||||
|
||||
fController->UnlockBitmap();
|
||||
|
||||
UnlockLooper();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::MessageReceived(BMessage *msg)
|
||||
VideoView::GetOverlayScaleLimits(float* minScale, float* maxScale) const
|
||||
{
|
||||
switch (msg->what) {
|
||||
*minScale = max_c(fOverlayRestrictions.min_width_scale,
|
||||
fOverlayRestrictions.min_height_scale);
|
||||
*maxScale = max_c(fOverlayRestrictions.max_width_scale,
|
||||
fOverlayRestrictions.max_height_scale);
|
||||
}
|
||||
|
||||
default:
|
||||
BView::MessageReceived(msg);
|
||||
}
|
||||
|
||||
void
|
||||
VideoView::OverlayScreenshotPrepare()
|
||||
{
|
||||
// TODO: Do nothing if the current bitmap is in RGB color space
|
||||
// and no overlay. Otherwise, convert current bitmap to RGB color
|
||||
// space an draw it in place of the normal display.
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::OverlayScreenshotCleanup()
|
||||
{
|
||||
// TODO: Do nothing if the current bitmap is in RGB color space
|
||||
// and no overlay. Otherwise clean view area with overlay color.
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VideoView::IsOverlaySupported()
|
||||
VideoView::IsOverlayActive()
|
||||
{
|
||||
struct colorcombo {
|
||||
color_space colspace;
|
||||
const char *name;
|
||||
} colspace[] = {
|
||||
{ B_RGB32, "B_RGB32"},
|
||||
{ B_RGBA32, "B_RGBA32"},
|
||||
{ B_RGB24, "B_RGB24"},
|
||||
{ B_RGB16, "B_RGB16"},
|
||||
{ B_RGB15, "B_RGB15"},
|
||||
{ B_RGBA15, "B_RGBA15"},
|
||||
{ B_RGB32_BIG, "B_RGB32_BIG"},
|
||||
{ B_RGBA32_BIG, "B_RGBA32_BIG "},
|
||||
{ B_RGB24_BIG, "B_RGB24_BIG "},
|
||||
{ B_RGB16_BIG, "B_RGB16_BIG "},
|
||||
{ B_RGB15_BIG, "B_RGB15_BIG "},
|
||||
{ B_RGBA15_BIG, "B_RGBA15_BIG "},
|
||||
{ B_YCbCr422, "B_YCbCr422"},
|
||||
{ B_YCbCr411, "B_YCbCr411"},
|
||||
{ B_YCbCr444, "B_YCbCr444"},
|
||||
{ B_YCbCr420, "B_YCbCr420"},
|
||||
{ B_YUV422, "B_YUV422"},
|
||||
{ B_YUV411, "B_YUV411"},
|
||||
{ B_YUV444, "B_YUV444"},
|
||||
{ B_YUV420, "B_YUV420"},
|
||||
{ B_NO_COLOR_SPACE, NULL}
|
||||
};
|
||||
|
||||
bool supported = false;
|
||||
for (int i = 0; colspace[i].name; i++) {
|
||||
BBitmap *test = new BBitmap(BRect(0,0,319,239), B_BITMAP_WILL_OVERLAY | B_BITMAP_RESERVE_OVERLAY_CHANNEL, colspace[i].colspace);
|
||||
if (test->InitCheck() == B_OK) {
|
||||
printf("Display supports %s (0x%08x) overlay\n", colspace[i].name, colspace[i].colspace);
|
||||
supported = true;
|
||||
}
|
||||
delete test;
|
||||
// if (supported)
|
||||
// break;
|
||||
bool active = false;
|
||||
if (LockBitmap()) {
|
||||
active = fOverlayMode;
|
||||
UnlockBitmap();
|
||||
}
|
||||
return supported;
|
||||
return active;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoView::DisableOverlay()
|
||||
{
|
||||
if (!fOverlayMode)
|
||||
return;
|
||||
|
||||
FillRect(Bounds());
|
||||
Sync();
|
||||
|
||||
ClearViewOverlay();
|
||||
snooze(20000);
|
||||
Sync();
|
||||
fOverlayMode = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,58 +1,42 @@
|
||||
/*
|
||||
* VideoView.h - Media Player for the Haiku Operating System
|
||||
*
|
||||
* Copyright (C) 2006 Marcus Overhagen <[email protected]>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Copyright © 2006-2008 Stephan Aßmus <supersti[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef __VIDEO_VIEW_H
|
||||
#define __VIDEO_VIEW_H
|
||||
#ifndef VIDEO_VIEW_H
|
||||
#define VIDEO_VIEW_H
|
||||
|
||||
|
||||
#include <View.h>
|
||||
#include "Controller.h"
|
||||
|
||||
class VideoView : public BView
|
||||
{
|
||||
#include "VideoTarget.h"
|
||||
|
||||
|
||||
class VideoView : public BView, public VideoTarget {
|
||||
public:
|
||||
VideoView(BRect frame, const char *name, uint32 resizeMask, uint32 flags);
|
||||
~VideoView();
|
||||
VideoView(BRect frame, const char* name,
|
||||
uint32 resizeMask);
|
||||
virtual ~VideoView();
|
||||
|
||||
void SetController(Controller *controller);
|
||||
|
||||
void RemoveVideoDisplay();
|
||||
void RemoveOverlay();
|
||||
|
||||
bool IsOverlaySupported();
|
||||
// BView interface
|
||||
virtual void Draw(BRect updateRect);
|
||||
|
||||
void OverlayLockAcquire();
|
||||
void OverlayLockRelease();
|
||||
// VideoTarget interface
|
||||
virtual void SetBitmap(const BBitmap* bitmap);
|
||||
|
||||
void OverlayScreenshotPrepare();
|
||||
void OverlayScreenshotCleanup();
|
||||
|
||||
void DrawFrame();
|
||||
// VideoView
|
||||
void GetOverlayScaleLimits(float* minScale,
|
||||
float* maxScale) const;
|
||||
|
||||
void OverlayScreenshotPrepare();
|
||||
void OverlayScreenshotCleanup();
|
||||
|
||||
bool IsOverlayActive();
|
||||
void DisableOverlay();
|
||||
|
||||
private:
|
||||
void AttachedToWindow();
|
||||
void MessageReceived(BMessage *msg);
|
||||
void Draw(BRect updateRect);
|
||||
|
||||
private:
|
||||
Controller * fController;
|
||||
volatile bool fOverlayActive;
|
||||
rgb_color fOverlayKeyColor;
|
||||
bool fOverlayMode;
|
||||
overlay_restrictions fOverlayRestrictions;
|
||||
rgb_color fOverlayKeyColor;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // VIDEO_VIEW_H
|
||||
|
||||
@@ -0,0 +1,688 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include "NodeManager.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <MediaRoster.h>
|
||||
#include <scheduler.h>
|
||||
#include <TimeSource.h>
|
||||
|
||||
#include "AudioProducer.h"
|
||||
#include "AudioSupplier.h"
|
||||
#include "VideoConsumer.h"
|
||||
#include "VideoProducer.h"
|
||||
#include "VideoSupplier.h"
|
||||
|
||||
// debugging
|
||||
//#define TRACE_NODE_MANAGER
|
||||
#ifdef TRACE_NODE_MANAGER
|
||||
# define TRACE(x...) printf(x)
|
||||
# define ERROR(x...) fprintf(stderr, x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
# define ERROR(x...) fprintf(stderr, x)
|
||||
#endif
|
||||
|
||||
#define print_error(str, status) printf(str ", error: %s\n", strerror(status))
|
||||
|
||||
NodeManager::Connection::Connection()
|
||||
: connected(false)
|
||||
{
|
||||
memset(&format, 0, sizeof(media_format));
|
||||
}
|
||||
|
||||
// constructor
|
||||
NodeManager::NodeManager()
|
||||
: PlaybackManager(),
|
||||
fMediaRoster(NULL),
|
||||
fAudioProducer(NULL),
|
||||
fVideoConsumer(NULL),
|
||||
fVideoProducer(NULL),
|
||||
fTimeSource(media_node::null),
|
||||
fAudioConnection(),
|
||||
fVideoConnection(),
|
||||
fPerformanceTimeBase(0),
|
||||
fStatus(B_NO_INIT),
|
||||
fVideoTarget(NULL),
|
||||
fAudioSupplier(NULL),
|
||||
fVideoSupplier(NULL),
|
||||
fVideoBounds(0, 0, -1, -1)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
NodeManager::~NodeManager()
|
||||
{
|
||||
_StopNodes();
|
||||
_TearDownNodes();
|
||||
}
|
||||
|
||||
// Init
|
||||
status_t
|
||||
NodeManager::Init(BRect videoBounds, float videoFrameRate, int32 loopingMode,
|
||||
bool loopingEnabled, float speed)
|
||||
{
|
||||
// init base class
|
||||
PlaybackManager::Init(videoFrameRate, loopingMode, loopingEnabled, speed);
|
||||
|
||||
// get some objects from a derived class
|
||||
if (!fVideoTarget)
|
||||
fVideoTarget = CreateVideoTarget();
|
||||
|
||||
if (!fVideoSupplier)
|
||||
fVideoSupplier = CreateVideoSupplier();
|
||||
|
||||
if (!fAudioSupplier)
|
||||
fAudioSupplier = CreateAudioSupplier();
|
||||
|
||||
return FormatChanged(videoBounds, videoFrameRate, true);
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
NodeManager::InitCheck()
|
||||
{
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
// SetPlayMode
|
||||
void
|
||||
NodeManager::SetPlayMode(int32 mode, bool continuePlaying)
|
||||
{
|
||||
// if (fMediaRoster && fMediaRoster->Lock()) {
|
||||
// BMediaNode::run_mode runMode = mode > 0 ?
|
||||
// BMediaNode::B_DROP_DATA : BMediaNode::B_OFFLINE;
|
||||
// fMediaRoster->SetRunModeNode(fVideoConnection.consumer, runMode);
|
||||
// fMediaRoster->Unlock();
|
||||
// }
|
||||
|
||||
PlaybackManager::SetPlayMode(mode, continuePlaying);
|
||||
}
|
||||
|
||||
// CleanupNodes
|
||||
status_t
|
||||
NodeManager::CleanupNodes()
|
||||
{
|
||||
_StopNodes();
|
||||
return _TearDownNodes(false);
|
||||
}
|
||||
|
||||
// FormatChanged
|
||||
status_t
|
||||
NodeManager::FormatChanged(BRect videoBounds, float videoFrameRate, bool force)
|
||||
{
|
||||
if (!force && videoBounds == VideoBounds()
|
||||
&& videoFrameRate == FramesPerSecond())
|
||||
return B_OK;
|
||||
|
||||
if (videoFrameRate != FramesPerSecond()) {
|
||||
PlaybackManager::Init(videoFrameRate, LoopMode(), IsLoopingEnabled(),
|
||||
Speed(), MODE_PLAYING_PAUSED_FORWARD, CurrentFrame());
|
||||
}
|
||||
|
||||
_StopNodes();
|
||||
_TearDownNodes();
|
||||
|
||||
SetVideoBounds(videoBounds);
|
||||
|
||||
status_t ret = _SetUpNodes();
|
||||
if (ret == B_OK)
|
||||
_StartNodes();
|
||||
else
|
||||
fprintf(stderr, "unable to setup nodes: %s\n", strerror(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// RealTimeForTime
|
||||
bigtime_t
|
||||
NodeManager::RealTimeForTime(bigtime_t time) const
|
||||
{
|
||||
bigtime_t result = 0;
|
||||
if (fVideoProducer) {
|
||||
result = fVideoProducer->TimeSource()->RealTimeFor(
|
||||
fPerformanceTimeBase + time, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// TimeForRealTime
|
||||
bigtime_t
|
||||
NodeManager::TimeForRealTime(bigtime_t time) const
|
||||
{
|
||||
bigtime_t result = 0;
|
||||
if (fVideoProducer) {
|
||||
result = fVideoProducer->TimeSource()->PerformanceTimeFor(time)
|
||||
- fPerformanceTimeBase;
|
||||
} else if (fAudioProducer) {
|
||||
result = fAudioProducer->TimeSource()->PerformanceTimeFor(time)
|
||||
- fPerformanceTimeBase;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// SetCurrentAudioTime
|
||||
void
|
||||
NodeManager::SetCurrentAudioTime(bigtime_t time)
|
||||
{
|
||||
//printf("NodeManager::SetCurrentAudioTime(%lld)\n", time);
|
||||
PlaybackManager::SetCurrentAudioTime(time);
|
||||
if (!fVideoProducer) {
|
||||
// running without video, update video time as well
|
||||
PlaybackManager::SetCurrentVideoTime(time);
|
||||
}
|
||||
}
|
||||
|
||||
// SetVideoBounds
|
||||
void
|
||||
NodeManager::SetVideoBounds(BRect bounds)
|
||||
{
|
||||
if (bounds != fVideoBounds) {
|
||||
fVideoBounds = bounds;
|
||||
NotifyVideoBoundsChanged(fVideoBounds);
|
||||
}
|
||||
}
|
||||
|
||||
// VideoBounds
|
||||
BRect
|
||||
NodeManager::VideoBounds() const
|
||||
{
|
||||
return fVideoBounds;
|
||||
}
|
||||
|
||||
// SetVideoTarget
|
||||
void
|
||||
NodeManager::SetVideoTarget(VideoTarget* videoTarget)
|
||||
{
|
||||
if (videoTarget != fVideoTarget) {
|
||||
fVideoTarget = videoTarget;
|
||||
if (fVideoConsumer)
|
||||
fVideoConsumer->SetTarget(fVideoTarget);
|
||||
}
|
||||
}
|
||||
|
||||
// GetVideoTarget
|
||||
VideoTarget*
|
||||
NodeManager::GetVideoTarget() const
|
||||
{
|
||||
return fVideoTarget;
|
||||
}
|
||||
|
||||
// SetVolume
|
||||
void
|
||||
NodeManager::SetVolume(float percent)
|
||||
{
|
||||
// TODO: would be nice to set the volume on the system mixer input of
|
||||
// our audio node...
|
||||
}
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
// _SetUpNodes
|
||||
status_t
|
||||
NodeManager::_SetUpNodes()
|
||||
{
|
||||
printf("NodeManager::_SetUpNodes()\n");
|
||||
|
||||
// find the media roster
|
||||
fStatus = B_OK;
|
||||
fMediaRoster = BMediaRoster::Roster(&fStatus);
|
||||
if (fStatus != B_OK) {
|
||||
print_error("Can't find the media roster", fStatus);
|
||||
fMediaRoster = NULL;
|
||||
return fStatus;
|
||||
}
|
||||
if (!fMediaRoster->Lock())
|
||||
return B_ERROR;
|
||||
|
||||
// find the time source
|
||||
fStatus = fMediaRoster->GetTimeSource(&fTimeSource);
|
||||
if (fStatus != B_OK) {
|
||||
print_error("Can't get a time source", fStatus);
|
||||
fMediaRoster->Unlock();
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
// setup the video nodes
|
||||
if (fVideoBounds.IsValid()) {
|
||||
fStatus = _SetUpVideoNodes();
|
||||
if (fStatus != B_OK) {
|
||||
print_error("Error setting up video nodes", fStatus);
|
||||
fMediaRoster->Unlock();
|
||||
return fStatus;
|
||||
}
|
||||
} else
|
||||
printf("running without video node\n");
|
||||
|
||||
// setup the audio nodes
|
||||
fStatus = _SetUpAudioNodes();
|
||||
if (fStatus != B_OK) {
|
||||
print_error("Error setting up video nodes", fStatus);
|
||||
fMediaRoster->Unlock();
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
|
||||
// we're done mocking with the media roster
|
||||
fMediaRoster->Unlock();
|
||||
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
|
||||
// _SetUpVideoNodes
|
||||
status_t
|
||||
NodeManager::_SetUpVideoNodes()
|
||||
{
|
||||
// create the video producer node
|
||||
fVideoProducer = new VideoProducer(NULL, "MediaPlayer Video Out", 0,
|
||||
this, fVideoSupplier);
|
||||
|
||||
// register the producer node
|
||||
fStatus = fMediaRoster->RegisterNode(fVideoProducer);
|
||||
if (fStatus != B_OK) {
|
||||
print_error("Can't register the video producer", fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
// make sure the Media Roster knows that we're using the node
|
||||
// fMediaRoster->GetNodeFor(fVideoProducer->Node().node,
|
||||
// &fVideoConnection.producer);
|
||||
fVideoConnection.producer = fVideoProducer->Node();
|
||||
|
||||
// create the video consumer node
|
||||
fVideoConsumer = new VideoConsumer("MediaPlayer Video In", NULL, 0, this,
|
||||
fVideoTarget);
|
||||
|
||||
// register the consumer node
|
||||
fStatus = fMediaRoster->RegisterNode(fVideoConsumer);
|
||||
if (fStatus != B_OK) {
|
||||
print_error("Can't register the video consumer", fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
// make sure the Media Roster knows that we're using the node
|
||||
// fMediaRoster->GetNodeFor(fVideoConsumer->Node().node,
|
||||
// &fVideoConnection.consumer);
|
||||
fVideoConnection.consumer = fVideoConsumer->Node();
|
||||
|
||||
// find free producer output
|
||||
media_input videoInput;
|
||||
media_output videoOutput;
|
||||
int32 count = 1;
|
||||
fStatus = fMediaRoster->GetFreeOutputsFor(fVideoConnection.producer,
|
||||
&videoOutput, 1, &count, B_MEDIA_RAW_VIDEO);
|
||||
if (fStatus != B_OK || count < 1) {
|
||||
fStatus = B_RESOURCE_UNAVAILABLE;
|
||||
print_error("Can't find an available video stream", fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
// find free consumer input
|
||||
count = 1;
|
||||
fStatus = fMediaRoster->GetFreeInputsFor(fVideoConnection.consumer,
|
||||
&videoInput, 1, &count, B_MEDIA_RAW_VIDEO);
|
||||
if (fStatus != B_OK || count < 1) {
|
||||
fStatus = B_RESOURCE_UNAVAILABLE;
|
||||
print_error("Can't find an available connection to the video window",
|
||||
fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
// connect the nodes
|
||||
media_format format;
|
||||
format.type = B_MEDIA_RAW_VIDEO;
|
||||
media_raw_video_format videoFormat = {
|
||||
FramesPerSecond(), 1, 0,
|
||||
fVideoBounds.IntegerWidth(),
|
||||
B_VIDEO_TOP_LEFT_RIGHT, 1, 1,
|
||||
{
|
||||
B_YCbCr422,
|
||||
fVideoBounds.IntegerWidth() + 1,
|
||||
fVideoBounds.IntegerHeight() + 1,
|
||||
0, 0, 0
|
||||
}
|
||||
};
|
||||
format.u.raw_video = videoFormat;
|
||||
|
||||
// connect video producer to consumer (B_YCbCr422)
|
||||
fStatus = fMediaRoster->Connect(videoOutput.source, videoInput.destination,
|
||||
&format, &videoOutput, &videoInput);
|
||||
|
||||
if (fStatus != B_OK) {
|
||||
print_error("Can't connect the video source to the video window... "
|
||||
"trying B_RGB32", fStatus);
|
||||
format.u.raw_video.display.format = B_RGB32;
|
||||
// connect video producer to consumer (B_RGB32)
|
||||
fStatus = fMediaRoster->Connect(videoOutput.source,
|
||||
videoInput.destination, &format, &videoOutput, &videoInput);
|
||||
}
|
||||
// bail if second attempt failed too
|
||||
if (fStatus != B_OK) {
|
||||
print_error("Can't connect the video source to the video window",
|
||||
fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
// the inputs and outputs might have been reassigned during the
|
||||
// nodes' negotiation of the Connect(). That's why we wait until
|
||||
// after Connect() finishes to save their contents.
|
||||
fVideoConnection.format = format;
|
||||
fVideoConnection.source = videoOutput.source;
|
||||
fVideoConnection.destination = videoInput.destination;
|
||||
fVideoConnection.connected = true;
|
||||
|
||||
// set time sources
|
||||
fStatus = fMediaRoster->SetTimeSourceFor(fVideoConnection.producer.node,
|
||||
fTimeSource.node);
|
||||
if (fStatus != B_OK) {
|
||||
print_error("Can't set the timesource for the video source", fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
fStatus = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(),
|
||||
fTimeSource.node);
|
||||
if (fStatus != B_OK) {
|
||||
print_error("Can't set the timesource for the video window", fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
// _SetUpAudioNodes
|
||||
status_t
|
||||
NodeManager::_SetUpAudioNodes()
|
||||
{
|
||||
fAudioProducer = new AudioProducer("MediaPlayer Audio Out", fAudioSupplier);
|
||||
fStatus = fMediaRoster->RegisterNode(fAudioProducer);
|
||||
if (fStatus != B_OK) {
|
||||
print_error("unable to register audio producer node!\n", fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
// make sure the Media Roster knows that we're using the node
|
||||
// fMediaRoster->GetNodeFor(fAudioProducer->Node().node,
|
||||
// &fAudioConnection.producer);
|
||||
fAudioConnection.producer = fAudioProducer->Node();
|
||||
|
||||
// connect to the mixer
|
||||
fStatus = fMediaRoster->GetAudioMixer(&fAudioConnection.consumer);
|
||||
if (fStatus != B_OK) {
|
||||
print_error("unable to get the system mixer", fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
fMediaRoster->SetTimeSourceFor(fAudioConnection.producer.node,
|
||||
fTimeSource.node);
|
||||
|
||||
// got the nodes; now we find the endpoints of the connection
|
||||
media_input mixerInput;
|
||||
media_output soundOutput;
|
||||
int32 count = 1;
|
||||
fStatus = fMediaRoster->GetFreeOutputsFor(fAudioConnection.producer,
|
||||
&soundOutput, 1, &count);
|
||||
if (fStatus != B_OK) {
|
||||
print_error("unable to get a free output from the producer node",
|
||||
fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
count = 1;
|
||||
fStatus = fMediaRoster->GetFreeInputsFor(fAudioConnection.consumer,
|
||||
&mixerInput, 1, &count);
|
||||
if (fStatus != B_OK) {
|
||||
print_error("unable to get a free input to the mixer", fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
// got the endpoints; now we connect it!
|
||||
media_format audio_format;
|
||||
audio_format.type = B_MEDIA_RAW_AUDIO;
|
||||
audio_format.u.raw_audio = media_raw_audio_format::wildcard;
|
||||
fStatus = fMediaRoster->Connect(soundOutput.source, mixerInput.destination,
|
||||
&audio_format, &soundOutput, &mixerInput);
|
||||
if (fStatus != B_OK) {
|
||||
print_error("unable to connect audio nodes", fStatus);
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
// the inputs and outputs might have been reassigned during the
|
||||
// nodes' negotiation of the Connect(). That's why we wait until
|
||||
// after Connect() finishes to save their contents.
|
||||
fAudioConnection.format = audio_format;
|
||||
fAudioConnection.source = soundOutput.source;
|
||||
fAudioConnection.destination = mixerInput.destination;
|
||||
fAudioConnection.connected = true;
|
||||
|
||||
// Set an appropriate run mode for the producer
|
||||
fMediaRoster->SetRunModeNode(fAudioConnection.producer,
|
||||
BMediaNode::B_INCREASE_LATENCY);
|
||||
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
// _TearDownNodes
|
||||
status_t
|
||||
NodeManager::_TearDownNodes(bool disconnect)
|
||||
{
|
||||
TRACE("NodeManager::_TearDownNodes()\n");
|
||||
status_t err = B_OK;
|
||||
fMediaRoster = BMediaRoster::Roster(&err);
|
||||
if (err != B_OK) {
|
||||
fprintf(stderr, "NodeManager::_TearDownNodes() - error getting media "
|
||||
"roster: %s\n", strerror(err));
|
||||
fMediaRoster = NULL;
|
||||
}
|
||||
// begin mucking with the media roster
|
||||
bool mediaRosterLocked = false;
|
||||
if (fMediaRoster && fMediaRoster->Lock())
|
||||
mediaRosterLocked = true;
|
||||
|
||||
if (fVideoConsumer && fVideoProducer && fVideoConnection.connected) {
|
||||
// disconnect
|
||||
if (fMediaRoster) {
|
||||
TRACE(" disconnecting video...\n");
|
||||
err = fMediaRoster->Disconnect(fVideoConnection.producer.node,
|
||||
fVideoConnection.source, fVideoConnection.consumer.node,
|
||||
fVideoConnection.destination);
|
||||
if (err < B_OK)
|
||||
print_error("unable to disconnect video nodes", err);
|
||||
} else {
|
||||
fprintf(stderr, "NodeManager::_TearDownNodes() - cannot "
|
||||
"disconnect video nodes, no media server!\n");
|
||||
}
|
||||
fVideoConnection.connected = false;
|
||||
}
|
||||
if (fVideoProducer) {
|
||||
TRACE(" releasing video producer...\n");
|
||||
fVideoProducer->Release();
|
||||
fVideoProducer = NULL;
|
||||
}
|
||||
if (fVideoConsumer) {
|
||||
TRACE(" releasing video consumer...\n");
|
||||
fVideoConsumer->Release();
|
||||
fVideoConsumer = NULL;
|
||||
}
|
||||
if (fAudioProducer) {
|
||||
disconnect = fAudioConnection.connected;
|
||||
// Ordinarily we'd stop *all* of the nodes in the chain at this point.
|
||||
// However, one of the nodes is the System Mixer, and stopping the
|
||||
// Mixer is a Bad Idea (tm). So, we just disconnect from it, and
|
||||
// release our references to the nodes that we're using. We *are*
|
||||
// supposed to do that even for global nodes like the Mixer.
|
||||
if (fMediaRoster && disconnect) {
|
||||
TRACE(" disconnecting audio...\n");
|
||||
err = fMediaRoster->Disconnect(fAudioConnection.producer.node,
|
||||
fAudioConnection.source, fAudioConnection.consumer.node,
|
||||
fAudioConnection.destination);
|
||||
if (err < B_OK) {
|
||||
print_error("unable to disconnect audio nodes", err);
|
||||
disconnect = false;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "NodeManager::_TearDownNodes() - cannot "
|
||||
"disconnect audio nodes, no media server!\n");
|
||||
}
|
||||
|
||||
TRACE(" releasing audio producer...\n");
|
||||
fAudioProducer->Release();
|
||||
fAudioProducer = NULL;
|
||||
fAudioConnection.connected = false;
|
||||
|
||||
if (fMediaRoster && disconnect) {
|
||||
TRACE(" releasing audio consumer...\n");
|
||||
fMediaRoster->ReleaseNode(fAudioConnection.consumer);
|
||||
} else {
|
||||
fprintf(stderr, "NodeManager::_TearDownNodes() - cannot release "
|
||||
"audio consumer (system mixer)!\n");
|
||||
}
|
||||
}
|
||||
// we're done mucking with the media roster
|
||||
if (mediaRosterLocked && fMediaRoster)
|
||||
fMediaRoster->Unlock();
|
||||
TRACE("NodeManager::_TearDownNodes() done\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
// _StartNodes
|
||||
status_t
|
||||
NodeManager::_StartNodes()
|
||||
{
|
||||
status_t status = B_NO_INIT;
|
||||
if (!fMediaRoster || !fAudioProducer)
|
||||
return status;
|
||||
// begin mucking with the media roster
|
||||
if (fMediaRoster->Lock()) {
|
||||
bigtime_t latency = 0;
|
||||
bigtime_t initLatency = 0;
|
||||
if (fVideoProducer && fVideoConsumer) {
|
||||
// figure out what recording delay to use
|
||||
status = fMediaRoster->GetLatencyFor(fVideoConnection.producer,
|
||||
&latency);
|
||||
if (status < B_OK) {
|
||||
print_error("error getting latency for video producer",
|
||||
status);
|
||||
} else
|
||||
TRACE("video latency: %Ld\n", latency);
|
||||
status = fMediaRoster->SetProducerRunModeDelay(
|
||||
fVideoConnection.producer, latency);
|
||||
if (status < B_OK) {
|
||||
print_error("error settings run mode delay for video producer",
|
||||
status);
|
||||
}
|
||||
|
||||
// start the nodes
|
||||
status = fMediaRoster->GetInitialLatencyFor(
|
||||
fVideoConnection.producer, &initLatency);
|
||||
if (status < B_OK) {
|
||||
print_error("error getting initial latency for video producer",
|
||||
status);
|
||||
}
|
||||
}
|
||||
initLatency += estimate_max_scheduling_latency();
|
||||
|
||||
bigtime_t audioLatency = 0;
|
||||
status = fMediaRoster->GetLatencyFor(fAudioConnection.producer,
|
||||
&audioLatency);
|
||||
TRACE("audio latency: %Ld\n", audioLatency);
|
||||
|
||||
BTimeSource* timeSource;
|
||||
if (fVideoProducer) {
|
||||
timeSource = fMediaRoster->MakeTimeSourceFor(
|
||||
fVideoConnection.producer);
|
||||
} else {
|
||||
timeSource = fMediaRoster->MakeTimeSourceFor(
|
||||
fAudioConnection.producer);
|
||||
}
|
||||
bool running = timeSource->IsRunning();
|
||||
|
||||
// workaround for people without sound cards
|
||||
// because the system time source won't be running
|
||||
bigtime_t real = BTimeSource::RealTime();
|
||||
if (!running) {
|
||||
status = fMediaRoster->StartTimeSource(fTimeSource, real);
|
||||
if (status != B_OK) {
|
||||
timeSource->Release();
|
||||
print_error("cannot start time source!", status);
|
||||
return status;
|
||||
}
|
||||
status = fMediaRoster->SeekTimeSource(fTimeSource, 0, real);
|
||||
if (status != B_OK) {
|
||||
timeSource->Release();
|
||||
print_error("cannot seek time source!", status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
bigtime_t perf = timeSource->PerformanceTimeFor(real + latency
|
||||
+ initLatency);
|
||||
|
||||
timeSource->Release();
|
||||
|
||||
// start the nodes
|
||||
if (fVideoProducer && fVideoConsumer) {
|
||||
status = fMediaRoster->StartNode(fVideoConnection.consumer, perf);
|
||||
if (status != B_OK) {
|
||||
print_error("Can't start the video consumer", status);
|
||||
return status;
|
||||
}
|
||||
status = fMediaRoster->StartNode(fVideoConnection.producer, perf);
|
||||
if (status != B_OK) {
|
||||
print_error("Can't start the video producer", status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
fAudioProducer->SetRunning(true);
|
||||
status = fMediaRoster->StartNode(fAudioConnection.producer, perf);
|
||||
if (status != B_OK) {
|
||||
print_error("Can't start the audio producer", status);
|
||||
return status;
|
||||
}
|
||||
fPerformanceTimeBase = perf;
|
||||
// done mucking with the media roster
|
||||
fMediaRoster->Unlock();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// _StopNodes
|
||||
void
|
||||
NodeManager::_StopNodes()
|
||||
{
|
||||
TRACE("NodeManager::_StopNodes()\n");
|
||||
// if (PlayMode() == MODE_PLAYING_PAUSED_FORWARD
|
||||
// || PlayMode() == MODE_PLAYING_PAUSED_FORWARD)
|
||||
// return;
|
||||
fMediaRoster = BMediaRoster::Roster();
|
||||
if (!fMediaRoster) {
|
||||
if (fAudioProducer)
|
||||
fAudioProducer->SetRunning(false);
|
||||
return;
|
||||
} else if (fMediaRoster->Lock()) {
|
||||
// begin mucking with the media roster
|
||||
if (fVideoProducer) {
|
||||
TRACE(" stopping video producer...\n");
|
||||
fMediaRoster->StopNode(fVideoConnection.producer, 0, true);
|
||||
}
|
||||
if (fAudioProducer) {
|
||||
TRACE(" stopping audio producer...\n");
|
||||
fAudioProducer->SetRunning(false);
|
||||
fMediaRoster->StopNode(fAudioConnection.producer, 0, true);
|
||||
// synchronous stop
|
||||
}
|
||||
if (fVideoConsumer) {
|
||||
TRACE(" stopping video consumer...\n");
|
||||
fMediaRoster->StopNode(fVideoConnection.consumer, 0, true);
|
||||
}
|
||||
TRACE(" all nodes stopped\n");
|
||||
// done mucking with the media roster
|
||||
fMediaRoster->Unlock();
|
||||
}
|
||||
TRACE("NodeManager::_StopNodes() done\n");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
//! This class controls our media nodes and general playback
|
||||
|
||||
#ifndef NODE_MANAGER_H
|
||||
#define NODE_MANAGER_H
|
||||
|
||||
#include <MediaNode.h>
|
||||
|
||||
#include "PlaybackManager.h"
|
||||
|
||||
class AudioProducer;
|
||||
class VideoTarget;
|
||||
class VideoProducer;
|
||||
class VideoConsumer;
|
||||
class AudioSupplier;
|
||||
class VideoSupplier;
|
||||
|
||||
class NodeManager : public PlaybackManager {
|
||||
public:
|
||||
NodeManager();
|
||||
virtual ~NodeManager();
|
||||
|
||||
// must be implemented in derived classes
|
||||
virtual VideoTarget* CreateVideoTarget() = 0;
|
||||
virtual VideoSupplier* CreateVideoSupplier() = 0;
|
||||
virtual AudioSupplier* CreateAudioSupplier() = 0;
|
||||
|
||||
// NodeManager
|
||||
status_t Init(BRect videoBounds, float videoFrameRate,
|
||||
int32 loopingMode = LOOPING_ALL,
|
||||
bool loopingEnabled = true,
|
||||
float speed = 1.0);
|
||||
status_t InitCheck();
|
||||
// only call this if the
|
||||
// media_server has died!
|
||||
status_t CleanupNodes();
|
||||
|
||||
status_t FormatChanged(BRect videoBounds,
|
||||
float videoFrameRate, bool force = false);
|
||||
virtual void SetPlayMode(int32 mode,
|
||||
bool continuePlaying = true);
|
||||
|
||||
virtual bigtime_t RealTimeForTime(bigtime_t time) const;
|
||||
virtual bigtime_t TimeForRealTime(bigtime_t time) const;
|
||||
|
||||
virtual void SetCurrentAudioTime(bigtime_t time);
|
||||
|
||||
void SetVideoBounds(BRect bounds);
|
||||
virtual BRect VideoBounds() const;
|
||||
|
||||
void SetVideoTarget(VideoTarget* vcTarget);
|
||||
VideoTarget* GetVideoTarget() const;
|
||||
|
||||
virtual void SetVolume(float percent);
|
||||
|
||||
private:
|
||||
status_t _SetUpNodes();
|
||||
status_t _SetUpVideoNodes();
|
||||
status_t _SetUpAudioNodes();
|
||||
status_t _TearDownNodes(bool disconnect = true);
|
||||
status_t _StartNodes();
|
||||
void _StopNodes();
|
||||
|
||||
private:
|
||||
struct Connection {
|
||||
Connection();
|
||||
|
||||
media_node producer;
|
||||
media_node consumer;
|
||||
media_source source;
|
||||
media_destination destination;
|
||||
media_format format;
|
||||
bool connected;
|
||||
};
|
||||
|
||||
private:
|
||||
BMediaRoster* fMediaRoster;
|
||||
|
||||
// media nodes
|
||||
AudioProducer* fAudioProducer;
|
||||
VideoConsumer* fVideoConsumer;
|
||||
VideoProducer* fVideoProducer;
|
||||
media_node fTimeSource;
|
||||
|
||||
Connection fAudioConnection;
|
||||
Connection fVideoConnection;
|
||||
|
||||
bigtime_t fPerformanceTimeBase;
|
||||
|
||||
status_t fStatus;
|
||||
//
|
||||
VideoTarget* fVideoTarget;
|
||||
AudioSupplier* fAudioSupplier;
|
||||
VideoSupplier* fVideoSupplier;
|
||||
BRect fVideoBounds;
|
||||
};
|
||||
|
||||
|
||||
#endif // NODE_MANAGER_H
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include <Message.h>
|
||||
|
||||
#include "PlaybackLOAdapter.h"
|
||||
|
||||
|
||||
PlaybackLOAdapter::PlaybackLOAdapter(BHandler* handler)
|
||||
: AbstractLOAdapter(handler)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
PlaybackLOAdapter::PlaybackLOAdapter(const BMessenger& messenger)
|
||||
: AbstractLOAdapter(messenger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
PlaybackLOAdapter::~PlaybackLOAdapter()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackLOAdapter::PlayModeChanged(int32 mode)
|
||||
{
|
||||
BMessage message(MSG_PLAYBACK_PLAY_MODE_CHANGED);
|
||||
message.AddInt32("play mode", mode);
|
||||
DeliverMessage(message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackLOAdapter::LoopModeChanged(int32 mode)
|
||||
{
|
||||
BMessage message(MSG_PLAYBACK_LOOP_MODE_CHANGED);
|
||||
message.AddInt32("loop mode", mode);
|
||||
DeliverMessage(message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackLOAdapter::LoopingEnabledChanged(bool enabled)
|
||||
{
|
||||
BMessage message(MSG_PLAYBACK_LOOPING_ENABLED_CHANGED);
|
||||
message.AddBool("looping enabled", enabled);
|
||||
DeliverMessage(message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackLOAdapter::VideoBoundsChanged(BRect bounds)
|
||||
{
|
||||
BMessage message(MSG_PLAYBACK_VIDEO_BOUNDS_CHANGED);
|
||||
message.AddRect("video bounds", bounds);
|
||||
DeliverMessage(message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackLOAdapter::FramesPerSecondChanged(float fps)
|
||||
{
|
||||
BMessage message(MSG_PLAYBACK_FPS_CHANGED);
|
||||
message.AddFloat("fps", fps);
|
||||
DeliverMessage(message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackLOAdapter::CurrentFrameChanged(double frame)
|
||||
{
|
||||
BMessage message(MSG_PLAYBACK_CURRENT_FRAME_CHANGED);
|
||||
message.AddDouble("current frame", frame);
|
||||
DeliverMessage(message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackLOAdapter::SpeedChanged(float speed)
|
||||
{
|
||||
BMessage message(MSG_PLAYBACK_SPEED_CHANGED);
|
||||
message.AddFloat("speed", speed);
|
||||
DeliverMessage(message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackLOAdapter::FrameDropped()
|
||||
{
|
||||
DeliverMessage(MSG_PLAYBACK_FRAME_DROPPED);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef PLAYBACK_LO_ADAPTER_H
|
||||
#define PLAYBACK_LO_ADAPTER_H
|
||||
|
||||
|
||||
#include "AbstractLOAdapter.h"
|
||||
#include "PlaybackListener.h"
|
||||
|
||||
|
||||
enum {
|
||||
MSG_PLAYBACK_PLAY_MODE_CHANGED = 'ppmc',
|
||||
MSG_PLAYBACK_LOOP_MODE_CHANGED = 'plmc',
|
||||
MSG_PLAYBACK_LOOPING_ENABLED_CHANGED = 'plec',
|
||||
MSG_PLAYBACK_VIDEO_BOUNDS_CHANGED = 'pmbc',
|
||||
MSG_PLAYBACK_FPS_CHANGED = 'pfps',
|
||||
MSG_PLAYBACK_CURRENT_FRAME_CHANGED = 'pcfc',
|
||||
MSG_PLAYBACK_SPEED_CHANGED = 'pspc',
|
||||
MSG_PLAYBACK_FRAME_DROPPED = 'pfdr',
|
||||
};
|
||||
|
||||
|
||||
class PlaybackLOAdapter : public AbstractLOAdapter, public PlaybackListener {
|
||||
public:
|
||||
PlaybackLOAdapter(BHandler* handler);
|
||||
PlaybackLOAdapter(
|
||||
const BMessenger& messenger);
|
||||
virtual ~PlaybackLOAdapter();
|
||||
|
||||
virtual void PlayModeChanged(int32 mode);
|
||||
virtual void LoopModeChanged(int32 mode);
|
||||
virtual void LoopingEnabledChanged(bool enabled);
|
||||
virtual void VideoBoundsChanged(BRect bounds);
|
||||
virtual void FramesPerSecondChanged(float fps);
|
||||
virtual void CurrentFrameChanged(double frame);
|
||||
virtual void SpeedChanged(float speed);
|
||||
virtual void FrameDropped();
|
||||
};
|
||||
|
||||
#endif // PLAYBACK_LO_ADAPTER_H
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include "PlaybackListener.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
PlaybackListener::PlaybackListener()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
PlaybackListener::~PlaybackListener()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackListener::PlayModeChanged(int32 mode)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackListener::LoopModeChanged(int32 mode)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackListener::LoopingEnabledChanged(bool enabled)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackListener::VideoBoundsChanged(BRect bounds)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackListener::FramesPerSecondChanged(float fps)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackListener::CurrentFrameChanged(double frame)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackListener::SpeedChanged(float speed)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaybackListener::FrameDropped()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
/*! This class listens to a PlaybackManager
|
||||
|
||||
The hooks are called by PlaybackManager after it executed a command,
|
||||
to keep every listener informed. FrameDropped() is something the nodes
|
||||
can call and it is passed onto the contollers, so that they can respond
|
||||
by displaying some kind of warning. */
|
||||
|
||||
#ifndef PLAYBACK_LISTENER_H
|
||||
#define PLAYBACK_LISTENER_H
|
||||
|
||||
#include <Rect.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
class PlaybackListener {
|
||||
public:
|
||||
PlaybackListener();
|
||||
virtual ~PlaybackListener();
|
||||
|
||||
virtual void PlayModeChanged(int32 mode);
|
||||
virtual void LoopModeChanged(int32 mode);
|
||||
virtual void LoopingEnabledChanged(bool enabled);
|
||||
|
||||
virtual void VideoBoundsChanged(BRect bounds);
|
||||
virtual void FramesPerSecondChanged(float fps);
|
||||
virtual void SpeedChanged(float speed);
|
||||
|
||||
virtual void CurrentFrameChanged(double frame);
|
||||
|
||||
virtual void FrameDropped();
|
||||
};
|
||||
|
||||
|
||||
#endif // PLAYBACK_LISTENER_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
/*! This class controls our playback
|
||||
Note: Add/RemoveListener() are the only methods that lock the object
|
||||
themselves. All other methods need it to be locked before.
|
||||
*/
|
||||
#ifndef PLAYBACK_MANAGER_H
|
||||
#define PLAYBACK_MANAGER_H
|
||||
|
||||
|
||||
#include <List.h>
|
||||
#include <Looper.h>
|
||||
#include <Rect.h>
|
||||
|
||||
|
||||
class PlaybackListener;
|
||||
|
||||
|
||||
enum {
|
||||
MODE_PLAYING_FORWARD = 1,
|
||||
MODE_PLAYING_BACKWARD = 2,
|
||||
MODE_PLAYING_PAUSED_FORWARD = -1,
|
||||
MODE_PLAYING_PAUSED_BACKWARD = -2,
|
||||
};
|
||||
|
||||
enum {
|
||||
LOOPING_ALL = 0,
|
||||
LOOPING_RANGE = 1,
|
||||
LOOPING_SELECTION = 2,
|
||||
LOOPING_VISIBLE = 3,
|
||||
};
|
||||
|
||||
enum {
|
||||
MSG_PLAYBACK_FORCE_UPDATE = 'pbfu',
|
||||
MSG_PLAYBACK_SET_RANGE = 'pbsr',
|
||||
MSG_PLAYBACK_SET_VISIBLE = 'pbsv',
|
||||
MSG_PLAYBACK_SET_LOOP_MODE = 'pbsl',
|
||||
};
|
||||
|
||||
|
||||
class PlaybackManager : public BLooper {
|
||||
private:
|
||||
struct PlayingState;
|
||||
struct SpeedInfo;
|
||||
|
||||
public:
|
||||
PlaybackManager();
|
||||
virtual ~PlaybackManager();
|
||||
|
||||
void Init(float frameRate,
|
||||
int32 loopingMode = LOOPING_ALL,
|
||||
bool loopingEnabled = true,
|
||||
float speed = 1.0,
|
||||
int32 playMode = MODE_PLAYING_PAUSED_FORWARD,
|
||||
int32 currentFrame = 0);
|
||||
void Cleanup();
|
||||
|
||||
// BHandler interface
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
void StartPlaying(bool atBeginning = false);
|
||||
void StopPlaying();
|
||||
void TogglePlaying(bool atBeginning = false);
|
||||
void PausePlaying();
|
||||
bool IsPlaying() const;
|
||||
|
||||
int32 PlayMode() const;
|
||||
int32 LoopMode() const;
|
||||
bool IsLoopingEnabled() const;
|
||||
int32 CurrentFrame() const;
|
||||
float Speed() const;
|
||||
|
||||
virtual void SetFramesPerSecond(float framesPerSecond);
|
||||
float FramesPerSecond() const;
|
||||
|
||||
virtual BRect VideoBounds() const = 0;
|
||||
|
||||
virtual void FrameDropped() const;
|
||||
|
||||
void DurationChanged();
|
||||
virtual int64 Duration() = 0;
|
||||
|
||||
virtual void SetVolume(float percent) = 0;
|
||||
|
||||
// playing state manipulation
|
||||
void SetCurrentFrame(int64 frame);
|
||||
virtual void SetPlayMode(int32 mode,
|
||||
bool continuePlaying = true);
|
||||
void SetLoopMode(int32 mode,
|
||||
bool continuePlaying = true);
|
||||
void SetLoopingEnabled(bool enabled,
|
||||
bool continuePlaying = true);
|
||||
void SetSpeed(float speed);
|
||||
|
||||
// playing state change info
|
||||
int64 NextFrame() const;
|
||||
int64 NextPlaylistFrame() const;
|
||||
|
||||
int64 FirstPlaybackRangeFrame();
|
||||
int64 LastPlaybackRangeFrame();
|
||||
|
||||
// playing state access
|
||||
int64 StartFrameAtFrame(int64 frame);
|
||||
int64 StartFrameAtTime(bigtime_t time);
|
||||
int64 EndFrameAtFrame(int64 frame);
|
||||
int64 EndFrameAtTime(bigtime_t time);
|
||||
int64 FrameCountAtFrame(int64 frame);
|
||||
int64 FrameCountAtTime(bigtime_t time);
|
||||
int32 PlayModeAtFrame(int64 frame);
|
||||
int32 PlayModeAtTime(bigtime_t time);
|
||||
int32 LoopModeAtFrame(int64 frame);
|
||||
int32 LoopModeAtTime(bigtime_t time);
|
||||
// ...
|
||||
|
||||
// playing frame/time/interval info
|
||||
int64 PlaylistFrameAtFrame(int64 frame,
|
||||
int32& playingDirection,
|
||||
bool& newState) const;
|
||||
int64 PlaylistFrameAtFrame(int64 frame,
|
||||
int32& playingDirection) const;
|
||||
int64 PlaylistFrameAtFrame(int64 frame) const;
|
||||
int64 NextChangeFrame(int64 startFrame,
|
||||
int64 endFrame) const;
|
||||
bigtime_t NextChangeTime(bigtime_t startTime,
|
||||
bigtime_t endTime) const;
|
||||
void GetPlaylistFrameInterval(
|
||||
int64 startFrame, int64& endFrame,
|
||||
int64& xStartFrame, int64& xEndFrame,
|
||||
int32& playingDirection) const;
|
||||
|
||||
// PlaybackManagerInterface
|
||||
virtual void GetPlaylistTimeInterval(
|
||||
bigtime_t startTime, bigtime_t& endTime,
|
||||
bigtime_t& xStartTime, bigtime_t& xEndTime,
|
||||
float& playingSpeed) const;
|
||||
|
||||
// conversion: video frame <-> (performance) time
|
||||
int64 FrameForTime(bigtime_t time) const;
|
||||
bigtime_t TimeForFrame(int64 frame) const;
|
||||
// conversion: (performance) time <-> real time
|
||||
virtual bigtime_t RealTimeForTime(bigtime_t time) const = 0;
|
||||
virtual bigtime_t TimeForRealTime(bigtime_t time) const = 0;
|
||||
// conversion: Playist frame <-> Playlist time
|
||||
int64 PlaylistFrameForTime(bigtime_t time) const;
|
||||
bigtime_t PlaylistTimeForFrame(int64 frame) const;
|
||||
|
||||
// to be called by audio/video producers
|
||||
virtual void SetCurrentAudioTime(bigtime_t time);
|
||||
void SetCurrentVideoFrame(int64 frame);
|
||||
void SetCurrentVideoTime(bigtime_t time);
|
||||
void SetPerformanceFrame(int64 frame);
|
||||
void SetPerformanceTime(bigtime_t time);
|
||||
|
||||
// listener support
|
||||
void AddListener(PlaybackListener* listener);
|
||||
void RemoveListener(PlaybackListener* listener);
|
||||
|
||||
virtual void NotifyPlayModeChanged(int32 mode) const;
|
||||
virtual void NotifyLoopModeChanged(int32 mode) const;
|
||||
virtual void NotifyLoopingEnabledChanged(
|
||||
bool enabled) const;
|
||||
virtual void NotifyVideoBoundsChanged(BRect bounds) const;
|
||||
virtual void NotifyFPSChanged(float fps) const;
|
||||
virtual void NotifyCurrentFrameChanged(int32 frame) const;
|
||||
virtual void NotifySpeedChanged(float speed) const;
|
||||
virtual void NotifyFrameDropped() const;
|
||||
virtual void NotifyStopFrameReached() const;
|
||||
|
||||
// debugging
|
||||
void PrintState(PlayingState* state);
|
||||
void PrintStateAtFrame(int64 frame);
|
||||
|
||||
private:
|
||||
// state management
|
||||
void _PushState(PlayingState* state,
|
||||
bool adjustCurrentFrame);
|
||||
void _UpdateStates();
|
||||
int32 _IndexForFrame(int64 frame) const;
|
||||
int32 _IndexForTime(bigtime_t time) const;
|
||||
PlayingState* _LastState() const;
|
||||
PlayingState* _StateAt(int32 index) const;
|
||||
PlayingState* _StateAtTime(bigtime_t time) const;
|
||||
PlayingState* _StateAtFrame(int64 frame) const;
|
||||
|
||||
static int32 _PlayingDirectionFor(int32 playingMode);
|
||||
static int32 _PlayingDirectionFor(PlayingState* state);
|
||||
static void _GetPlayingBoundsFor(PlayingState* state,
|
||||
int64& startFrame,
|
||||
int64& endFrame,
|
||||
int64& frameCount);
|
||||
static int64 _PlayingStartFrameFor(PlayingState* state);
|
||||
static int64 _PlayingEndFrameFor(PlayingState* state);
|
||||
static int64 _RangeFrameForFrame(PlayingState* state,
|
||||
int64 frame);
|
||||
static int64 _FrameForRangeFrame(PlayingState* state,
|
||||
int64 index);
|
||||
static int64 _NextFrameInRange(PlayingState* state,
|
||||
int64 frame);
|
||||
|
||||
// speed management
|
||||
void _PushSpeedInfo(SpeedInfo* info);
|
||||
SpeedInfo* _LastSpeedInfo() const;
|
||||
SpeedInfo* _SpeedInfoAt(int32 index) const;
|
||||
int32 _SpeedInfoIndexForFrame(int64 frame) const;
|
||||
int32 _SpeedInfoIndexForTime(bigtime_t time) const;
|
||||
SpeedInfo* _SpeedInfoForFrame(int64 frame) const;
|
||||
SpeedInfo* _SpeedInfoForTime(bigtime_t time) const;
|
||||
void _UpdateSpeedInfos();
|
||||
|
||||
bigtime_t _TimeForLastFrame() const;
|
||||
bigtime_t _TimeForNextFrame() const;
|
||||
|
||||
void _CheckStopPlaying();
|
||||
|
||||
private:
|
||||
BList fStates;
|
||||
BList fSpeeds;
|
||||
volatile bigtime_t fCurrentAudioTime;
|
||||
volatile bigtime_t fCurrentVideoTime;
|
||||
volatile bigtime_t fPerformanceTime;
|
||||
volatile float fFrameRate; // video frame rate
|
||||
volatile bigtime_t fStopPlayingFrame; // frame at which playing
|
||||
// shall be stopped,
|
||||
// disabled: -1
|
||||
|
||||
BList fListeners;
|
||||
};
|
||||
|
||||
|
||||
#endif // PLAYBACK_MANAGER_H
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
#include "AudioAdapter.h"
|
||||
|
||||
#include <new>
|
||||
#include <algobase.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ByteOrder.h>
|
||||
|
||||
#include "AudioChannelConverter.h"
|
||||
#include "AudioFormatConverter.h"
|
||||
#include "AudioResampler.h"
|
||||
|
||||
using std::nothrow;
|
||||
|
||||
#define TRACE_AUDIO_ADAPTER
|
||||
#ifdef TRACE_AUDIO_ADAPTER
|
||||
# define TRACE(x...) printf(x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
#endif
|
||||
|
||||
|
||||
AudioAdapter::AudioAdapter(AudioReader* source, const media_format& format)
|
||||
: AudioReader(format),
|
||||
fSource(source),
|
||||
fFinalConverter(NULL),
|
||||
fFormatConverter(NULL),
|
||||
fChannelConverter(NULL),
|
||||
fResampler(NULL)
|
||||
{
|
||||
uint32 hostByteOrder
|
||||
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
|
||||
fFormat.u.raw_audio.byte_order = hostByteOrder;
|
||||
if (source && source->Format().type == B_MEDIA_RAW_AUDIO) {
|
||||
|
||||
if (fFormat.u.raw_audio.format != source->Format().u.raw_audio.format
|
||||
|| source->Format().u.raw_audio.byte_order != hostByteOrder) {
|
||||
TRACE("AudioAdapter() - using format converter\n");
|
||||
fFormatConverter = new (nothrow) AudioFormatConverter(source,
|
||||
fFormat.u.raw_audio.format, hostByteOrder);
|
||||
source = fFormatConverter;
|
||||
}
|
||||
|
||||
if (fFormat.u.raw_audio.frame_rate
|
||||
!= source->Format().u.raw_audio.frame_rate) {
|
||||
TRACE("AudioAdapter() - using resampler\n");
|
||||
fResampler = new (nothrow) AudioResampler(source,
|
||||
fFormat.u.raw_audio.frame_rate);
|
||||
source = fResampler;
|
||||
}
|
||||
|
||||
if (fFormat.u.raw_audio.channel_count
|
||||
!= source->Format().u.raw_audio.channel_count) {
|
||||
TRACE("AudioAdapter() - using channel converter\n");
|
||||
fChannelConverter = new (nothrow) AudioChannelConverter(source,
|
||||
fFormat);
|
||||
source = fChannelConverter;
|
||||
}
|
||||
|
||||
fFinalConverter = source;
|
||||
} else
|
||||
fSource = NULL;
|
||||
}
|
||||
|
||||
|
||||
AudioAdapter::~AudioAdapter()
|
||||
{
|
||||
delete fFormatConverter;
|
||||
delete fChannelConverter;
|
||||
delete fResampler;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioAdapter::Read(void* buffer, int64 pos, int64 frames)
|
||||
{
|
||||
// TRACE("AudioAdapter::Read(%p, %Ld, %Ld)\n", buffer, pos, frames);
|
||||
status_t error = InitCheck();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
pos += fOutOffset;
|
||||
status_t ret = fFinalConverter->Read(buffer, pos, frames);
|
||||
// TRACE("AudioAdapter::Read() done: %s\n", strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioAdapter::InitCheck() const
|
||||
{
|
||||
status_t error = AudioReader::InitCheck();
|
||||
if (error == B_OK && !fFinalConverter)
|
||||
error = B_NO_INIT;
|
||||
if (error == B_OK && fFinalConverter)
|
||||
error = fFinalConverter->InitCheck();
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
AudioReader*
|
||||
AudioAdapter::Source() const
|
||||
{
|
||||
return fSource;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
|
||||
/*! This AudioReader slaves an AudioConverter and an AudioResampler
|
||||
to convert the source data to a given format.
|
||||
At this time the number of channels cannot be changed and the output
|
||||
format byte order is set to the one of the host.
|
||||
If input and output format are the same, the overhead is quit small.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_ADAPTER_H
|
||||
#define AUDIO_ADAPTER_H
|
||||
|
||||
#include "AudioReader.h"
|
||||
|
||||
class AudioChannelConverter;
|
||||
class AudioFormatConverter;
|
||||
class AudioResampler;
|
||||
|
||||
class AudioAdapter : public AudioReader {
|
||||
public:
|
||||
AudioAdapter(AudioReader* source,
|
||||
const media_format& format);
|
||||
virtual ~AudioAdapter();
|
||||
|
||||
virtual status_t Read(void* buffer, int64 pos, int64 frames);
|
||||
|
||||
virtual status_t InitCheck() const;
|
||||
|
||||
AudioReader* Source() const;
|
||||
|
||||
protected:
|
||||
void _ConvertChannels(void* buffer,
|
||||
int64 frames) const;
|
||||
|
||||
AudioReader* fSource;
|
||||
AudioReader* fFinalConverter;
|
||||
AudioFormatConverter* fFormatConverter;
|
||||
AudioChannelConverter* fChannelConverter;
|
||||
AudioResampler* fResampler;
|
||||
};
|
||||
|
||||
#endif // AUDIO_ADAPTER_H
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright © 2008 Stephan Aßmus <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
#include "AudioChannelConverter.h"
|
||||
|
||||
#include <new>
|
||||
#include <algobase.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
using std::nothrow;
|
||||
|
||||
//#define TRACE_AUDIO_CONVERTER
|
||||
#ifdef TRACE_AUDIO_CONVERTER
|
||||
# define TRACE(x...) printf(x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
#endif
|
||||
|
||||
|
||||
AudioChannelConverter::AudioChannelConverter(AudioReader* source,
|
||||
const media_format& format)
|
||||
: AudioReader(format),
|
||||
fSource(source)
|
||||
{
|
||||
// TODO: check the format and make sure everything matches
|
||||
// except for channel count
|
||||
}
|
||||
|
||||
|
||||
AudioChannelConverter::~AudioChannelConverter()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<typename Type, typename BigType>
|
||||
static void
|
||||
convert(Type* inBuffer, Type* outBuffer, int32 inChannels, int32 outChannels,
|
||||
int32 frames)
|
||||
{
|
||||
// TODO: more conversions!
|
||||
switch (inChannels) {
|
||||
case 1:
|
||||
switch (outChannels) {
|
||||
case 2:
|
||||
for (int32 i = 0; i < frames; i++) {
|
||||
*outBuffer++ = *inBuffer;
|
||||
*outBuffer++ = *inBuffer++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (outChannels) {
|
||||
case 1:
|
||||
for (int32 i = 0; i < frames; i++) {
|
||||
*outBuffer++
|
||||
= (Type)((BigType)inBuffer[0] + inBuffer[1]) / 2;
|
||||
inBuffer += 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioChannelConverter::Read(void* outBuffer, int64 pos, int64 frames)
|
||||
{
|
||||
TRACE("AudioChannelConverter::Read(%p, %Ld, %Ld)\n", outBuffer, pos, frames);
|
||||
status_t error = InitCheck();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
pos += fOutOffset;
|
||||
|
||||
int32 inChannels = fSource->Format().u.raw_audio.channel_count;
|
||||
int32 outChannels = fFormat.u.raw_audio.channel_count;
|
||||
TRACE(" convert %ld -> %ld channels\n", inChannels, outChannels);
|
||||
|
||||
int32 inSampleSize = fSource->Format().u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
int32 inFrameSize = inSampleSize * inChannels;
|
||||
uint8* inBuffer = new (nothrow) uint8[inFrameSize * frames];
|
||||
|
||||
TRACE(" fSource->Read()\n");
|
||||
status_t ret = fSource->Read(inBuffer, pos, frames);
|
||||
if (ret != B_OK) {
|
||||
delete[] inBuffer;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// We know that both formats are the same except for channel count
|
||||
switch (fFormat.u.raw_audio.format) {
|
||||
case media_raw_audio_format::B_AUDIO_FLOAT:
|
||||
convert<float, float>((float*)inBuffer, (float*)outBuffer,
|
||||
inChannels, outChannels, frames);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_INT:
|
||||
convert<int32, int64>((int32*)inBuffer, (int32*)outBuffer,
|
||||
inChannels, outChannels, frames);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_SHORT:
|
||||
convert<int16, int32>((int16*)inBuffer, (int16*)outBuffer,
|
||||
inChannels, outChannels, frames);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_UCHAR:
|
||||
convert<uint8, uint16>((uint8*)inBuffer, (uint8*)outBuffer,
|
||||
inChannels, outChannels, frames);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_CHAR:
|
||||
convert<int8, int16>((int8*)inBuffer, (int8*)outBuffer,
|
||||
inChannels, outChannels, frames);
|
||||
break;
|
||||
}
|
||||
|
||||
delete[] inBuffer;
|
||||
|
||||
TRACE("AudioChannelConverter::Read() done: %s\n", strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioChannelConverter::InitCheck() const
|
||||
{
|
||||
status_t error = AudioReader::InitCheck();
|
||||
if (error == B_OK && !fSource)
|
||||
error = B_NO_INIT;
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
AudioReader*
|
||||
AudioChannelConverter::Source() const
|
||||
{
|
||||
return fSource;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright © 2008 Stephan Aßmus <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
|
||||
/*! This AudioReader just converts the source channel count
|
||||
into another one, e.g. 1 -> 2. Frame rate and sample format
|
||||
remain unchanged.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_CHANNEL_CONVERTER_H
|
||||
#define AUDIO_CHANNEL_CONVERTER_H
|
||||
|
||||
#include "AudioReader.h"
|
||||
|
||||
class AudioChannelConverter : public AudioReader {
|
||||
public:
|
||||
AudioChannelConverter(AudioReader* source,
|
||||
const media_format& format);
|
||||
virtual ~AudioChannelConverter();
|
||||
|
||||
virtual status_t Read(void* buffer, int64 pos, int64 frames);
|
||||
|
||||
virtual status_t InitCheck() const;
|
||||
|
||||
AudioReader* Source() const;
|
||||
|
||||
protected:
|
||||
AudioReader* fSource;
|
||||
};
|
||||
|
||||
#endif // AUDIO_CHANNEL_CONVERTER_H
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
|
||||
* Copyright © 2008 Stephan Aßmus <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
|
||||
#include "AudioFormatConverter.h"
|
||||
|
||||
#include <algobase.h>
|
||||
|
||||
#include <ByteOrder.h>
|
||||
#include <MediaDefs.h>
|
||||
|
||||
|
||||
//#define TRACE_AUDIO_CONVERTER
|
||||
#ifdef TRACE_AUDIO_CONVERTER
|
||||
# define TRACE(x...) printf(x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
#endif
|
||||
|
||||
|
||||
AudioFormatConverter::AudioFormatConverter(AudioReader* source, uint32 format,
|
||||
uint32 byte_order)
|
||||
: AudioReader(),
|
||||
fSource(NULL)
|
||||
{
|
||||
uint32 hostByteOrder
|
||||
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
|
||||
if (source && source->Format().type == B_MEDIA_RAW_AUDIO
|
||||
&& source->Format().u.raw_audio.byte_order == hostByteOrder) {
|
||||
fFormat = source->Format();
|
||||
fFormat.u.raw_audio.format = format;
|
||||
fFormat.u.raw_audio.byte_order = byte_order;
|
||||
int32 inSampleSize = source->Format().u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
int32 outSampleSize = fFormat.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
if (inSampleSize != outSampleSize) {
|
||||
fFormat.u.raw_audio.buffer_size
|
||||
= source->Format().u.raw_audio.buffer_size * outSampleSize
|
||||
/ inSampleSize;
|
||||
}
|
||||
} else
|
||||
source = NULL;
|
||||
fSource = source;
|
||||
}
|
||||
|
||||
|
||||
AudioFormatConverter::~AudioFormatConverter()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
struct ReadFloat {
|
||||
inline int operator()(const void* buffer) const {
|
||||
// 0 == mid, -1.0 == bottom, 1.0 == top
|
||||
float b = *(float*)buffer;
|
||||
if (b < -1.0f)
|
||||
b = -1.0f;
|
||||
else if (b > 1.0f)
|
||||
b = 1.0f;
|
||||
return (int)((double)b * (double)0x7fffffff);
|
||||
}
|
||||
};
|
||||
|
||||
struct ReadInt {
|
||||
inline int operator()(const void* buffer) const {
|
||||
// 0 == mid, 0x80000001 == bottom, 0x7fffffff == top
|
||||
short b = *(int*)buffer;
|
||||
if (b == 0x80000000)
|
||||
b++;
|
||||
return b;
|
||||
}
|
||||
};
|
||||
|
||||
struct ReadShort {
|
||||
inline int operator()(const void* buffer) const {
|
||||
// 0 == mid, -32767 == bottom, +32767
|
||||
short b = *(short*)buffer;
|
||||
if (b == -32768)
|
||||
b++;
|
||||
return int(int64(b) * 0x7fffffff / 32767);
|
||||
}
|
||||
};
|
||||
|
||||
struct ReadUChar {
|
||||
inline int operator()(const void* buffer) const {
|
||||
// 128 == mid, 1 == bottom, 255 == top
|
||||
uchar b = *(uchar*)buffer;
|
||||
if (b == 0)
|
||||
b++;
|
||||
return int((int64(b) - 0x80) * 0x7fffffff / 127);
|
||||
}
|
||||
};
|
||||
|
||||
struct ReadChar {
|
||||
inline int operator()(const void* buffer) const {
|
||||
// 0 == mid, -127 == bottom, +127 == top
|
||||
char b = *(char*)buffer;
|
||||
if (b == 0)
|
||||
b++;
|
||||
return int(int64(b) * 0x7fffffff / 127);
|
||||
}
|
||||
};
|
||||
|
||||
struct WriteFloat {
|
||||
inline void operator()(void* buffer, int value) const {
|
||||
*(float*)buffer = (double)value / (double)0x7fffffff;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct WriteInt {
|
||||
inline void operator()(void* buffer, int value) const {
|
||||
*(int*)buffer = value;
|
||||
}
|
||||
};
|
||||
|
||||
struct WriteShort {
|
||||
inline void operator()(void* buffer, int value) const {
|
||||
*(short*)buffer = (short)(value / (int)0x10000);
|
||||
}
|
||||
};
|
||||
|
||||
struct WriteUChar {
|
||||
inline void operator()(void* buffer, int value) const {
|
||||
*(uchar*)buffer = (uchar)(value / (int)0x1000000 + 128);
|
||||
}
|
||||
};
|
||||
|
||||
struct WriteChar {
|
||||
inline void operator()(void* buffer, int value) const {
|
||||
*(char*)buffer = (char)(value / (int)0x1000000);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename ReadT, typename WriteT>
|
||||
static void
|
||||
convert(const ReadT& read, const WriteT& write,
|
||||
const char* inBuffer, char* outBuffer, int32 frames,
|
||||
int32 inSampleSize, int32 outSampleSize, int32 channelCount)
|
||||
{
|
||||
for (int32 i = 0; i < frames; i++) {
|
||||
for (int32 c = 0; c < channelCount; c++) {
|
||||
write(outBuffer, read(inBuffer));
|
||||
inBuffer += inSampleSize;
|
||||
outBuffer += outSampleSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void
|
||||
swap_sample_byte_order(void* buffer, uint32 format, size_t length)
|
||||
{
|
||||
type_code type = B_ANY_TYPE;
|
||||
switch (format) {
|
||||
case media_raw_audio_format::B_AUDIO_FLOAT:
|
||||
type = B_FLOAT_TYPE;
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_INT:
|
||||
type = B_INT32_TYPE;
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_SHORT:
|
||||
type = B_INT16_TYPE;
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_UCHAR:
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_CHAR:
|
||||
break;
|
||||
}
|
||||
if (type != B_ANY_TYPE)
|
||||
swap_data(type, buffer, length, B_SWAP_ALWAYS);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioFormatConverter::Read(void* buffer, int64 pos, int64 frames)
|
||||
{
|
||||
TRACE("AudioFormatConverter::Read(%p, %Ld, %Ld)\n", buffer, pos, frames);
|
||||
status_t error = InitCheck();
|
||||
if (error != B_OK) {
|
||||
TRACE("AudioFormatConverter::Read() done 1\n");
|
||||
return error;
|
||||
}
|
||||
pos += fOutOffset;
|
||||
|
||||
if (fFormat.u.raw_audio.format == fSource->Format().u.raw_audio.format
|
||||
&& fFormat.u.raw_audio.byte_order
|
||||
== fSource->Format().u.raw_audio.byte_order) {
|
||||
TRACE("AudioFormatConverter::Read() done 2\n");
|
||||
return fSource->Read(buffer, pos, frames);
|
||||
}
|
||||
|
||||
|
||||
int32 inSampleSize = fSource->Format().u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
int32 outSampleSize = fFormat.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
int32 channelCount = fFormat.u.raw_audio.channel_count;
|
||||
int32 inFrameSize = inSampleSize * channelCount;
|
||||
int32 outFrameSize = outSampleSize * channelCount;
|
||||
char* reformatBuffer = NULL;
|
||||
char* inBuffer = (char*)buffer;
|
||||
|
||||
#ifdef TRACE_AUDIO_CONVERTER
|
||||
char formatString[256];
|
||||
string_for_format(fSource->Format(), formatString, 256);
|
||||
TRACE(" source format: %s\n", formatString);
|
||||
TRACE(" in format : format: %lx, sample size: %ld, channels: %ld, "
|
||||
"byte order: %lu\n", fSource->Format().u.raw_audio.format,
|
||||
inSampleSize, channelCount,
|
||||
fSource->Format().u.raw_audio.byte_order);
|
||||
TRACE(" out format: format: %lx, sample size: %ld, channels: %ld, "
|
||||
"byte order: %lu\n", fFormat.u.raw_audio.format, outSampleSize,
|
||||
channelCount, fFormat.u.raw_audio.byte_order);
|
||||
#endif // TRACE_AUDIO_CONVERTER
|
||||
|
||||
if (inSampleSize != outSampleSize) {
|
||||
reformatBuffer = new char[frames * inFrameSize];
|
||||
inBuffer = reformatBuffer;
|
||||
}
|
||||
error = fSource->Read(inBuffer, pos, frames);
|
||||
// convert samples to host endianess
|
||||
uint32 hostByteOrder
|
||||
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
|
||||
if (fSource->Format().u.raw_audio.byte_order != hostByteOrder) {
|
||||
swap_sample_byte_order(inBuffer, fSource->Format().u.raw_audio.format,
|
||||
frames * inFrameSize);
|
||||
}
|
||||
// convert the sample type
|
||||
switch (fSource->Format().u.raw_audio.format) {
|
||||
// float
|
||||
case media_raw_audio_format::B_AUDIO_FLOAT:
|
||||
switch (fFormat.u.raw_audio.format) {
|
||||
case media_raw_audio_format::B_AUDIO_FLOAT:
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_INT:
|
||||
convert(ReadFloat(), WriteInt(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_SHORT:
|
||||
convert(ReadFloat(), WriteShort(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_UCHAR:
|
||||
convert(ReadFloat(), WriteUChar(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_CHAR:
|
||||
convert(ReadFloat(), WriteChar(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// int
|
||||
case media_raw_audio_format::B_AUDIO_INT:
|
||||
switch (fFormat.u.raw_audio.format) {
|
||||
case media_raw_audio_format::B_AUDIO_FLOAT:
|
||||
convert(ReadInt(), WriteFloat(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_INT:
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_SHORT:
|
||||
convert(ReadInt(), WriteShort(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_UCHAR:
|
||||
convert(ReadInt(), WriteUChar(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_CHAR:
|
||||
convert(ReadInt(), WriteChar(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// short
|
||||
case media_raw_audio_format::B_AUDIO_SHORT:
|
||||
switch (fFormat.u.raw_audio.format) {
|
||||
case media_raw_audio_format::B_AUDIO_FLOAT:
|
||||
convert(ReadShort(), WriteFloat(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_INT:
|
||||
convert(ReadShort(), WriteInt(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_SHORT:
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_UCHAR:
|
||||
convert(ReadShort(), WriteUChar(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_CHAR:
|
||||
convert(ReadShort(), WriteChar(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// uchar
|
||||
case media_raw_audio_format::B_AUDIO_UCHAR:
|
||||
switch (fFormat.u.raw_audio.format) {
|
||||
case media_raw_audio_format::B_AUDIO_FLOAT:
|
||||
convert(ReadUChar(), WriteFloat(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_INT:
|
||||
convert(ReadUChar(), WriteInt(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_SHORT:
|
||||
convert(ReadUChar(), WriteShort(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_UCHAR:
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_CHAR:
|
||||
convert(ReadUChar(), WriteChar(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// char
|
||||
case media_raw_audio_format::B_AUDIO_CHAR:
|
||||
switch (fFormat.u.raw_audio.format) {
|
||||
case media_raw_audio_format::B_AUDIO_FLOAT:
|
||||
convert(ReadChar(), WriteFloat(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_INT:
|
||||
convert(ReadChar(), WriteInt(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_SHORT:
|
||||
convert(ReadChar(), WriteShort(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_UCHAR:
|
||||
convert(ReadChar(), WriteUChar(), inBuffer, (char*)buffer,
|
||||
frames, inSampleSize, outSampleSize, channelCount);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_CHAR:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// convert samples to output endianess
|
||||
if (fFormat.u.raw_audio.byte_order != hostByteOrder) {
|
||||
swap_sample_byte_order(buffer, fFormat.u.raw_audio.format,
|
||||
frames * outFrameSize);
|
||||
}
|
||||
|
||||
delete[] reformatBuffer;
|
||||
TRACE("AudioFormatConverter::Read() done\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioFormatConverter::InitCheck() const
|
||||
{
|
||||
status_t error = AudioReader::InitCheck();
|
||||
if (error == B_OK && !fSource)
|
||||
error = B_NO_INIT;
|
||||
if (error == B_OK)
|
||||
error = fSource->InitCheck();
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
AudioReader*
|
||||
AudioFormatConverter::Source() const
|
||||
{
|
||||
return fSource;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
|
||||
* Copyright © 2008 Stephan Aßmus <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
|
||||
/*! This AudioReader just converts the source sample format (and byte order)
|
||||
into another one, e.g. LE short -> BE float. Frame rate and channel
|
||||
count remain unchanged.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_FORMAT_CONVERTER_H
|
||||
#define AUDIO_FORMAT_CONVERTER_H
|
||||
|
||||
#include "AudioReader.h"
|
||||
|
||||
class AudioFormatConverter : public AudioReader {
|
||||
public:
|
||||
AudioFormatConverter(AudioReader* source,
|
||||
uint32 format, uint32 byte_order);
|
||||
virtual ~AudioFormatConverter();
|
||||
|
||||
virtual status_t Read(void* buffer, int64 pos, int64 frames);
|
||||
|
||||
virtual status_t InitCheck() const;
|
||||
|
||||
AudioReader* Source() const;
|
||||
|
||||
protected:
|
||||
AudioReader* fSource;
|
||||
};
|
||||
|
||||
#endif // AUDIO_FORMAT_CONVERTER_H
|
||||
@@ -0,0 +1,742 @@
|
||||
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
|
||||
* Distributed under the terms of the Be Sample Code license.
|
||||
*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include "AudioProducer.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <BufferGroup.h>
|
||||
#include <Buffer.h>
|
||||
#include <MediaDefs.h>
|
||||
#include <ParameterWeb.h>
|
||||
#include <TimeSource.h>
|
||||
|
||||
#include "AudioSupplier.h"
|
||||
|
||||
#define DEBUG_TO_FILE 0
|
||||
|
||||
#if DEBUG_TO_FILE
|
||||
# include <Entry.h>
|
||||
# include <MediaFormats.h>
|
||||
# include <MediaFile.h>
|
||||
# include <MediaTrack.h>
|
||||
#endif // DEBUG_TO_FILE
|
||||
|
||||
|
||||
// debugging
|
||||
//#define TRACE_AUDIO_PRODUCER
|
||||
#ifdef TRACE_AUDIO_PRODUCER
|
||||
# define TRACE(x...) printf(x)
|
||||
# define ERROR(x...) fprintf(stderr, x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
# define ERROR(x...) fprintf(stderr, x)
|
||||
#endif
|
||||
|
||||
|
||||
#if DEBUG_TO_FILE
|
||||
static BMediaFile*
|
||||
init_media_file(media_format format, BMediaTrack** _track)
|
||||
{
|
||||
static BMediaFile* file = NULL;
|
||||
static BMediaTrack* track = NULL;
|
||||
if (file == NULL) {
|
||||
entry_ref ref;
|
||||
get_ref_for_path("/boot/home/Desktop/test.wav", &ref);
|
||||
|
||||
media_file_format fileFormat;
|
||||
int32 cookie = 0;
|
||||
while (get_next_file_format(&cookie, &fileFormat) == B_OK) {
|
||||
if (strcmp(fileFormat.short_name, "wav") == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
file = new BMediaFile(&ref, &fileFormat);
|
||||
|
||||
media_codec_info info;
|
||||
cookie = 0;
|
||||
while (get_next_encoder(&cookie, &info) == B_OK) {
|
||||
if (strcmp(info.short_name, "raw-audio") == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
track = file->CreateTrack(&format, &info);
|
||||
if (!track)
|
||||
printf("failed to create track\n");
|
||||
|
||||
file->CommitHeader();
|
||||
}
|
||||
*_track = track;
|
||||
return track != NULL ? file : NULL;
|
||||
}
|
||||
#endif // DEBUG_TO_FILE
|
||||
|
||||
|
||||
|
||||
// constructor
|
||||
AudioProducer::AudioProducer(const char* name, AudioSupplier* supplier,
|
||||
bool lowLatency)
|
||||
: BMediaNode(name),
|
||||
BBufferProducer(B_MEDIA_RAW_AUDIO),
|
||||
BMediaEventLooper(),
|
||||
|
||||
fBufferGroup(NULL),
|
||||
fLatency(0),
|
||||
fInternalLatency(0),
|
||||
fLowLatency(lowLatency),
|
||||
fOutputEnabled(true),
|
||||
fFramesSent(0),
|
||||
fStartTime(0),
|
||||
fSupplier(supplier),
|
||||
fRunning(false)
|
||||
{
|
||||
TRACE("%p->AudioProducer::AudioProducer(%s, %p, %d)\n", this, name,
|
||||
supplier, lowLatency);
|
||||
|
||||
// initialize our preferred format object
|
||||
fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
|
||||
fPreferredFormat.u.raw_audio.format
|
||||
= media_raw_audio_format::B_AUDIO_FLOAT;
|
||||
// = media_raw_audio_format::B_AUDIO_SHORT;
|
||||
fPreferredFormat.u.raw_audio.channel_count = 2;
|
||||
fPreferredFormat.u.raw_audio.frame_rate = 44100.0;
|
||||
fPreferredFormat.u.raw_audio.byte_order
|
||||
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
|
||||
|
||||
// NOTE: the (buffer_size * 1000000) needs to be dividable by
|
||||
// fPreferredFormat.u.raw_audio.frame_rate!
|
||||
fPreferredFormat.u.raw_audio.buffer_size = 441 * 2
|
||||
* (fPreferredFormat.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK);
|
||||
|
||||
if (!fLowLatency)
|
||||
fPreferredFormat.u.raw_audio.buffer_size *= 6;
|
||||
|
||||
// we're not connected yet
|
||||
fOutput.destination = media_destination::null;
|
||||
fOutput.format = fPreferredFormat;
|
||||
// init the audio supplier
|
||||
if (fSupplier) {
|
||||
fSupplier->SetAudioProducer(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AudioProducer::~AudioProducer()
|
||||
{
|
||||
TRACE("%p->AudioProducer::~AudioProducer()\n", this);
|
||||
|
||||
#if DEBUG_TO_FILE
|
||||
BMediaTrack* track;
|
||||
if (BMediaFile* file = init_media_file(fPreferredFormat, &track)) {
|
||||
printf("deleting file...\n");
|
||||
track->Flush();
|
||||
file->ReleaseTrack(track);
|
||||
file->CloseFile();
|
||||
delete file;
|
||||
}
|
||||
#endif // DEBUG_TO_FILE
|
||||
|
||||
// Stop the BMediaEventLooper thread
|
||||
Quit();
|
||||
TRACE("AudioProducer::~AudioProducer() done\n");
|
||||
}
|
||||
|
||||
|
||||
BMediaAddOn*
|
||||
AudioProducer::AddOn(int32* internalId) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioProducer::FormatSuggestionRequested(media_type type, int32 quality,
|
||||
media_format* _format)
|
||||
{
|
||||
TRACE("%p->AudioProducer::FormatSuggestionRequested()\n", this);
|
||||
|
||||
if (!_format)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// this is the format we'll be returning (our preferred format)
|
||||
*_format = fPreferredFormat;
|
||||
|
||||
// a wildcard type is okay; we can specialize it
|
||||
if (type == B_MEDIA_UNKNOWN_TYPE)
|
||||
type = B_MEDIA_RAW_AUDIO;
|
||||
|
||||
// we only support raw audio
|
||||
if (type != B_MEDIA_RAW_AUDIO)
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
AudioProducer::FormatProposal(const media_source& output, media_format* format)
|
||||
{
|
||||
TRACE("%p->AudioProducer::FormatProposal()\n", this);
|
||||
|
||||
// is this a proposal for our one output?
|
||||
if (output != fOutput.source) {
|
||||
TRACE(" -> B_MEDIA_BAD_SOURCE\n");
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
}
|
||||
|
||||
// TODO: we might want to support different audio formats
|
||||
// we only support floating-point raw audio, so we always return that, but
|
||||
// we supply an error code depending on whether we found the proposal
|
||||
// acceptable.
|
||||
media_type requestedType = format->type;
|
||||
*format = fPreferredFormat;
|
||||
|
||||
// raw audio or wildcard type, either is okay by us
|
||||
if (requestedType != B_MEDIA_UNKNOWN_TYPE
|
||||
&& requestedType != B_MEDIA_RAW_AUDIO) {
|
||||
TRACE(" -> B_MEDIA_BAD_FORMAT\n");
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioProducer::FormatChangeRequested(const media_source& source,
|
||||
const media_destination& destination, media_format* ioFormat,
|
||||
int32* _deprecated_)
|
||||
{
|
||||
TRACE("%p->AudioProducer::FormatChangeRequested()\n", this);
|
||||
|
||||
if (destination != fOutput.destination) {
|
||||
TRACE(" -> B_MEDIA_BAD_DESTINATION\n");
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
}
|
||||
|
||||
if (source != fOutput.source) {
|
||||
TRACE(" -> B_MEDIA_BAD_SOURCE\n");
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
}
|
||||
|
||||
fOutput.format = *ioFormat;
|
||||
|
||||
// notify our audio supplier of the format change
|
||||
if (fSupplier)
|
||||
fSupplier->SetFormat(fOutput.format);
|
||||
|
||||
return _AllocateBuffers(ioFormat);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioProducer::GetNextOutput(int32* cookie, media_output* _output)
|
||||
{
|
||||
TRACE("%p->AudioProducer::GetNextOutput(%ld)\n", this, *cookie);
|
||||
|
||||
// we have only a single output; if we supported multiple outputs, we'd
|
||||
// iterate over whatever data structure we were using to keep track of
|
||||
// them.
|
||||
if (0 == *cookie) {
|
||||
*_output = fOutput;
|
||||
*cookie += 1;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_BAD_INDEX;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioProducer::DisposeOutputCookie(int32 cookie)
|
||||
{
|
||||
// do nothing because we don't use the cookie for anything special
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioProducer::SetBufferGroup(const media_source& forSource,
|
||||
BBufferGroup* newGroup)
|
||||
{
|
||||
TRACE("%p->AudioProducer::SetBufferGroup()\n", this);
|
||||
|
||||
if (forSource != fOutput.source)
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
|
||||
if (newGroup == fBufferGroup)
|
||||
return B_OK;
|
||||
|
||||
if (fUsingOurBuffers && fBufferGroup)
|
||||
delete fBufferGroup; // waits for all buffers to recycle
|
||||
|
||||
if (newGroup != NULL) {
|
||||
// we were given a valid group; just use that one from now on
|
||||
fBufferGroup = newGroup;
|
||||
fUsingOurBuffers = false;
|
||||
} else {
|
||||
// we were passed a NULL group pointer; that means we construct
|
||||
// our own buffer group to use from now on
|
||||
size_t size = fOutput.format.u.raw_audio.buffer_size;
|
||||
int32 count = int32(fLatency / BufferDuration() + 1 + 1);
|
||||
fBufferGroup = new BBufferGroup(size, count);
|
||||
fUsingOurBuffers = true;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioProducer::GetLatency(bigtime_t* _latency)
|
||||
{
|
||||
TRACE("%p->AudioProducer::GetLatency()\n", this);
|
||||
|
||||
// report our *total* latency: internal plus downstream plus scheduling
|
||||
*_latency = EventLatency() + SchedulingLatency();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioProducer::PrepareToConnect(const media_source& what,
|
||||
const media_destination& where, media_format* format,
|
||||
media_source* _source, char* _name)
|
||||
{
|
||||
TRACE("%p->AudioProducer::PrepareToConnect()\n", this);
|
||||
|
||||
// trying to connect something that isn't our source?
|
||||
if (what != fOutput.source) {
|
||||
TRACE(" -> B_MEDIA_BAD_SOURCE\n");
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
}
|
||||
|
||||
// are we already connected?
|
||||
if (fOutput.destination != media_destination::null) {
|
||||
TRACE(" -> B_MEDIA_ALREADY_CONNECTED\n");
|
||||
return B_MEDIA_ALREADY_CONNECTED;
|
||||
}
|
||||
|
||||
// the format may not yet be fully specialized (the consumer might have
|
||||
// passed back some wildcards). Finish specializing it now, and return an
|
||||
// error if we don't support the requested format.
|
||||
if (format->type != B_MEDIA_RAW_AUDIO) {
|
||||
TRACE(" -> B_MEDIA_BAD_FORMAT\n");
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
// TODO: we might want to support different audio formats
|
||||
} else if (format->u.raw_audio.format
|
||||
!= fPreferredFormat.u.raw_audio.format) {
|
||||
TRACE(" -> B_MEDIA_BAD_FORMAT\n");
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// !!! validate all other fields except for buffer_size here, because the
|
||||
// consumer might have supplied different values from AcceptFormat()?
|
||||
|
||||
// check the buffer size, which may still be wildcarded
|
||||
if (format->u.raw_audio.buffer_size
|
||||
== media_raw_audio_format::wildcard.buffer_size) {
|
||||
// pick something comfortable to suggest
|
||||
// NOTE: the (buffer_size * 1000000) needs to be dividable by
|
||||
// fPreferredFormat.u.raw_audio.frame_rate!
|
||||
// TODO: this needs to depend on the other parameters
|
||||
// (but it doesn't matter sincer the AudioProducer is not
|
||||
// currently used like that)
|
||||
TRACE(" -> adjusting buffer size, it was wildcard\n");
|
||||
format->u.raw_audio.buffer_size = 441 * 2 * sizeof(float);
|
||||
}
|
||||
|
||||
// Now reserve the connection, and return information about it
|
||||
fOutput.destination = where;
|
||||
fOutput.format = *format;
|
||||
*_source = fOutput.source;
|
||||
strncpy(_name, fOutput.name, B_MEDIA_NAME_LENGTH);
|
||||
TRACE(" -> B_OK\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static bigtime_t
|
||||
estimate_internal_latency(const media_format& format)
|
||||
{
|
||||
bigtime_t startTime = system_time();
|
||||
// calculate the number of samples per buffer
|
||||
int32 sampleSize = format.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
int32 sampleCount = format.u.raw_audio.buffer_size / sampleSize;
|
||||
// alloc float buffers of this size
|
||||
const int bufferCount = 10; // number of input buffers
|
||||
float* buffers[bufferCount + 1];
|
||||
for (int32 i = 0; i < bufferCount + 1; i++)
|
||||
buffers[i] = new float[sampleCount];
|
||||
float* outBuffer = buffers[bufferCount];
|
||||
// fill all buffers save the last one with arbitrary data and merge them
|
||||
// into the last one
|
||||
for (int32 i = 0; i < bufferCount; i++) {
|
||||
for (int32 k = 0; k < sampleCount; k++) {
|
||||
buffers[i][k] = ((float)i * (float)k)
|
||||
/ float(bufferCount * sampleCount);
|
||||
}
|
||||
}
|
||||
for (int32 k = 0; k < sampleCount; k++) {
|
||||
outBuffer[k] = 0;
|
||||
for (int32 i = 0; i < bufferCount; i++)
|
||||
outBuffer[k] += buffers[i][k];
|
||||
outBuffer[k] /= bufferCount;
|
||||
}
|
||||
// cleanup
|
||||
for (int32 i = 0; i < bufferCount + 1; i++)
|
||||
delete[] buffers[i];
|
||||
return system_time() - startTime;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioProducer::Connect(status_t error, const media_source& source,
|
||||
const media_destination& destination, const media_format& format,
|
||||
char* _name)
|
||||
{
|
||||
TRACE("AudioProducer::Connect(%s)\n", strerror(error));
|
||||
|
||||
// If something earlier failed, Connect() might still be called, but with
|
||||
// a non-zero error code. When that happens we simply unreserve the
|
||||
// connection and do nothing else.
|
||||
if (error != B_OK) {
|
||||
fOutput.destination = media_destination::null;
|
||||
fOutput.format = fPreferredFormat;
|
||||
return;
|
||||
}
|
||||
|
||||
// Okay, the connection has been confirmed. Record the destination and
|
||||
// format that we agreed on, and report our connection name again.
|
||||
fOutput.destination = destination;
|
||||
fOutput.format = format;
|
||||
strncpy(_name, fOutput.name, B_MEDIA_NAME_LENGTH);
|
||||
|
||||
// tell our audio supplier about the format
|
||||
if (fSupplier) {
|
||||
TRACE("AudioProducer::Connect() fSupplier->SetFormat()\n");
|
||||
fSupplier->SetFormat(fOutput.format);
|
||||
}
|
||||
|
||||
TRACE("AudioProducer::Connect() FindLatencyFor()\n");
|
||||
|
||||
// Now that we're connected, we can determine our downstream latency.
|
||||
// Do so, then make sure we get our events early enough.
|
||||
media_node_id id;
|
||||
FindLatencyFor(fOutput.destination, &fLatency, &id);
|
||||
|
||||
// Use a dry run to see how long it takes me to fill a buffer of data
|
||||
size_t sampleSize = fOutput.format.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
size_t samplesPerBuffer
|
||||
= fOutput.format.u.raw_audio.buffer_size / sampleSize;
|
||||
fInternalLatency = estimate_internal_latency(fOutput.format);
|
||||
if (!fLowLatency)
|
||||
fInternalLatency *= 32;
|
||||
SetEventLatency(fLatency + fInternalLatency);
|
||||
|
||||
// reset our buffer duration, etc. to avoid later calculations
|
||||
bigtime_t duration = bigtime_t(1000000)
|
||||
* samplesPerBuffer / bigtime_t(fOutput.format.u.raw_audio.frame_rate
|
||||
* fOutput.format.u.raw_audio.channel_count);
|
||||
TRACE("AudioProducer::Connect() SetBufferDuration(%lld)\n", duration);
|
||||
SetBufferDuration(duration);
|
||||
|
||||
TRACE("AudioProducer::Connect() _AllocateBuffers()\n");
|
||||
|
||||
// Set up the buffer group for our connection, as long as nobody handed
|
||||
// us a buffer group (via SetBufferGroup()) prior to this. That can
|
||||
// happen, for example, if the consumer calls SetOutputBuffersFor() on
|
||||
// us from within its Connected() method.
|
||||
if (!fBufferGroup)
|
||||
_AllocateBuffers(&fOutput.format);
|
||||
|
||||
TRACE("AudioProducer::Connect() done\n");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioProducer::Disconnect(const media_source& what,
|
||||
const media_destination& where)
|
||||
{
|
||||
TRACE("%p->AudioProducer::Disconnect()\n", this);
|
||||
|
||||
// Make sure that our connection is the one being disconnected
|
||||
if ((where == fOutput.destination) && (what == fOutput.source)) {
|
||||
fOutput.destination = media_destination::null;
|
||||
fOutput.format = fPreferredFormat;
|
||||
TRACE("AudioProducer: deleting buffer group...\n");
|
||||
// Always delete the buffer group, even if it is not ours.
|
||||
// (See BeBook::SetBufferGroup()).
|
||||
delete fBufferGroup;
|
||||
TRACE("AudioProducer: buffer group deleted\n");
|
||||
fBufferGroup = NULL;
|
||||
}
|
||||
|
||||
TRACE("%p->AudioProducer::Disconnect() done\n", this);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioProducer::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
|
||||
bigtime_t performanceTime)
|
||||
{
|
||||
ERROR("%p->AudioProducer::LateNoticeReceived(%lld, %lld)\n", this, howMuch,
|
||||
performanceTime);
|
||||
// If we're late, we need to catch up. Respond in a manner appropriate
|
||||
// to our current run mode.
|
||||
if (what == fOutput.source) {
|
||||
if (RunMode() == B_RECORDING) {
|
||||
// ...
|
||||
} else if (RunMode() == B_INCREASE_LATENCY) {
|
||||
fInternalLatency += howMuch;
|
||||
SetEventLatency(fLatency + fInternalLatency);
|
||||
} else {
|
||||
size_t sampleSize
|
||||
= fOutput.format.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
size_t nSamples
|
||||
= fOutput.format.u.raw_audio.buffer_size / sampleSize;
|
||||
fFramesSent += nSamples;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioProducer::EnableOutput(const media_source& what, bool enabled,
|
||||
int32* _deprecated_)
|
||||
{
|
||||
TRACE("%p->AudioProducer::EnableOutput(%d)\n", this, enabled);
|
||||
|
||||
if (what == fOutput.source)
|
||||
fOutputEnabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioProducer::SetPlayRate(int32 numer, int32 denom)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioProducer::HandleMessage(int32 message, const void *data, size_t size)
|
||||
{
|
||||
TRACE("%p->AudioProducer::HandleMessage()\n", this);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioProducer::AdditionalBufferRequested(const media_source& source,
|
||||
media_buffer_id prevBuffer, bigtime_t prevTime,
|
||||
const media_seek_tag *prevTag)
|
||||
{
|
||||
TRACE("%p->AudioProducer::AdditionalBufferRequested()\n", this);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioProducer::LatencyChanged(const media_source& source,
|
||||
const media_destination& destination, bigtime_t newLatency, uint32 flags)
|
||||
{
|
||||
TRACE("%p->AudioProducer::LatencyChanged(%lld)\n", this, newLatency);
|
||||
|
||||
if ((source == fOutput.source) && (destination == fOutput.destination)) {
|
||||
fLatency = newLatency;
|
||||
SetEventLatency(fLatency + fInternalLatency);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioProducer::NodeRegistered()
|
||||
{
|
||||
TRACE("%p->AudioProducer::NodeRegistered()\n", this);
|
||||
|
||||
// Start the BMediaEventLooper thread
|
||||
SetPriority(B_REAL_TIME_PRIORITY);
|
||||
Run();
|
||||
|
||||
// set up as much information about our output as we can
|
||||
fOutput.source.port = ControlPort();
|
||||
fOutput.source.id = 0;
|
||||
fOutput.node = Node();
|
||||
::strcpy(fOutput.name, "MediaPlayer Sound Output");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioProducer::SetRunMode(run_mode mode)
|
||||
{
|
||||
TRACE("%p->AudioProducer::SetRunMode()\n", this);
|
||||
|
||||
if (B_OFFLINE == mode)
|
||||
ReportError(B_NODE_FAILED_SET_RUN_MODE);
|
||||
else
|
||||
BBufferProducer::SetRunMode(mode);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
{
|
||||
TRACE("%p->AudioProducer::HandleEvent()\n", this);
|
||||
|
||||
switch (event->type) {
|
||||
case BTimedEventQueue::B_START:
|
||||
TRACE("AudioProducer::HandleEvent(B_START)\n");
|
||||
if (RunState() != B_STARTED) {
|
||||
fFramesSent = 0;
|
||||
fStartTime = event->event_time;
|
||||
media_timed_event firstBufferEvent(fStartTime,
|
||||
BTimedEventQueue::B_HANDLE_BUFFER);
|
||||
EventQueue()->AddEvent(firstBufferEvent);
|
||||
}
|
||||
TRACE("AudioProducer::HandleEvent(B_START) done\n");
|
||||
break;
|
||||
|
||||
case BTimedEventQueue::B_STOP:
|
||||
TRACE("AudioProducer::HandleEvent(B_STOP)\n");
|
||||
EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
|
||||
BTimedEventQueue::B_HANDLE_BUFFER);
|
||||
TRACE("AudioProducer::HandleEvent(B_STOP) done\n");
|
||||
break;
|
||||
|
||||
case BTimedEventQueue::B_HANDLE_BUFFER: {
|
||||
TRACE("AudioProducer::HandleEvent(B_HANDLE_BUFFER)\n");
|
||||
if ((RunState() == BMediaEventLooper::B_STARTED)
|
||||
&& (fOutput.destination != media_destination::null)) {
|
||||
BBuffer* buffer = _FillNextBuffer(event->event_time);
|
||||
if (buffer) {
|
||||
status_t err = B_ERROR;
|
||||
if (fOutputEnabled)
|
||||
err = SendBuffer(buffer, fOutput.destination);
|
||||
if (err)
|
||||
buffer->Recycle();
|
||||
}
|
||||
size_t sampleSize
|
||||
= fOutput.format.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
|
||||
size_t nFrames = fOutput.format.u.raw_audio.buffer_size
|
||||
/ (sampleSize
|
||||
* fOutput.format.u.raw_audio.channel_count);
|
||||
fFramesSent += nFrames;
|
||||
|
||||
bigtime_t nextEvent = fStartTime
|
||||
+ bigtime_t(double(fFramesSent) * 1000000.0
|
||||
/ double(fOutput.format.u.raw_audio.frame_rate));
|
||||
media_timed_event nextBufferEvent(nextEvent,
|
||||
BTimedEventQueue::B_HANDLE_BUFFER);
|
||||
EventQueue()->AddEvent(nextBufferEvent);
|
||||
}
|
||||
TRACE("AudioProducer::HandleEvent(B_HANDLE_BUFFER) done\n");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioProducer::SetRunning(bool running)
|
||||
{
|
||||
TRACE("%p->AudioProducer::SetRunning(%d)\n", this, running);
|
||||
|
||||
fRunning = running;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
status_t
|
||||
AudioProducer::_AllocateBuffers(media_format* format)
|
||||
{
|
||||
TRACE("%p->AudioProducer::_AllocateBuffers()\n", this);
|
||||
|
||||
if (fBufferGroup && fUsingOurBuffers) {
|
||||
delete fBufferGroup;
|
||||
fBufferGroup = NULL;
|
||||
}
|
||||
size_t size = format->u.raw_audio.buffer_size;
|
||||
int32 bufferDuration = BufferDuration();
|
||||
int32 count = 0;
|
||||
if (bufferDuration > 0) {
|
||||
count = (int32)((fLatency + fInternalLatency)
|
||||
/ bufferDuration + 2);
|
||||
}
|
||||
|
||||
fBufferGroup = new BBufferGroup(size, count);
|
||||
fUsingOurBuffers = true;
|
||||
return fBufferGroup->InitCheck();
|
||||
}
|
||||
|
||||
|
||||
BBuffer*
|
||||
AudioProducer::_FillNextBuffer(bigtime_t eventTime)
|
||||
{
|
||||
BBuffer* buffer = fBufferGroup->RequestBuffer(
|
||||
fOutput.format.u.raw_audio.buffer_size, BufferDuration());
|
||||
|
||||
if (!buffer) {
|
||||
TRACE("AudioProducer::_FillNextBuffer() - no buffer\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t sampleSize = fOutput.format.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
size_t numSamples = fOutput.format.u.raw_audio.buffer_size / sampleSize;
|
||||
// number of sample in the buffer
|
||||
|
||||
// fill in the buffer header
|
||||
media_header* hdr = buffer->Header();
|
||||
hdr->type = B_MEDIA_RAW_AUDIO;
|
||||
hdr->time_source = TimeSource()->ID();
|
||||
buffer->SetSizeUsed(fOutput.format.u.raw_audio.buffer_size);
|
||||
|
||||
bigtime_t performanceTime = bigtime_t(double(fFramesSent)
|
||||
* 1000000.0 / double(fOutput.format.u.raw_audio.frame_rate));
|
||||
|
||||
// fill in data from audio supplier
|
||||
size_t frameCount = numSamples / fOutput.format.u.raw_audio.channel_count;
|
||||
bigtime_t startTime = performanceTime;
|
||||
bigtime_t endTime = bigtime_t(double(fFramesSent + frameCount)
|
||||
* 1000000.0 / double(fOutput.format.u.raw_audio.frame_rate));
|
||||
|
||||
if (!fSupplier || fSupplier->InitCheck() != B_OK
|
||||
|| fSupplier->GetFrames(buffer->Data(), frameCount, startTime,
|
||||
endTime) != B_OK) {
|
||||
TRACE("AudioProducer::_FillNextBuffer() - error -> silence\n");
|
||||
memset(buffer->Data(), 0, buffer->SizeUsed());
|
||||
}
|
||||
|
||||
// stamp buffer
|
||||
if (RunMode() == B_RECORDING) {
|
||||
hdr->start_time = eventTime;
|
||||
} else {
|
||||
hdr->start_time = fStartTime + performanceTime;
|
||||
}
|
||||
|
||||
#if DEBUG_TO_FILE
|
||||
BMediaTrack* track;
|
||||
if (BMediaFile* file = init_media_file(fOutput.format, &track)) {
|
||||
track->WriteFrames(buffer->Data(), frameCount);
|
||||
}
|
||||
#endif // DEBUG_TO_FILE
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
|
||||
* Distributed under the terms of the Be Sample Code license.
|
||||
*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef AUDIO_PRODUCER_H
|
||||
#define AUDIO_PRODUCER_H
|
||||
|
||||
#include <BufferProducer.h>
|
||||
//#include <Controllable.h>
|
||||
#include <MediaEventLooper.h>
|
||||
|
||||
class AudioSupplier;
|
||||
|
||||
class AudioProducer : public BBufferProducer, public BMediaEventLooper {
|
||||
public:
|
||||
AudioProducer(const char* name,
|
||||
AudioSupplier* supplier,
|
||||
bool lowLatency = true);
|
||||
virtual ~AudioProducer();
|
||||
|
||||
// BMediaNode interface
|
||||
virtual BMediaAddOn* AddOn(int32 *internal_id) const;
|
||||
|
||||
// BBufferProducer interface
|
||||
virtual status_t FormatSuggestionRequested(media_type type,
|
||||
int32 quality, media_format* _format);
|
||||
|
||||
virtual status_t FormatProposal(const media_source& output,
|
||||
media_format* format);
|
||||
|
||||
virtual status_t FormatChangeRequested(
|
||||
const media_source& source,
|
||||
const media_destination& destination,
|
||||
media_format* ioFormat,
|
||||
int32* _deprecated_);
|
||||
|
||||
virtual status_t GetNextOutput(int32* cookie,
|
||||
media_output* _output);
|
||||
|
||||
virtual status_t DisposeOutputCookie(int32 cookie);
|
||||
|
||||
virtual status_t SetBufferGroup(const media_source& forSource,
|
||||
BBufferGroup* group);
|
||||
|
||||
virtual status_t GetLatency(bigtime_t* _latency);
|
||||
|
||||
virtual status_t PrepareToConnect(const media_source& what,
|
||||
const media_destination& where,
|
||||
media_format* format,
|
||||
media_source* outSource, char* outName);
|
||||
|
||||
virtual void Connect(status_t error,
|
||||
const media_source& source,
|
||||
const media_destination& destination,
|
||||
const media_format& format,
|
||||
char* ioName);
|
||||
|
||||
virtual void Disconnect(const media_source &what,
|
||||
const media_destination& where);
|
||||
|
||||
virtual void LateNoticeReceived(const media_source& what,
|
||||
bigtime_t howMuch,
|
||||
bigtime_t performanceTime);
|
||||
|
||||
virtual void EnableOutput(const media_source& what,
|
||||
bool enabled, int32* _deprecated_);
|
||||
|
||||
virtual status_t SetPlayRate(int32 numer, int32 denom);
|
||||
|
||||
virtual status_t HandleMessage(int32 message,
|
||||
const void* data, size_t size);
|
||||
|
||||
virtual void AdditionalBufferRequested(
|
||||
const media_source& source,
|
||||
media_buffer_id prevBuffer,
|
||||
bigtime_t prevTime,
|
||||
const media_seek_tag* prevTag);
|
||||
// may be NULL
|
||||
|
||||
virtual void LatencyChanged(const media_source& source,
|
||||
const media_destination& destination,
|
||||
bigtime_t newLatency, uint32 flags);
|
||||
|
||||
// BMediaEventLooper interface
|
||||
virtual void NodeRegistered();
|
||||
|
||||
virtual void SetRunMode(run_mode mode);
|
||||
|
||||
virtual void HandleEvent(const media_timed_event* event,
|
||||
bigtime_t lateness,
|
||||
bool realTimeEvent = false);
|
||||
|
||||
void SetRunning(bool running);
|
||||
|
||||
private:
|
||||
status_t _AllocateBuffers(media_format* format);
|
||||
BBuffer* _FillNextBuffer(bigtime_t eventTime);
|
||||
|
||||
void _FillSampleBuffer(float* data,
|
||||
size_t numSamples);
|
||||
|
||||
BBufferGroup* fBufferGroup;
|
||||
bool fUsingOurBuffers;
|
||||
bigtime_t fLatency;
|
||||
bigtime_t fInternalLatency;
|
||||
bool fLowLatency;
|
||||
media_output fOutput;
|
||||
bool fOutputEnabled;
|
||||
media_format fPreferredFormat;
|
||||
|
||||
uint64 fFramesSent;
|
||||
bigtime_t fStartTime;
|
||||
|
||||
AudioSupplier* fSupplier;
|
||||
volatile bool fRunning;
|
||||
};
|
||||
|
||||
#endif // AUDIO_PRODUCER_H
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
#include <algobase.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "AudioReader.h"
|
||||
|
||||
|
||||
AudioReader::AudioReader()
|
||||
: fFormat(),
|
||||
fOutOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AudioReader::AudioReader(const media_format& format)
|
||||
: fFormat(format),
|
||||
fOutOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AudioReader::~AudioReader()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioReader::InitCheck() const
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioReader::SetFormat(const media_format& format)
|
||||
{
|
||||
fFormat = format;
|
||||
}
|
||||
|
||||
|
||||
const media_format&
|
||||
AudioReader::Format() const
|
||||
{
|
||||
return fFormat;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioReader::SetOutOffset(int64 offset)
|
||||
{
|
||||
fOutOffset = offset;
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
AudioReader::OutOffset() const
|
||||
{
|
||||
return fOutOffset;
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
AudioReader::FrameForTime(bigtime_t time) const
|
||||
{
|
||||
double frameRate = fFormat.u.raw_audio.frame_rate;
|
||||
return int64(double(time) * frameRate / 1000000.0);
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
AudioReader::TimeForFrame(int64 frame) const
|
||||
{
|
||||
double frameRate = fFormat.u.raw_audio.frame_rate;
|
||||
return bigtime_t(double(frame) * 1000000.0 / frameRate);
|
||||
}
|
||||
|
||||
|
||||
//! helper function for ReadSilence()
|
||||
template<typename sample_t>
|
||||
inline void
|
||||
fill_buffer(void* buffer, int32 count, sample_t value)
|
||||
{
|
||||
sample_t* buf = (sample_t*)buffer;
|
||||
sample_t* bufferEnd = buf + count;
|
||||
for (; buf < bufferEnd; buf++)
|
||||
*buf = value;
|
||||
}
|
||||
|
||||
|
||||
/*! Fills the supplied buffer with /frames/ frames of silence and returns a
|
||||
pointer to the frames after the filled range.
|
||||
/frames/ must be >= 0.*/
|
||||
void*
|
||||
AudioReader::ReadSilence(void* buffer, int64 frames) const
|
||||
{
|
||||
void* bufferEnd = SkipFrames(buffer, frames);
|
||||
int32 sampleCount = frames * fFormat.u.raw_audio.channel_count;
|
||||
switch (fFormat.u.raw_audio.format) {
|
||||
case media_raw_audio_format::B_AUDIO_FLOAT:
|
||||
fill_buffer(buffer, sampleCount, (float)0);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_INT:
|
||||
fill_buffer(buffer, sampleCount, (int)0);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_SHORT:
|
||||
fill_buffer(buffer, sampleCount, (short)0);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_UCHAR:
|
||||
fill_buffer(buffer, sampleCount, (uchar)128);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_CHAR:
|
||||
fill_buffer(buffer, sampleCount, (uchar)0);
|
||||
break;
|
||||
default:
|
||||
memset(buffer, 0, (char*)bufferEnd - (char*)buffer);
|
||||
break;
|
||||
}
|
||||
return bufferEnd;
|
||||
}
|
||||
|
||||
|
||||
//! Returns a buffer pointer offset by /frames/ frames.
|
||||
void*
|
||||
AudioReader::SkipFrames(void* buffer, int64 frames) const
|
||||
{
|
||||
int32 sampleSize = fFormat.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
int32 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
|
||||
return (char*)buffer + frames * frameSize;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioReader::ReverseFrames(void* buffer, int64 frames) const
|
||||
{
|
||||
int32 sampleSize = fFormat.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
int32 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
|
||||
char* front = (char*)buffer;
|
||||
char* back = (char*)buffer + (frames - 1) * frameSize;
|
||||
while (front < back) {
|
||||
for (int32 i = 0; i < frameSize; i++)
|
||||
swap(front[i], back[i]);
|
||||
front += frameSize;
|
||||
back -= frameSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
#ifndef AUDIO_READER_H
|
||||
#define AUDIO_READER_H
|
||||
|
||||
#include <MediaDefs.h>
|
||||
|
||||
class AudioReader {
|
||||
public:
|
||||
AudioReader();
|
||||
AudioReader(const media_format& format);
|
||||
virtual ~AudioReader();
|
||||
|
||||
virtual status_t InitCheck() const;
|
||||
|
||||
void SetFormat(const media_format& format);
|
||||
const media_format& Format() const;
|
||||
|
||||
virtual status_t Read(void* buffer, int64 pos, int64 frames) = 0;
|
||||
|
||||
void SetOutOffset(int64 offset);
|
||||
int64 OutOffset() const;
|
||||
|
||||
int64 FrameForTime(bigtime_t time) const;
|
||||
bigtime_t TimeForFrame(int64 frame) const;
|
||||
|
||||
protected:
|
||||
void* ReadSilence(void* buffer, int64 frames) const;
|
||||
void* SkipFrames(void* buffer, int64 frames) const;
|
||||
void ReverseFrames(void* buffer,
|
||||
int64 frames) const;
|
||||
|
||||
protected:
|
||||
media_format fFormat;
|
||||
int64 fOutOffset;
|
||||
};
|
||||
|
||||
#endif // AUDIO_READER_H
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
#include "AudioResampler.h"
|
||||
|
||||
#include <algobase.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SampleBuffer.h"
|
||||
|
||||
|
||||
//#define TRACE_AUDIO_RESAMPLER
|
||||
#ifdef TRACE_AUDIO_RESAMPLER
|
||||
# define TRACE(x...) printf(x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
#endif
|
||||
|
||||
|
||||
AudioResampler::AudioResampler()
|
||||
: AudioReader(),
|
||||
fSource(NULL),
|
||||
fTimeScale(1.0),
|
||||
fInOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AudioResampler::AudioResampler(AudioReader* source, float frameRate,
|
||||
float timeScale)
|
||||
: AudioReader(),
|
||||
fSource(NULL),
|
||||
fTimeScale(timeScale),
|
||||
fInOffset(0)
|
||||
{
|
||||
SetSource(source);
|
||||
if (fSource)
|
||||
fFormat.u.raw_audio.frame_rate = frameRate;
|
||||
}
|
||||
|
||||
|
||||
AudioResampler::~AudioResampler()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//! Calculates the greatest common divider of /a/ and /b/.
|
||||
template<typename T> inline
|
||||
T
|
||||
gcd(T a, T b)
|
||||
{
|
||||
while (b != 0) {
|
||||
T r = a % b;
|
||||
a = b;
|
||||
b = r;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
template<typename Buffer>
|
||||
static void
|
||||
resample_linear(void* _inBuffer, void* _outBuffer, uint32 channelCount,
|
||||
double inFrameRate, double outFrameRate, int32 frames)
|
||||
{
|
||||
typedef double sample_t;
|
||||
Buffer inBuffer(_inBuffer);
|
||||
Buffer outFrameBuf(_outBuffer);
|
||||
for (sample_t outFrame = 0; outFrame < frames; outFrame++) {
|
||||
// time of the out sample
|
||||
sample_t outTime = outFrame / outFrameRate;
|
||||
// first in frame
|
||||
int64 inFrame = int64(outTime * inFrameRate);
|
||||
// time of the first and the second in frame
|
||||
sample_t inTime1 = (sample_t)inFrame / inFrameRate;
|
||||
sample_t inTime2 = (sample_t)(inFrame + 1) / inFrameRate;
|
||||
// differences between the out frame time and the in frame times
|
||||
sample_t timeDiff1 = outTime - inTime1;
|
||||
sample_t timeDiff2 = inTime2 - outTime;
|
||||
sample_t timeDiff = timeDiff1 + timeDiff2;
|
||||
// pointer to the first and second in frame
|
||||
Buffer inFrameBuf1 = inBuffer + inFrame * channelCount;
|
||||
Buffer inFrameBuf2 = inFrameBuf1 + channelCount;
|
||||
for (uint32 c = 0; c < channelCount;
|
||||
c++, inFrameBuf1++, inFrameBuf2++, outFrameBuf++) {
|
||||
// sum weighted according to the distance to the respective other
|
||||
// in frame
|
||||
outFrameBuf.WriteSample((timeDiff2 * inFrameBuf1.ReadSample()
|
||||
+ timeDiff1 * inFrameBuf2.ReadSample()) / timeDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioResampler::Read(void* buffer, int64 pos, int64 frames)
|
||||
{
|
||||
TRACE("AudioResampler::Read(%p, %Ld, %Ld)\n", buffer, pos, frames);
|
||||
|
||||
status_t error = InitCheck();
|
||||
if (error != B_OK) {
|
||||
TRACE("AudioResampler::Read() done1\n");
|
||||
return error;
|
||||
}
|
||||
// calculate position and frames in the source data
|
||||
int64 sourcePos = ConvertToSource(pos);
|
||||
int64 sourceFrames = ConvertToSource(pos + frames) - sourcePos;
|
||||
// check the frame counts
|
||||
if (sourceFrames == frames) {
|
||||
TRACE("AudioResampler::Read() done2\n");
|
||||
return fSource->Read(buffer, sourcePos, sourceFrames);
|
||||
}
|
||||
if (sourceFrames == 0) {
|
||||
ReadSilence(buffer, frames);
|
||||
TRACE("AudioResampler::Read() done3\n");
|
||||
return B_OK;
|
||||
}
|
||||
// check, if playing backwards
|
||||
bool backward = false;
|
||||
if (sourceFrames < 0) {
|
||||
sourceFrames = -sourceFrames;
|
||||
sourcePos -= sourceFrames;
|
||||
backward = true;
|
||||
}
|
||||
|
||||
// we need at least two frames to interpolate
|
||||
sourceFrames += 2;
|
||||
int32 sampleSize = media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
uint32 channelCount = fFormat.u.raw_audio.channel_count;
|
||||
char* inBuffer = new char[sourceFrames * channelCount * sampleSize];
|
||||
error = fSource->Read(inBuffer, sourcePos, sourceFrames);
|
||||
if (error != B_OK) {
|
||||
TRACE("AudioResampler::_ReadLinear() done4\n");
|
||||
return error;
|
||||
}
|
||||
double inFrameRate = fSource->Format().u.raw_audio.frame_rate;
|
||||
double outFrameRate = (double)fFormat.u.raw_audio.frame_rate
|
||||
/ (double)fTimeScale;
|
||||
// choose the sample buffer to be used
|
||||
switch (fFormat.u.raw_audio.format) {
|
||||
case media_raw_audio_format::B_AUDIO_FLOAT:
|
||||
resample_linear< FloatSampleBuffer<double> >(inBuffer, buffer,
|
||||
channelCount, inFrameRate, outFrameRate, (int32)frames);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_INT:
|
||||
resample_linear< IntSampleBuffer<double> >(inBuffer, buffer,
|
||||
channelCount, inFrameRate, outFrameRate, (int32)frames);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_SHORT:
|
||||
resample_linear< ShortSampleBuffer<double> >(inBuffer, buffer,
|
||||
channelCount, inFrameRate, outFrameRate, (int32)frames);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_UCHAR:
|
||||
resample_linear< UCharSampleBuffer<double> >(inBuffer, buffer,
|
||||
channelCount, inFrameRate, outFrameRate, (int32)frames);
|
||||
break;
|
||||
case media_raw_audio_format::B_AUDIO_CHAR:
|
||||
resample_linear< CharSampleBuffer<double> >(inBuffer, buffer,
|
||||
channelCount, inFrameRate, outFrameRate, (int32)frames);
|
||||
break;
|
||||
}
|
||||
// reverse the frame order if reading backwards
|
||||
if (backward)
|
||||
ReverseFrames(buffer, frames);
|
||||
delete[] inBuffer;
|
||||
TRACE("AudioResampler::Read() done\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioResampler::InitCheck() const
|
||||
{
|
||||
status_t error = AudioReader::InitCheck();
|
||||
if (error == B_OK && !fSource)
|
||||
error = B_NO_INIT;
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioResampler::SetSource(AudioReader* source)
|
||||
{
|
||||
if (!source) {
|
||||
TRACE("AudioResampler::SetSource() - NULL source\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (source->Format().type != B_MEDIA_RAW_AUDIO) {
|
||||
TRACE("AudioResampler::SetSource() - not B_MEDIA_RAW_AUDIO\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 hostByteOrder
|
||||
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
|
||||
if (source->Format().u.raw_audio.byte_order != hostByteOrder) {
|
||||
TRACE("AudioResampler::SetSource() - not host byte order\n");
|
||||
return;
|
||||
}
|
||||
|
||||
float frameRate = FrameRate();
|
||||
// don't overwrite previous audio frame rate
|
||||
fSource = source;
|
||||
fFormat = source->Format();
|
||||
fFormat.u.raw_audio.frame_rate = frameRate;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioResampler::SetFrameRate(float frameRate)
|
||||
{
|
||||
fFormat.u.raw_audio.frame_rate = frameRate;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioResampler::SetTimeScale(float timeScale)
|
||||
{
|
||||
fTimeScale = timeScale;
|
||||
}
|
||||
|
||||
|
||||
AudioReader*
|
||||
AudioResampler::Source() const
|
||||
{
|
||||
return fSource;
|
||||
}
|
||||
|
||||
|
||||
float
|
||||
AudioResampler::FrameRate() const
|
||||
{
|
||||
return fFormat.u.raw_audio.frame_rate;
|
||||
}
|
||||
|
||||
|
||||
float
|
||||
AudioResampler::TimeScale() const
|
||||
{
|
||||
return fTimeScale;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioResampler::SetInOffset(int64 offset)
|
||||
{
|
||||
fInOffset = offset;
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
AudioResampler::InOffset() const
|
||||
{
|
||||
return fInOffset;
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
AudioResampler::ConvertFromSource(int64 pos) const
|
||||
{
|
||||
double inFrameRate = fSource->Format().u.raw_audio.frame_rate;
|
||||
double outFrameRate = fFormat.u.raw_audio.frame_rate;
|
||||
return (int64)((double)(pos - fInOffset) * outFrameRate / inFrameRate
|
||||
/ (double)fTimeScale) - fOutOffset;
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
AudioResampler::ConvertToSource(int64 pos) const
|
||||
{
|
||||
double inFrameRate = fSource->Format().u.raw_audio.frame_rate;
|
||||
double outFrameRate = fFormat.u.raw_audio.frame_rate;
|
||||
return (int64)((double)(pos + fOutOffset) * inFrameRate / outFrameRate
|
||||
* (double)fTimeScale) + fInOffset;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
|
||||
/*! This AudioReader does both resampling an audio source to a different
|
||||
sample rate and rescaling the time, e.g. it is possible to convert the
|
||||
source data from 41.1 KHz to 96 KHz played backward twice as fast
|
||||
(time scale = -2).
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_RESAMPLER_H
|
||||
#define AUDIO_RESAMPLER_H
|
||||
|
||||
#include "AudioReader.h"
|
||||
|
||||
class AudioResampler : public AudioReader {
|
||||
public:
|
||||
AudioResampler();
|
||||
AudioResampler(AudioReader* source,
|
||||
float frameRate, float timeScale = 1.0);
|
||||
virtual ~AudioResampler();
|
||||
|
||||
virtual status_t Read(void* buffer, int64 pos, int64 frames);
|
||||
|
||||
virtual status_t InitCheck() const;
|
||||
|
||||
void SetSource(AudioReader* source);
|
||||
void SetFrameRate(float frameRate);
|
||||
void SetTimeScale(float timeScale);
|
||||
|
||||
AudioReader* Source() const;
|
||||
float FrameRate() const;
|
||||
float TimeScale() const;
|
||||
|
||||
void SetInOffset(int64 offset);
|
||||
int64 InOffset() const;
|
||||
|
||||
int64 ConvertFromSource(int64 pos) const;
|
||||
int64 ConvertToSource(int64 pos) const;
|
||||
|
||||
private:
|
||||
status_t _ReadLinear(void* buffer, int64 pos,
|
||||
int64 frames);
|
||||
|
||||
private:
|
||||
AudioReader* fSource;
|
||||
float fTimeScale; // speed
|
||||
int64 fInOffset;
|
||||
};
|
||||
|
||||
#endif // AUDIO_RESAMPLER_H
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include "AudioSupplier.h"
|
||||
|
||||
#include "AudioProducer.h"
|
||||
|
||||
|
||||
AudioSupplier::AudioSupplier()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AudioSupplier::~AudioSupplier()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioSupplier::SetAudioProducer(AudioProducer* producer)
|
||||
{
|
||||
fAudioProducer = producer;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AudioSupplier::InitCheck() const
|
||||
{
|
||||
return (fAudioProducer ? B_OK : B_NO_INIT);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
/*! This class is an interface used by the AudioProducer to retreive the
|
||||
audio data to be played. */
|
||||
#ifndef AUDIO_SUPPLIER_H
|
||||
#define AUDIO_SUPPLIER_H
|
||||
|
||||
|
||||
#include <MediaDefs.h>
|
||||
|
||||
|
||||
class AudioProducer;
|
||||
|
||||
class AudioSupplier {
|
||||
public:
|
||||
AudioSupplier();
|
||||
virtual ~AudioSupplier();
|
||||
|
||||
virtual void SetAudioProducer(AudioProducer* producer);
|
||||
|
||||
virtual status_t GetFrames(void* buffer, int64 frameCount,
|
||||
bigtime_t startTime,
|
||||
bigtime_t endTime) = 0;
|
||||
|
||||
virtual void SetFormat(const media_format& format) = 0;
|
||||
|
||||
virtual status_t InitCheck() const;
|
||||
|
||||
protected:
|
||||
AudioProducer* fAudioProducer;
|
||||
};
|
||||
|
||||
|
||||
#endif // AUDIO_SUPPLIER_H
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright © 2000-2003 Ingo Weinhold <[email protected]>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
|
||||
// This file contains the following classes:
|
||||
// * SampleBuffer
|
||||
// * FloatSampleBuffer
|
||||
// * IntSampleBuffer
|
||||
// * ShortSampleBuffer
|
||||
// * UCharSampleBuffer
|
||||
// * CharSampleBuffer
|
||||
|
||||
#ifndef SAMPLE_BUFFER_H
|
||||
#define SAMPLE_BUFFER_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
// SampleBuffer
|
||||
|
||||
template<int BYTES_PER_SAMPLE>
|
||||
class SampleBuffer {
|
||||
protected:
|
||||
typedef uint8 sample_block_t[BYTES_PER_SAMPLE];
|
||||
|
||||
public:
|
||||
inline SampleBuffer(void* buffer)
|
||||
: fBuffer((sample_block_t*)buffer) { }
|
||||
|
||||
inline void operator+=(int samples) {
|
||||
fBuffer += samples;
|
||||
}
|
||||
inline void operator-=(int samples) {
|
||||
fBuffer -= samples;
|
||||
}
|
||||
|
||||
inline void operator++(int) {
|
||||
fBuffer++;
|
||||
}
|
||||
inline void operator--(int) {
|
||||
fBuffer--;
|
||||
}
|
||||
|
||||
inline void* operator+(int samples) {
|
||||
return fBuffer + samples;
|
||||
}
|
||||
inline void* operator-(int samples) {
|
||||
return fBuffer + samples;
|
||||
}
|
||||
|
||||
protected:
|
||||
sample_block_t* fBuffer;
|
||||
};
|
||||
|
||||
|
||||
// FloatSampleBuffer
|
||||
|
||||
template<typename sample_t>
|
||||
class FloatSampleBuffer : public SampleBuffer<sizeof(float)> {
|
||||
public:
|
||||
inline FloatSampleBuffer(void* buffer)
|
||||
: SampleBuffer<sizeof(float)>(buffer) {
|
||||
}
|
||||
|
||||
inline sample_t ReadSample() const {
|
||||
return *((float*)fBuffer);
|
||||
}
|
||||
|
||||
inline void WriteSample(sample_t value) {
|
||||
*((float*)fBuffer) = value;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// IntSampleBuffer
|
||||
|
||||
template<typename sample_t>
|
||||
class IntSampleBuffer : public SampleBuffer<sizeof(int)> {
|
||||
public:
|
||||
inline IntSampleBuffer(void* buffer)
|
||||
: SampleBuffer<sizeof(int)>(buffer) {
|
||||
}
|
||||
|
||||
inline sample_t ReadSample() const {
|
||||
return (sample_t)*((int*)fBuffer) / (sample_t)0x7fffffff;
|
||||
}
|
||||
|
||||
inline void WriteSample(sample_t value) {
|
||||
*((int*)fBuffer) = int(value * (sample_t)0x7fffffff);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ShortSampleBuffer
|
||||
|
||||
template<typename sample_t>
|
||||
class ShortSampleBuffer : public SampleBuffer<sizeof(short)> {
|
||||
public:
|
||||
inline ShortSampleBuffer(void* buffer)
|
||||
: SampleBuffer<sizeof(short)>(buffer) {
|
||||
}
|
||||
|
||||
inline sample_t ReadSample() const {
|
||||
return (sample_t)*((short*)fBuffer) / (sample_t)32767;
|
||||
}
|
||||
|
||||
inline void WriteSample(sample_t value) {
|
||||
*((short*)fBuffer) = short(value * (sample_t)32767);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// UCharSampleBuffer
|
||||
|
||||
template<typename sample_t>
|
||||
class UCharSampleBuffer : public SampleBuffer<sizeof(uchar)> {
|
||||
public:
|
||||
inline UCharSampleBuffer(void* buffer)
|
||||
: SampleBuffer<sizeof(uchar)>(buffer) {
|
||||
}
|
||||
|
||||
inline sample_t ReadSample() const {
|
||||
return ((sample_t)*((uchar*)fBuffer) - 128) / (sample_t)127;
|
||||
}
|
||||
|
||||
inline void WriteSample(sample_t value) {
|
||||
*((uchar*)fBuffer) = uchar(value * (sample_t)127 + 128);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// CharSampleBuffer
|
||||
|
||||
template<typename sample_t>
|
||||
class CharSampleBuffer : public SampleBuffer<sizeof(char)> {
|
||||
public:
|
||||
inline CharSampleBuffer(void* buffer)
|
||||
: SampleBuffer<sizeof(char)>(buffer) {
|
||||
}
|
||||
|
||||
inline sample_t ReadSample() const {
|
||||
return (sample_t)*((char*)fBuffer) / (sample_t)127;
|
||||
}
|
||||
|
||||
inline void WriteSample(sample_t value) {
|
||||
*((uchar*)fBuffer) = uchar(value * (sample_t)127);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // SAMPLE_BUFFER_H
|
||||
@@ -0,0 +1,616 @@
|
||||
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
|
||||
* Distributed under the terms of the Be Sample Code license.
|
||||
*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include "VideoConsumer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <Buffer.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <NodeInfo.h>
|
||||
#include <Application.h>
|
||||
#include <Bitmap.h>
|
||||
#include <View.h>
|
||||
#include <Window.h>
|
||||
#include <scheduler.h>
|
||||
#include <TimeSource.h>
|
||||
#include <MediaRoster.h>
|
||||
#include <BufferGroup.h>
|
||||
|
||||
#include "NodeManager.h"
|
||||
#include "VideoTarget.h"
|
||||
|
||||
|
||||
// debugging
|
||||
//#define TRACE_VIDEO_CONSUMER
|
||||
#ifdef TRACE_VIDEO_CONSUMER
|
||||
# define TRACE(x...) printf(x)
|
||||
# define PROGRESS(x...) printf(x)
|
||||
# define FUNCTION(x...) printf(x)
|
||||
# define LOOP(x...) printf(x)
|
||||
# define ERROR(x...) fprintf(stderr, x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
# define PROGRESS(x...)
|
||||
# define FUNCTION(x...)
|
||||
# define LOOP(x...)
|
||||
# define ERROR(x...) fprintf(stderr, x)
|
||||
#endif
|
||||
|
||||
#define M1 ((double)1000000.0)
|
||||
#define JITTER 20000
|
||||
|
||||
|
||||
VideoConsumer::VideoConsumer(const char* name, BMediaAddOn* addon,
|
||||
const uint32 internal_id, NodeManager* manager,
|
||||
VideoTarget* target)
|
||||
: BMediaNode(name),
|
||||
BMediaEventLooper(),
|
||||
BBufferConsumer(B_MEDIA_RAW_VIDEO),
|
||||
fInternalID(internal_id),
|
||||
fAddOn(addon),
|
||||
fConnectionActive(false),
|
||||
fMyLatency(20000),
|
||||
fOurBuffers(false),
|
||||
fBuffers(NULL),
|
||||
fManager(manager),
|
||||
fTargetLock(),
|
||||
fTarget(target),
|
||||
fTargetBufferIndex(-1)
|
||||
{
|
||||
FUNCTION("VideoConsumer::VideoConsumer\n");
|
||||
|
||||
AddNodeKind(B_PHYSICAL_OUTPUT);
|
||||
SetEventLatency(0);
|
||||
|
||||
for (uint32 i = 0; i < kBufferCount; i++) {
|
||||
fBitmap[i] = NULL;
|
||||
fBufferMap[i] = 0;
|
||||
}
|
||||
|
||||
SetPriority(B_DISPLAY_PRIORITY);
|
||||
}
|
||||
|
||||
|
||||
VideoConsumer::~VideoConsumer()
|
||||
{
|
||||
Quit();
|
||||
DeleteBuffers();
|
||||
}
|
||||
|
||||
|
||||
BMediaAddOn*
|
||||
VideoConsumer::AddOn(long* cookie) const
|
||||
{
|
||||
FUNCTION("VideoConsumer::AddOn\n");
|
||||
// do the right thing if we're ever used with an add-on
|
||||
*cookie = fInternalID;
|
||||
return fAddOn;
|
||||
}
|
||||
|
||||
|
||||
// This implementation is required to get around a bug in
|
||||
// the ppc compiler.
|
||||
void
|
||||
VideoConsumer::Start(bigtime_t performance_time)
|
||||
{
|
||||
BMediaEventLooper::Start(performance_time);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoConsumer::Stop(bigtime_t performance_time, bool immediate)
|
||||
{
|
||||
BMediaEventLooper::Stop(performance_time, immediate);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoConsumer::Seek(bigtime_t media_time, bigtime_t performance_time)
|
||||
{
|
||||
BMediaEventLooper::Seek(media_time, performance_time);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoConsumer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
|
||||
{
|
||||
BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoConsumer::NodeRegistered()
|
||||
{
|
||||
FUNCTION("VideoConsumer::NodeRegistered\n");
|
||||
fIn.destination.port = ControlPort();
|
||||
fIn.destination.id = 0;
|
||||
fIn.source = media_source::null;
|
||||
fIn.format.type = B_MEDIA_RAW_VIDEO;
|
||||
// wild cards yet
|
||||
fIn.format.u.raw_video = media_raw_video_format::wildcard;
|
||||
fIn.format.u.raw_video.interlace = 1;
|
||||
fIn.format.u.raw_video.display.format = B_NO_COLOR_SPACE;
|
||||
fIn.format.u.raw_video.display.bytes_per_row = 0;
|
||||
fIn.format.u.raw_video.display.line_width = 0;
|
||||
fIn.format.u.raw_video.display.line_count = 0;
|
||||
|
||||
Run();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoConsumer::RequestCompleted(const media_request_info& info)
|
||||
{
|
||||
FUNCTION("VideoConsumer::RequestCompleted\n");
|
||||
switch(info.what) {
|
||||
case media_request_info::B_SET_OUTPUT_BUFFERS_FOR:
|
||||
if (info.status != B_OK)
|
||||
ERROR("VideoConsumer::RequestCompleted: Not using our "
|
||||
"buffers!\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoConsumer::HandleMessage(int32 message, const void* data, size_t size)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoConsumer::BufferReceived(BBuffer* buffer)
|
||||
{
|
||||
LOOP("VideoConsumer::Buffer #%ld received\n", buffer->ID());
|
||||
|
||||
if (RunState() == B_STOPPED) {
|
||||
buffer->Recycle();
|
||||
return;
|
||||
}
|
||||
|
||||
media_timed_event event(buffer->Header()->start_time,
|
||||
BTimedEventQueue::B_HANDLE_BUFFER, buffer,
|
||||
BTimedEventQueue::B_RECYCLE_BUFFER);
|
||||
EventQueue()->AddEvent(event);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoConsumer::ProducerDataStatus(const media_destination& forWhom,
|
||||
int32 status, bigtime_t atMediaTime)
|
||||
{
|
||||
FUNCTION("VideoConsumer::ProducerDataStatus\n");
|
||||
|
||||
if (forWhom != fIn.destination)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoConsumer::CreateBuffers(const media_format& format)
|
||||
{
|
||||
FUNCTION("VideoConsumer::CreateBuffers\n");
|
||||
|
||||
// delete any old buffers
|
||||
DeleteBuffers();
|
||||
|
||||
status_t status = B_OK;
|
||||
|
||||
// create a buffer group
|
||||
uint32 width = format.u.raw_video.display.line_width;
|
||||
uint32 height = format.u.raw_video.display.line_count;
|
||||
color_space colorSpace = format.u.raw_video.display.format;
|
||||
PROGRESS("VideoConsumer::CreateBuffers - Colorspace = %d\n", colorSpace);
|
||||
|
||||
fBuffers = new BBufferGroup();
|
||||
status = fBuffers->InitCheck();
|
||||
if (B_OK != status) {
|
||||
ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
// and attach the bitmaps to the buffer group
|
||||
BRect bounds(0, 0, width - 1, height - 1);
|
||||
for (uint32 i = 0; i < kBufferCount; i++) {
|
||||
// figure out the bitmap creation flags
|
||||
uint32 bitmapFlags = 0;
|
||||
if (colorSpace == B_YCbCr422 || colorSpace == B_YCbCr444) {
|
||||
// try to use hardware overlay
|
||||
bitmapFlags |= B_BITMAP_WILL_OVERLAY;
|
||||
if (i == 0)
|
||||
bitmapFlags |= B_BITMAP_RESERVE_OVERLAY_CHANNEL;
|
||||
} else
|
||||
bitmapFlags = B_BITMAP_IS_LOCKED;
|
||||
|
||||
fBitmap[i] = new BBitmap(bounds, bitmapFlags, colorSpace);
|
||||
status = fBitmap[i]->InitCheck();
|
||||
if (status >= B_OK) {
|
||||
buffer_clone_info info;
|
||||
|
||||
uint8* bits = (uint8*)fBitmap[i]->Bits();
|
||||
info.area = area_for(bits);
|
||||
area_info bitmapAreaInfo;
|
||||
status = get_area_info(info.area, &bitmapAreaInfo);
|
||||
if (status != B_OK) {
|
||||
fprintf(stderr, "VideoConsumer::CreateBuffers() - "
|
||||
"get_area_info(): %s\n", strerror(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
//printf("area info for bitmap %ld (%p):\n", i, fBitmap[i]->Bits());
|
||||
//printf(" area: %ld\n", bitmapAreaInfo.area);
|
||||
//printf(" size: %ld\n", bitmapAreaInfo.size);
|
||||
//printf(" lock: %ld\n", bitmapAreaInfo.lock);
|
||||
//printf(" protection: %ld\n", bitmapAreaInfo.protection);
|
||||
//printf(" ram size: %ld\n", bitmapAreaInfo.ram_size);
|
||||
//printf(" copy_count: %ld\n", bitmapAreaInfo.copy_count);
|
||||
//printf(" out_count: %ld\n", bitmapAreaInfo.out_count);
|
||||
//printf(" address: %p\n", bitmapAreaInfo.address);
|
||||
|
||||
info.offset = bits - (uint8*)bitmapAreaInfo.address;
|
||||
info.size = (size_t)fBitmap[i]->BitsLength();
|
||||
info.flags = 0;
|
||||
info.buffer = 0;
|
||||
|
||||
BBuffer *buffer = NULL;
|
||||
if ((status = fBuffers->AddBuffer(info, &buffer)) != B_OK) {
|
||||
ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER TO GROUP\n");
|
||||
return status;
|
||||
} else
|
||||
PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD BUFFER TO GROUP\n");
|
||||
} else {
|
||||
ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING BUFFER: %s\n", strerror(status));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
BBuffer** buffList = new BBuffer*[kBufferCount];
|
||||
for (uint32 i = 0; i < kBufferCount; i++)
|
||||
buffList[i] = 0;
|
||||
|
||||
if ((status = fBuffers->GetBufferList(kBufferCount, buffList)) == B_OK) {
|
||||
for (uint32 i = 0; i < kBufferCount; i++) {
|
||||
if (buffList[i] != NULL) {
|
||||
fBufferMap[i] = (uint32)buffList[i];
|
||||
PROGRESS(" i = %lu buffer = %08lx\n", i, fBufferMap[i]);
|
||||
} else {
|
||||
ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
} else
|
||||
ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n");
|
||||
|
||||
FUNCTION("VideoConsumer::CreateBuffers - EXIT\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoConsumer::DeleteBuffers()
|
||||
{
|
||||
FUNCTION("VideoConsumer::DeleteBuffers\n");
|
||||
if (fBuffers) {
|
||||
fTargetLock.Lock();
|
||||
if (fTargetBufferIndex >= 0) {
|
||||
if (fTarget)
|
||||
fTarget->SetBitmap(NULL);
|
||||
fTargetBufferIndex = -1;
|
||||
}
|
||||
fTargetLock.Unlock();
|
||||
|
||||
delete fBuffers;
|
||||
fBuffers = NULL;
|
||||
|
||||
for (uint32 i = 0; i < kBufferCount; i++) {
|
||||
snooze(20000);
|
||||
delete fBitmap[i];
|
||||
fBitmap[i] = NULL;
|
||||
}
|
||||
}
|
||||
FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoConsumer::SetTarget(VideoTarget* target)
|
||||
{
|
||||
fTargetLock.Lock();
|
||||
if (fTarget)
|
||||
fTarget->SetBitmap(NULL);
|
||||
fTarget = target;
|
||||
if (fTarget && fTargetBufferIndex >= 0)
|
||||
fTarget->SetBitmap(fBitmap[fTargetBufferIndex]);
|
||||
fTargetLock.Unlock();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoConsumer::Connected(const media_source& producer,
|
||||
const media_destination& where, const media_format& format,
|
||||
media_input* outInput)
|
||||
{
|
||||
FUNCTION("VideoConsumer::Connected\n");
|
||||
|
||||
fIn.source = producer;
|
||||
fIn.format = format;
|
||||
fIn.node = Node();
|
||||
sprintf(fIn.name, "Video Consumer");
|
||||
*outInput = fIn;
|
||||
|
||||
uint32 user_data = 0;
|
||||
int32 change_tag = 1;
|
||||
if (CreateBuffers(format) == B_OK) {
|
||||
BBufferConsumer::SetOutputBuffersFor(producer, fDestination,
|
||||
fBuffers, (void *)&user_data,
|
||||
&change_tag, true);
|
||||
fIn.format.u.raw_video.display.bytes_per_row = fBitmap[0]->BytesPerRow();
|
||||
} else {
|
||||
ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
*outInput = fIn;
|
||||
// bytes per row might have changed
|
||||
fConnectionActive = true;
|
||||
|
||||
FUNCTION("VideoConsumer::Connected - EXIT\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoConsumer::Disconnected(const media_source& producer,
|
||||
const media_destination& where)
|
||||
{
|
||||
FUNCTION("VideoConsumer::Disconnected\n");
|
||||
|
||||
if (where != fIn.destination || producer != fIn.source)
|
||||
return;
|
||||
|
||||
// reclaim our buffers
|
||||
int32 changeTag = 0;
|
||||
BBufferConsumer::SetOutputBuffersFor(producer, fDestination, NULL,
|
||||
NULL, &changeTag, false);
|
||||
if (fOurBuffers) {
|
||||
status_t reclaimError = fBuffers->ReclaimAllBuffers();
|
||||
if (reclaimError != B_OK) {
|
||||
fprintf(stderr, "VideoConsumer::Disconnected() - Failed to "
|
||||
"reclaim our buffers: %s\n", strerror(reclaimError));
|
||||
}
|
||||
}
|
||||
// disconnect the connection
|
||||
fIn.source = media_source::null;
|
||||
fConnectionActive = false;
|
||||
|
||||
// Unset the target's bitmap. Just to be safe -- since it is usually
|
||||
// done when the stop event arrives, but someone may disonnect
|
||||
// without stopping us before.
|
||||
fTargetLock.Lock();
|
||||
if (fTargetBufferIndex >= 0) {
|
||||
if (fTarget)
|
||||
fTarget->SetBitmap(NULL);
|
||||
if (fOurBuffers)
|
||||
((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle();
|
||||
fTargetBufferIndex = -1;
|
||||
}
|
||||
fTargetLock.Unlock();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format)
|
||||
{
|
||||
FUNCTION("VideoConsumer::AcceptFormat\n");
|
||||
|
||||
if (dest != fIn.destination) {
|
||||
ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n");
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
}
|
||||
|
||||
if (format->type == B_MEDIA_NO_TYPE)
|
||||
format->type = B_MEDIA_RAW_VIDEO;
|
||||
|
||||
if (format->type != B_MEDIA_RAW_VIDEO) {
|
||||
ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n");
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
|
||||
if (format->u.raw_video.display.format != B_YCbCr444 &&
|
||||
format->u.raw_video.display.format != B_YCbCr422 &&
|
||||
format->u.raw_video.display.format != B_RGB32 &&
|
||||
format->u.raw_video.display.format != B_RGB16 &&
|
||||
format->u.raw_video.display.format != B_RGB15 &&
|
||||
format->u.raw_video.display.format != B_GRAY8 &&
|
||||
format->u.raw_video.display.format
|
||||
!= media_raw_video_format::wildcard.display.format) {
|
||||
ERROR("AcceptFormat - not a format we know about!\n");
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
|
||||
char string[256];
|
||||
string[0] = 0;
|
||||
string_for_format(*format, string, 256);
|
||||
FUNCTION("VideoConsumer::AcceptFormat: %s\n", string);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoConsumer::GetNextInput(int32* cookie, media_input* outInput)
|
||||
{
|
||||
FUNCTION("VideoConsumer::GetNextInput\n");
|
||||
|
||||
// custom build a destination for this connection
|
||||
// put connection number in id
|
||||
|
||||
if (*cookie < 1) {
|
||||
fIn.node = Node();
|
||||
fIn.destination.id = *cookie;
|
||||
sprintf(fIn.name, "Video Consumer");
|
||||
*outInput = fIn;
|
||||
(*cookie)++;
|
||||
return B_OK;
|
||||
} else {
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoConsumer::DisposeInputCookie(int32 /*cookie*/)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoConsumer::GetLatencyFor(
|
||||
const media_destination &for_whom,
|
||||
bigtime_t * out_latency,
|
||||
media_node_id * out_timesource)
|
||||
{
|
||||
FUNCTION("VideoConsumer::GetLatencyFor\n");
|
||||
|
||||
if (for_whom != fIn.destination)
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
|
||||
*out_latency = fMyLatency;
|
||||
*out_timesource = TimeSource()->ID();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoConsumer::FormatChanged(const media_source& producer,
|
||||
const media_destination& consumer, int32 fromChangeCount,
|
||||
const media_format& format)
|
||||
{
|
||||
FUNCTION("VideoConsumer::FormatChanged\n");
|
||||
|
||||
if (consumer != fIn.destination)
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
|
||||
if (producer != fIn.source)
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
|
||||
fIn.format = format;
|
||||
|
||||
return CreateBuffers(format);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
|
||||
bool realTimeEvent)
|
||||
{
|
||||
LOOP("VideoConsumer::HandleEvent\n");
|
||||
|
||||
BBuffer* buffer;
|
||||
|
||||
switch (event->type) {
|
||||
case BTimedEventQueue::B_START:
|
||||
PROGRESS("VideoConsumer::HandleEvent - START\n");
|
||||
break;
|
||||
|
||||
case BTimedEventQueue::B_STOP:
|
||||
PROGRESS("VideoConsumer::HandleEvent - STOP\n");
|
||||
EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
|
||||
// unset the target's bitmap
|
||||
fTargetLock.Lock();
|
||||
if (fTargetBufferIndex >= 0) {
|
||||
if (fTarget)
|
||||
fTarget->SetBitmap(NULL);
|
||||
if (fOurBuffers)
|
||||
((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle();
|
||||
fTargetBufferIndex = -1;
|
||||
}
|
||||
fTargetLock.Unlock();
|
||||
break;
|
||||
|
||||
case BTimedEventQueue::B_HANDLE_BUFFER:
|
||||
LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n");
|
||||
buffer = (BBuffer *) event->pointer;
|
||||
|
||||
if (RunState() == B_STARTED && fConnectionActive) {
|
||||
// see if this is one of our buffers
|
||||
uint32 index = 0;
|
||||
fOurBuffers = true;
|
||||
while (index < kBufferCount) {
|
||||
if ((uint32)buffer == fBufferMap[index])
|
||||
break;
|
||||
else
|
||||
index++;
|
||||
}
|
||||
if (index == kBufferCount) {
|
||||
// no, buffers belong to consumer
|
||||
fOurBuffers = false;
|
||||
index = (fTargetBufferIndex + 1) % kBufferCount;
|
||||
}
|
||||
|
||||
bool dropped = false;
|
||||
bool recycle = true;
|
||||
if ((RunMode() == B_OFFLINE)
|
||||
// TODO: Fix the runmode stuff! Setting the consumer to B_OFFLINE does
|
||||
// not do the trick. I made the VideoConsumer check the performance
|
||||
// time of the buffer and if it is 0, it plays it regardless.
|
||||
|| (buffer->Header()->start_time == 2 * fMyLatency + SchedulingLatency())
|
||||
|| (/*(TimeSource()->Now()
|
||||
> (buffer->Header()->start_time - JITTER)) &&*/
|
||||
(TimeSource()->Now() < (buffer->Header()->start_time
|
||||
+ JITTER)))) {
|
||||
if (!fOurBuffers) {
|
||||
memcpy(fBitmap[index]->Bits(), buffer->Data(),
|
||||
fBitmap[index]->BitsLength());
|
||||
}
|
||||
|
||||
fTargetLock.Lock();
|
||||
if (fTarget) {
|
||||
fTarget->SetBitmap(fBitmap[index]);
|
||||
if (fOurBuffers) {
|
||||
// recycle the previous but not the current buffer
|
||||
if (fTargetBufferIndex >= 0) {
|
||||
((BBuffer*)fBufferMap[fTargetBufferIndex])
|
||||
->Recycle();
|
||||
}
|
||||
recycle = false;
|
||||
}
|
||||
fTargetBufferIndex = index;
|
||||
}
|
||||
fTargetLock.Unlock();
|
||||
} else {
|
||||
dropped = true;
|
||||
PROGRESS("VideoConsumer::HandleEvent - DROPPED FRAME\n"
|
||||
" start_time: %lld, current: %lld, latency: %lld\n",
|
||||
buffer->Header()->start_time, TimeSource()->Now(),
|
||||
SchedulingLatency());
|
||||
}
|
||||
if (dropped) {
|
||||
if (fManager->LockWithTimeout(10000) == B_OK) {
|
||||
fManager->FrameDropped();
|
||||
fManager->Unlock();
|
||||
}
|
||||
}
|
||||
if (recycle)
|
||||
buffer->Recycle();
|
||||
} else {
|
||||
TRACE("RunState() != B_STARTED\n");
|
||||
buffer->Recycle();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ERROR("VideoConsumer::HandleEvent - BAD EVENT\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
|
||||
* Distributed under the terms of the Be Sample Code license.
|
||||
*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef VIDEO_CONSUMER_H
|
||||
#define VIDEO_CONSUMER_H
|
||||
|
||||
|
||||
#include <BufferConsumer.h>
|
||||
#include <Locker.h>
|
||||
#include <MediaEventLooper.h>
|
||||
|
||||
|
||||
class BBitmap;
|
||||
class NodeManager;
|
||||
class VideoTarget;
|
||||
|
||||
|
||||
// TODO: The buffer count should depend on the latency!
|
||||
static const unsigned int kBufferCount = 4;
|
||||
|
||||
|
||||
class VideoConsumer : public BMediaEventLooper, public BBufferConsumer {
|
||||
public:
|
||||
VideoConsumer(
|
||||
const char* name,
|
||||
BMediaAddOn* addon,
|
||||
const uint32 internal_id,
|
||||
NodeManager* manager,
|
||||
VideoTarget* target);
|
||||
~VideoConsumer();
|
||||
|
||||
// BMediaNode interface
|
||||
public:
|
||||
|
||||
virtual BMediaAddOn* AddOn(long* cookie) const;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void Start(bigtime_t performanceTime);
|
||||
virtual void Stop(bigtime_t performanceTime,
|
||||
bool immediate);
|
||||
virtual void Seek(bigtime_t mediaTime,
|
||||
bigtime_t performanceTime);
|
||||
virtual void TimeWarp(bigtime_t atRealTime,
|
||||
bigtime_t toPerformanceTime);
|
||||
|
||||
virtual void NodeRegistered();
|
||||
virtual status_t RequestCompleted(
|
||||
const media_request_info& info);
|
||||
|
||||
virtual status_t HandleMessage(int32 message, const void* data,
|
||||
size_t size);
|
||||
|
||||
// BMediaEventLooper interface
|
||||
protected:
|
||||
virtual void HandleEvent(const media_timed_event* event,
|
||||
bigtime_t lateness, bool realTimeEvent);
|
||||
|
||||
// BBufferConsumer interface
|
||||
public:
|
||||
virtual status_t AcceptFormat(const media_destination& dest,
|
||||
media_format* format);
|
||||
virtual status_t GetNextInput(int32* cookie,
|
||||
media_input* _input);
|
||||
|
||||
virtual void DisposeInputCookie(int32 cookie);
|
||||
|
||||
protected:
|
||||
virtual void BufferReceived(BBuffer* buffer);
|
||||
|
||||
private:
|
||||
virtual void ProducerDataStatus(
|
||||
const media_destination& forWhom,
|
||||
int32 status,
|
||||
bigtime_t atMediaTime);
|
||||
virtual status_t GetLatencyFor(
|
||||
const media_destination& forWhom,
|
||||
bigtime_t* outLatency,
|
||||
media_node_id* outId);
|
||||
virtual status_t Connected(const media_source& producer,
|
||||
const media_destination& where,
|
||||
const media_format& withFormat,
|
||||
media_input* outInput);
|
||||
virtual void Disconnected(const media_source& producer,
|
||||
const media_destination& where);
|
||||
virtual status_t FormatChanged(const media_source& producer,
|
||||
const media_destination& consumer,
|
||||
int32 from_change_count,
|
||||
const media_format& format);
|
||||
|
||||
// VideoConsumer
|
||||
public:
|
||||
status_t CreateBuffers(
|
||||
const media_format& withFormat);
|
||||
|
||||
void DeleteBuffers();
|
||||
|
||||
void SetTarget(VideoTarget* target);
|
||||
|
||||
private:
|
||||
uint32 fInternalID;
|
||||
BMediaAddOn* fAddOn;
|
||||
|
||||
bool fConnectionActive;
|
||||
media_input fIn;
|
||||
media_destination fDestination;
|
||||
bigtime_t fMyLatency;
|
||||
|
||||
BBitmap* fBitmap[kBufferCount];
|
||||
bool fOurBuffers;
|
||||
BBufferGroup* fBuffers;
|
||||
uint32 fBufferMap[kBufferCount];
|
||||
|
||||
NodeManager* fManager;
|
||||
BLocker fTargetLock; // locks the following variable
|
||||
VideoTarget* volatile fTarget;
|
||||
int32 fTargetBufferIndex;
|
||||
};
|
||||
|
||||
#endif // VIDEO_CONSUMER_H
|
||||
@@ -0,0 +1,851 @@
|
||||
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
|
||||
* Distributed under the terms of the Be Sample Code license.
|
||||
*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include "VideoProducer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Autolock.h>
|
||||
#include <Buffer.h>
|
||||
#include <BufferGroup.h>
|
||||
#include <TimeSource.h>
|
||||
|
||||
#include "NodeManager.h"
|
||||
#include "VideoSupplier.h"
|
||||
|
||||
|
||||
// debugging
|
||||
//#define TRACE_VIDEO_PRODUCER
|
||||
#ifdef TRACE_VIDEO_PRODUCER
|
||||
# define TRACE(x...) printf("VideoProducer::"); printf(x)
|
||||
# define FUNCTION(x...) TRACE(x)
|
||||
# define ERROR(x...) fprintf(stderr, "VideoProducer::"); fprintf(stderr, x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
# define FUNCTION(x...)
|
||||
# define ERROR(x...) fprintf(stderr, "VideoProducer::"); fprintf(stderr, x)
|
||||
#endif
|
||||
|
||||
|
||||
#define BUFFER_COUNT 3
|
||||
|
||||
#define TOUCH(x) ((void)(x))
|
||||
|
||||
|
||||
VideoProducer::VideoProducer(BMediaAddOn* addon, const char* name,
|
||||
int32 internalId, NodeManager* manager, VideoSupplier* supplier)
|
||||
: BMediaNode(name),
|
||||
BMediaEventLooper(),
|
||||
BBufferProducer(B_MEDIA_RAW_VIDEO),
|
||||
fInitStatus(B_NO_INIT),
|
||||
fInternalID(internalId),
|
||||
fAddOn(addon),
|
||||
fBufferGroup(NULL),
|
||||
fUsedBufferGroup(NULL),
|
||||
fThread(-1),
|
||||
fFrameSync(-1),
|
||||
fRunning(false),
|
||||
fConnected(false),
|
||||
fEnabled(false),
|
||||
fManager(manager),
|
||||
fSupplier(supplier)
|
||||
{
|
||||
fOutput.destination = media_destination::null;
|
||||
fInitStatus = B_OK;
|
||||
}
|
||||
|
||||
|
||||
VideoProducer::~VideoProducer()
|
||||
{
|
||||
if (fInitStatus == B_OK) {
|
||||
// Clean up after ourselves, in case the application didn't make us
|
||||
// do so.
|
||||
if (fConnected)
|
||||
Disconnect(fOutput.source, fOutput.destination);
|
||||
if (fRunning)
|
||||
_HandleStop();
|
||||
}
|
||||
Quit();
|
||||
}
|
||||
|
||||
|
||||
port_id
|
||||
VideoProducer::ControlPort() const
|
||||
{
|
||||
return BMediaNode::ControlPort();
|
||||
}
|
||||
|
||||
|
||||
BMediaAddOn*
|
||||
VideoProducer::AddOn(int32* _internalId) const
|
||||
{
|
||||
if (_internalId)
|
||||
*_internalId = fInternalID;
|
||||
return fAddOn;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::HandleMessage(int32 message, const void* data, size_t size)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::SetTimeSource(BTimeSource* timeSource)
|
||||
{
|
||||
// Tell frame generation thread to recalculate delay value
|
||||
release_sem(fFrameSync);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::RequestCompleted(const media_request_info& info)
|
||||
{
|
||||
return BMediaNode::RequestCompleted(info);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::NodeRegistered()
|
||||
{
|
||||
if (fInitStatus != B_OK) {
|
||||
ReportError(B_NODE_IN_DISTRESS);
|
||||
return;
|
||||
}
|
||||
|
||||
fOutput.node = Node();
|
||||
fOutput.source.port = ControlPort();
|
||||
fOutput.source.id = 0;
|
||||
fOutput.destination = media_destination::null;
|
||||
strcpy(fOutput.name, Name());
|
||||
|
||||
// fill with wild cards at this point in time
|
||||
fOutput.format.type = B_MEDIA_RAW_VIDEO;
|
||||
fOutput.format.u.raw_video = media_raw_video_format::wildcard;
|
||||
fOutput.format.u.raw_video.interlace = 1;
|
||||
fOutput.format.u.raw_video.display.format = B_NO_COLOR_SPACE;
|
||||
fOutput.format.u.raw_video.display.bytes_per_row = 0;
|
||||
fOutput.format.u.raw_video.display.line_width = 0;
|
||||
fOutput.format.u.raw_video.display.line_count = 0;
|
||||
|
||||
// start the BMediaEventLooper control loop running
|
||||
Run();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::Start(bigtime_t performanceTime)
|
||||
{
|
||||
// notify the manager in case we were started from the outside world
|
||||
// fManager->StartPlaying();
|
||||
|
||||
BMediaEventLooper::Start(performanceTime);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::Stop(bigtime_t performanceTime, bool immediate)
|
||||
{
|
||||
// notify the manager in case we were stopped from the outside world
|
||||
// fManager->StopPlaying();
|
||||
|
||||
BMediaEventLooper::Stop(performanceTime, immediate);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::Seek(bigtime_t media_time, bigtime_t performanceTime)
|
||||
{
|
||||
BMediaEventLooper::Seek(media_time, performanceTime);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
|
||||
{
|
||||
BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
|
||||
{
|
||||
return BMediaEventLooper::AddTimer(at_performance_time, cookie);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::SetRunMode(run_mode mode)
|
||||
{
|
||||
printf("VideoProducer::SetRunMode(%d)\n", mode);
|
||||
TRACE("SetRunMode(%d)\n", mode);
|
||||
BMediaEventLooper::SetRunMode(mode);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::HandleEvent(const media_timed_event* event,
|
||||
bigtime_t lateness, bool realTimeEvent)
|
||||
{
|
||||
TOUCH(lateness); TOUCH(realTimeEvent);
|
||||
|
||||
switch(event->type) {
|
||||
case BTimedEventQueue::B_START:
|
||||
_HandleStart(event->event_time);
|
||||
break;
|
||||
case BTimedEventQueue::B_STOP:
|
||||
_HandleStop();
|
||||
break;
|
||||
case BTimedEventQueue::B_WARP:
|
||||
_HandleTimeWarp(event->bigdata);
|
||||
break;
|
||||
case BTimedEventQueue::B_SEEK:
|
||||
_HandleSeek(event->bigdata);
|
||||
break;
|
||||
case BTimedEventQueue::B_HANDLE_BUFFER:
|
||||
case BTimedEventQueue::B_DATA_STATUS:
|
||||
case BTimedEventQueue::B_PARAMETER:
|
||||
default:
|
||||
TRACE("HandleEvent: Unhandled event -- %lx\n", event->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::CleanUpEvent(const media_timed_event *event)
|
||||
{
|
||||
BMediaEventLooper::CleanUpEvent(event);
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
VideoProducer::OfflineTime()
|
||||
{
|
||||
return BMediaEventLooper::OfflineTime();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::ControlLoop()
|
||||
{
|
||||
BMediaEventLooper::ControlLoop();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::DeleteHook(BMediaNode* node)
|
||||
{
|
||||
return BMediaEventLooper::DeleteHook(node);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::FormatSuggestionRequested(media_type type, int32 quality,
|
||||
media_format* _format)
|
||||
{
|
||||
FUNCTION("FormatSuggestionRequested\n");
|
||||
|
||||
if (type != B_MEDIA_ENCODED_VIDEO)
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
|
||||
TOUCH(quality);
|
||||
|
||||
*_format = fOutput.format;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::FormatProposal(const media_source& output, media_format* format)
|
||||
{
|
||||
#ifdef TRACE_VIDEO_PRODUCER
|
||||
char string[256];
|
||||
string_for_format(*format, string, 256);
|
||||
FUNCTION("FormatProposal(%s)\n", string);
|
||||
#endif
|
||||
|
||||
if (!format)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (output != fOutput.source)
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
|
||||
status_t ret = format_is_compatible(*format, fOutput.format) ?
|
||||
B_OK : B_MEDIA_BAD_FORMAT;
|
||||
if (ret != B_OK)
|
||||
ERROR("FormatProposal() error\n");
|
||||
// change any wild cards to specific values
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::FormatChangeRequested(const media_source& source,
|
||||
const media_destination& destination, media_format* ioFormat,
|
||||
int32 *_deprecated_)
|
||||
{
|
||||
TOUCH(destination); TOUCH(ioFormat); TOUCH(_deprecated_);
|
||||
|
||||
if (source != fOutput.source)
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::GetNextOutput(int32* cookie, media_output* outOutput)
|
||||
{
|
||||
if (!outOutput)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if ((*cookie) != 0)
|
||||
return B_BAD_INDEX;
|
||||
|
||||
*outOutput = fOutput;
|
||||
(*cookie)++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::DisposeOutputCookie(int32 cookie)
|
||||
{
|
||||
TOUCH(cookie);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::SetBufferGroup(const media_source& forSource,
|
||||
BBufferGroup *group)
|
||||
{
|
||||
if (forSource != fOutput.source)
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
|
||||
TRACE("VideoProducer::SetBufferGroup() - using buffer group of "
|
||||
"consumer.\n");
|
||||
fUsedBufferGroup = group;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::VideoClippingChanged(const media_source& forSource,
|
||||
int16 numShorts, int16* clipData, const media_video_display_info& display,
|
||||
int32* _deprecated_)
|
||||
{
|
||||
TOUCH(forSource); TOUCH(numShorts); TOUCH(clipData);
|
||||
TOUCH(display); TOUCH(_deprecated_);
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::GetLatency(bigtime_t* _latency)
|
||||
{
|
||||
if (!_latency)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
*_latency = EventLatency() + SchedulingLatency();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::PrepareToConnect(const media_source& source,
|
||||
const media_destination& destination, media_format* format,
|
||||
media_source* outSource, char* outName)
|
||||
{
|
||||
FUNCTION("PrepareToConnect() %ldx%ld\n",
|
||||
format->u.raw_video.display.line_width,
|
||||
format->u.raw_video.display.line_count);
|
||||
|
||||
if (fConnected) {
|
||||
ERROR("PrepareToConnect() - already connected!\n");
|
||||
return B_MEDIA_ALREADY_CONNECTED;
|
||||
}
|
||||
|
||||
if (source != fOutput.source)
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
|
||||
if (fOutput.destination != media_destination::null) {
|
||||
ERROR("PrepareToConnect() - destination != null.\n");
|
||||
return B_MEDIA_ALREADY_CONNECTED;
|
||||
}
|
||||
|
||||
// The format parameter comes in with the suggested format, and may be
|
||||
// specialized as desired by the node
|
||||
if (!format_is_compatible(*format, fOutput.format)) {
|
||||
ERROR("PrepareToConnect() - incompatible format.\n");
|
||||
*format = fOutput.format;
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
|
||||
if (format->u.raw_video.display.line_width == 0)
|
||||
format->u.raw_video.display.line_width = 384;
|
||||
if (format->u.raw_video.display.line_count == 0)
|
||||
format->u.raw_video.display.line_count = 288;
|
||||
if (format->u.raw_video.field_rate == 0)
|
||||
format->u.raw_video.field_rate = 25.0;
|
||||
if (format->u.raw_video.display.bytes_per_row == 0)
|
||||
format->u.raw_video.display.bytes_per_row = format->u.raw_video.display.line_width * 4;
|
||||
|
||||
*outSource = fOutput.source;
|
||||
strcpy(outName, fOutput.name);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
#define NODE_LATENCY 20000
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::Connect(status_t error, const media_source& source,
|
||||
const media_destination& destination, const media_format& format,
|
||||
char* _name)
|
||||
{
|
||||
FUNCTION("Connect() %ldx%ld\n",
|
||||
format.u.raw_video.display.line_width,
|
||||
format.u.raw_video.display.line_count);
|
||||
|
||||
if (fConnected) {
|
||||
ERROR("Connect() - already connected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (source != fOutput.source) {
|
||||
ERROR("Connect() - wrong source.\n");
|
||||
return;
|
||||
}
|
||||
if (error < B_OK) {
|
||||
ERROR("Connect() - consumer error: %s\n", strerror(error));
|
||||
return;
|
||||
}
|
||||
if (!const_cast<media_format*>(&format)->Matches(&fOutput.format)) {
|
||||
ERROR("Connect() - format mismatch.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fOutput.destination = destination;
|
||||
strcpy(_name, fOutput.name);
|
||||
|
||||
if (fOutput.format.u.raw_video.field_rate != 0.0f) {
|
||||
fPerformanceTimeBase = fPerformanceTimeBase
|
||||
+ (bigtime_t)((fFrame - fFrameBase)
|
||||
* 1000000 / fOutput.format.u.raw_video.field_rate);
|
||||
fFrameBase = fFrame;
|
||||
}
|
||||
|
||||
fConnectedFormat = format.u.raw_video;
|
||||
if (fConnectedFormat.display.bytes_per_row == 0) {
|
||||
ERROR("Connect() - connected format still has BPR wildcard!\n");
|
||||
fConnectedFormat.display.bytes_per_row
|
||||
= 4 * fConnectedFormat.display.line_width;
|
||||
}
|
||||
|
||||
// get the latency
|
||||
bigtime_t latency = 0;
|
||||
media_node_id tsID = 0;
|
||||
FindLatencyFor(fOutput.destination, &latency, &tsID);
|
||||
SetEventLatency(latency + NODE_LATENCY);
|
||||
|
||||
// Create the buffer group
|
||||
if (!fUsedBufferGroup) {
|
||||
fBufferGroup = new BBufferGroup(fConnectedFormat.display.bytes_per_row
|
||||
* fConnectedFormat.display.line_count, BUFFER_COUNT);
|
||||
status_t err = fBufferGroup->InitCheck();
|
||||
if (err < B_OK) {
|
||||
delete fBufferGroup;
|
||||
fBufferGroup = NULL;
|
||||
ERROR("Connect() - buffer group error: %s\n", strerror(err));
|
||||
return;
|
||||
}
|
||||
fUsedBufferGroup = fBufferGroup;
|
||||
}
|
||||
|
||||
fConnected = true;
|
||||
fEnabled = true;
|
||||
|
||||
// Tell frame generation thread to recalculate delay value
|
||||
release_sem(fFrameSync);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::Disconnect(const media_source& source,
|
||||
const media_destination& destination)
|
||||
{
|
||||
FUNCTION("Disconnect()\n");
|
||||
|
||||
if (!fConnected) {
|
||||
ERROR("Disconnect() - Not connected\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((source != fOutput.source) || (destination != fOutput.destination)) {
|
||||
ERROR("Disconnect() - Bad source and/or destination\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fEnabled = false;
|
||||
fOutput.destination = media_destination::null;
|
||||
|
||||
if (fLock.Lock()) {
|
||||
// Always delete the buffer group, even if it is not ours.
|
||||
// (See BeBook::SetBufferGroup()).
|
||||
delete fUsedBufferGroup;
|
||||
fUsedBufferGroup = NULL;
|
||||
fBufferGroup = NULL;
|
||||
fLock.Unlock();
|
||||
}
|
||||
|
||||
fConnected = false;
|
||||
TRACE("Disconnect() done\n");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::LateNoticeReceived(const media_source &source,
|
||||
bigtime_t how_much, bigtime_t performanceTime)
|
||||
{
|
||||
TOUCH(source); TOUCH(how_much); TOUCH(performanceTime);
|
||||
TRACE("Late!!!\n");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::EnableOutput(const media_source& source, bool enabled,
|
||||
int32* _deprecated_)
|
||||
{
|
||||
TOUCH(_deprecated_);
|
||||
|
||||
if (source != fOutput.source)
|
||||
return;
|
||||
|
||||
fEnabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VideoProducer::SetPlayRate(int32 numer, int32 denom)
|
||||
{
|
||||
TOUCH(numer); TOUCH(denom);
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::AdditionalBufferRequested(const media_source& source,
|
||||
media_buffer_id prevBuffer, bigtime_t prevTime,
|
||||
const media_seek_tag* prevTag)
|
||||
{
|
||||
TOUCH(source); TOUCH(prevBuffer); TOUCH(prevTime); TOUCH(prevTag);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::LatencyChanged(const media_source& source,
|
||||
const media_destination& destination,
|
||||
bigtime_t newLatency, uint32 flags)
|
||||
{
|
||||
TOUCH(source); TOUCH(destination); TOUCH(newLatency); TOUCH(flags);
|
||||
TRACE("Latency changed!\n");
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::_HandleStart(bigtime_t performanceTime)
|
||||
{
|
||||
// Start producing frames, even if the output hasn't been connected yet.
|
||||
TRACE("_HandleStart(%Ld)\n", performanceTime);
|
||||
|
||||
if (fRunning) {
|
||||
TRACE("_HandleStart: Node already started\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fFrame = 0;
|
||||
fFrameBase = 0;
|
||||
fPerformanceTimeBase = performanceTime;
|
||||
|
||||
fFrameSync = create_sem(0, "frame synchronization");
|
||||
if (fFrameSync < B_OK)
|
||||
return;
|
||||
|
||||
fThread = spawn_thread(_FrameGeneratorThreadEntry, "frame generator",
|
||||
B_NORMAL_PRIORITY, this);
|
||||
if (fThread < B_OK) {
|
||||
delete_sem(fFrameSync);
|
||||
return;
|
||||
}
|
||||
|
||||
resume_thread(fThread);
|
||||
fRunning = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::_HandleStop()
|
||||
{
|
||||
TRACE("_HandleStop()\n");
|
||||
|
||||
if (!fRunning) {
|
||||
TRACE("_HandleStop: Node isn't running\n");
|
||||
return;
|
||||
}
|
||||
|
||||
delete_sem(fFrameSync);
|
||||
wait_for_thread(fThread, &fThread);
|
||||
|
||||
fRunning = false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::_HandleTimeWarp(bigtime_t performanceTime)
|
||||
{
|
||||
fPerformanceTimeBase = performanceTime;
|
||||
fFrameBase = fFrame;
|
||||
|
||||
// Tell frame generation thread to recalculate delay value
|
||||
release_sem(fFrameSync);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProducer::_HandleSeek(bigtime_t performanceTime)
|
||||
{
|
||||
fPerformanceTimeBase = performanceTime;
|
||||
fFrameBase = fFrame;
|
||||
|
||||
// Tell frame generation thread to recalculate delay value
|
||||
release_sem(fFrameSync);
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
VideoProducer::_FrameGeneratorThreadEntry(void* data)
|
||||
{
|
||||
return ((VideoProducer*)data)->_FrameGeneratorThread();
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
VideoProducer::_FrameGeneratorThread()
|
||||
{
|
||||
bool forceSendingBuffer = true;
|
||||
bigtime_t lastFrameSentAt = 0;
|
||||
int64 lastPlaylistFrame = 0;
|
||||
bool running = true;
|
||||
while (running) {
|
||||
TRACE("_FrameGeneratorThread: loop: %Ld\n", fFrame);
|
||||
// lock the node manager
|
||||
status_t err = fManager->LockWithTimeout(10000);
|
||||
bool ignoreEvent = false;
|
||||
// Data to be retrieved from the node manager.
|
||||
bigtime_t performanceTime = 0;
|
||||
bigtime_t nextPerformanceTime = 0;
|
||||
bigtime_t waitUntil = 0;
|
||||
bigtime_t nextWaitUntil = 0;
|
||||
bigtime_t maxRenderTime = 0;
|
||||
int32 playingDirection = 0;
|
||||
int32 playingMode = 0;
|
||||
int64 playlistFrame = 0;
|
||||
switch (err) {
|
||||
case B_OK: {
|
||||
TRACE("_FrameGeneratorThread: node manager successfully "
|
||||
"locked\n");
|
||||
// get the times for the current and the next frame
|
||||
performanceTime = fManager->TimeForFrame(fFrame);
|
||||
nextPerformanceTime = fManager->TimeForFrame(fFrame + 1);
|
||||
maxRenderTime = min_c(bigtime_t(33334 * 0.9),
|
||||
max_c(fSupplier->ProcessingLatency(), maxRenderTime));
|
||||
playingMode = fManager->PlayModeAtFrame(fFrame);
|
||||
|
||||
waitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase
|
||||
+ performanceTime, 0) - maxRenderTime;
|
||||
nextWaitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase
|
||||
+ nextPerformanceTime, 0) - maxRenderTime;
|
||||
// get playing direction and playlist frame for the current
|
||||
// frame
|
||||
bool newPlayingState;
|
||||
playlistFrame = fManager->PlaylistFrameAtFrame(fFrame,
|
||||
playingDirection, newPlayingState);
|
||||
TRACE("_FrameGeneratorThread: performance time: %Ld, "
|
||||
"playlist frame: %lld\n", performanceTime, playlistFrame);
|
||||
forceSendingBuffer |= newPlayingState;
|
||||
if (lastPlaylistFrame != playlistFrame) {
|
||||
forceSendingBuffer = true;
|
||||
lastPlaylistFrame = playlistFrame;
|
||||
}
|
||||
fManager->SetCurrentVideoTime(nextPerformanceTime);
|
||||
fManager->Unlock();
|
||||
break;
|
||||
}
|
||||
case B_TIMED_OUT:
|
||||
TRACE("_FrameGeneratorThread: Couldn't lock the node "
|
||||
"manager.\n");
|
||||
ignoreEvent = true;
|
||||
waitUntil = system_time() - 1;
|
||||
break;
|
||||
default:
|
||||
ERROR("_FrameGeneratorThread: Couldn't lock the node manager. "
|
||||
"Terminating video producer frame generator thread.\n");
|
||||
TRACE("_FrameGeneratorThread: frame generator thread done.\n");
|
||||
// do not access any member variables, since this could
|
||||
// also me the Node has been deleted
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
TRACE("_FrameGeneratorThread: waiting (%Ld)...\n", waitUntil);
|
||||
// wait until...
|
||||
err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, waitUntil);
|
||||
// The only acceptable responses are B_OK and B_TIMED_OUT. Everything
|
||||
// else means the thread should quit. Deleting the semaphore, as in
|
||||
// VideoProducer::_HandleStop(), will trigger this behavior.
|
||||
switch (err) {
|
||||
case B_OK:
|
||||
TRACE("_FrameGeneratorThread: going back to sleep.\n");
|
||||
break;
|
||||
case B_TIMED_OUT:
|
||||
TRACE("_FrameGeneratorThread: timed out => event\n");
|
||||
// Catch the cases in which the node manager could not be
|
||||
// locked and we therefore have no valid data to work with,
|
||||
// or the producer is not running or enabled.
|
||||
if (ignoreEvent || !fRunning || !fEnabled) {
|
||||
TRACE("_FrameGeneratorThread: ignore event\n");
|
||||
// nothing to do
|
||||
} else if (nextWaitUntil < system_time()) {
|
||||
// Drop frame if it's at least a frame late.
|
||||
//printf("VideoProducer: dropped frame (%ld)\n", fFrame);
|
||||
if (fManager->LockWithTimeout(10000) == B_OK) {
|
||||
fManager->FrameDropped();
|
||||
fManager->Unlock();
|
||||
}
|
||||
// next frame
|
||||
fFrame++;
|
||||
} else if (playingDirection != 0 || forceSendingBuffer) {
|
||||
// Send buffers only, if playing, the node is running and
|
||||
// the output has been enabled
|
||||
TRACE("_FrameGeneratorThread: produce frame\n");
|
||||
BAutolock _(fLock);
|
||||
// Fetch a buffer from the buffer group
|
||||
BBuffer *buffer = fUsedBufferGroup->RequestBuffer(
|
||||
fConnectedFormat.display.bytes_per_row
|
||||
* fConnectedFormat.display.line_count, 0LL);
|
||||
if (buffer) {
|
||||
// Fill out the details about this buffer.
|
||||
media_header *h = buffer->Header();
|
||||
h->type = B_MEDIA_RAW_VIDEO;
|
||||
h->time_source = TimeSource()->ID();
|
||||
h->size_used = fConnectedFormat.display.bytes_per_row
|
||||
* fConnectedFormat.display.line_count;
|
||||
// For a buffer originating from a device, you might
|
||||
// want to calculate this based on the
|
||||
// PerformanceTimeFor the time your buffer arrived at
|
||||
// the hardware (plus any applicable adjustments).
|
||||
h->start_time = fPerformanceTimeBase + performanceTime;
|
||||
// TODO: Fix the runmode stuff! Setting the consumer to B_OFFLINE does
|
||||
// not do the trick. I made the VideoConsumer check the performance
|
||||
// time of the buffer and if it is 0, it plays it regardless.
|
||||
if (playingMode < 0) {
|
||||
h->start_time = 0;
|
||||
}
|
||||
h->file_pos = 0;
|
||||
h->orig_size = 0;
|
||||
h->data_offset = 0;
|
||||
h->u.raw_video.field_gamma = 1.0;
|
||||
h->u.raw_video.field_sequence = fFrame;
|
||||
h->u.raw_video.field_number = 0;
|
||||
h->u.raw_video.pulldown_number = 0;
|
||||
h->u.raw_video.first_active_line = 1;
|
||||
h->u.raw_video.line_count
|
||||
= fConnectedFormat.display.line_count;
|
||||
// Fill in a frame
|
||||
media_format mf;
|
||||
mf.type = B_MEDIA_RAW_VIDEO;
|
||||
mf.u.raw_video = fConnectedFormat;
|
||||
TRACE("_FrameGeneratorThread: frame: %Ld, "
|
||||
"playlistFrame: %Ld\n", fFrame, playlistFrame);
|
||||
bool forceOrWasCached = forceSendingBuffer;
|
||||
|
||||
if (fManager->LockWithTimeout(5000) == B_OK) {
|
||||
// we need to lock the manager, or our
|
||||
// fSupplier might work on bad data
|
||||
err = fSupplier->FillBuffer(playlistFrame,
|
||||
buffer->Data(), &mf, forceOrWasCached);
|
||||
fManager->Unlock();
|
||||
} else {
|
||||
err = B_ERROR;
|
||||
}
|
||||
// clean the buffer if something went wrong
|
||||
if (err != B_OK) {
|
||||
memset(buffer->Data(), 0, h->size_used);
|
||||
err = B_OK;
|
||||
}
|
||||
// Send the buffer on down to the consumer
|
||||
if (SendBuffer(buffer, fOutput.destination) < B_OK) {
|
||||
ERROR("_FrameGeneratorThread: Error "
|
||||
"sending buffer\n");
|
||||
// If there is a problem sending the buffer,
|
||||
// or if we don't send the buffer because its
|
||||
// contents are the same as the last one,
|
||||
// return it to its buffer group.
|
||||
buffer->Recycle();
|
||||
// we tell the supplier to delete
|
||||
// its caches if there was a problem sending
|
||||
// the buffer
|
||||
fSupplier->DeleteCaches();
|
||||
}
|
||||
// Only if everything went fine we clear the flag
|
||||
// that forces us to send a buffer even if not
|
||||
// playing.
|
||||
if (err == B_OK) {
|
||||
forceSendingBuffer = false;
|
||||
lastFrameSentAt = performanceTime;
|
||||
}
|
||||
} else {
|
||||
TRACE("_FrameGeneratorThread: no buffer!\n");
|
||||
// ERROR("_FrameGeneratorThread: no buffer!\n");
|
||||
}
|
||||
// next frame
|
||||
fFrame++;
|
||||
} else {
|
||||
TRACE("_FrameGeneratorThread: not playing\n");
|
||||
// next frame
|
||||
fFrame++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
TRACE("_FrameGeneratorThread: Couldn't acquire semaphore. "
|
||||
"Error: %s\n", strerror(err));
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TRACE("_FrameGeneratorThread: frame generator thread done.\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
|
||||
* Distributed under the terms of the Be Sample Code license.
|
||||
*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef _VIDEO_PRODUCER_H
|
||||
#define _VIDEO_PRODUCER_H
|
||||
|
||||
|
||||
#include <BufferProducer.h>
|
||||
#include <Controllable.h>
|
||||
#include <Locker.h>
|
||||
#include <MediaDefs.h>
|
||||
#include <MediaEventLooper.h>
|
||||
#include <MediaNode.h>
|
||||
#include <OS.h>
|
||||
#include <Rect.h>
|
||||
|
||||
|
||||
class NodeManager;
|
||||
class VideoSupplier;
|
||||
|
||||
|
||||
class VideoProducer : public virtual BMediaEventLooper,
|
||||
public virtual BBufferProducer {
|
||||
public:
|
||||
VideoProducer(BMediaAddOn* addon, const char* name,
|
||||
int32 internalId, NodeManager* manager,
|
||||
VideoSupplier* supplier);
|
||||
virtual ~VideoProducer();
|
||||
|
||||
virtual status_t InitCheck() const
|
||||
{ return fInitStatus; }
|
||||
|
||||
// BMediaNode interface
|
||||
public:
|
||||
virtual port_id ControlPort() const;
|
||||
virtual BMediaAddOn* AddOn(int32* _internalId) const;
|
||||
virtual status_t HandleMessage(int32 message, const void* data,
|
||||
size_t size);
|
||||
protected:
|
||||
virtual void SetTimeSource(BTimeSource* timeSource);
|
||||
virtual status_t RequestCompleted(const media_request_info& info);
|
||||
|
||||
// BMediaEventLooper interface
|
||||
protected:
|
||||
virtual void NodeRegistered();
|
||||
virtual void Start(bigtime_t performanceTime);
|
||||
virtual void Stop(bigtime_t performanceTime, bool immediate);
|
||||
virtual void Seek(bigtime_t mediaTime,
|
||||
bigtime_t performanceTime);
|
||||
virtual void TimeWarp(bigtime_t atRealTime,
|
||||
bigtime_t toPerformanceTime);
|
||||
virtual status_t AddTimer(bigtime_t atPerformanceTime,
|
||||
int32 cookie);
|
||||
virtual void SetRunMode(run_mode mode);
|
||||
virtual void HandleEvent(const media_timed_event* event,
|
||||
bigtime_t lateness,
|
||||
bool realTimeEvent = false);
|
||||
virtual void CleanUpEvent(const media_timed_event* event);
|
||||
virtual bigtime_t OfflineTime();
|
||||
virtual void ControlLoop();
|
||||
virtual status_t DeleteHook(BMediaNode* node);
|
||||
|
||||
// BBufferProducer interface
|
||||
protected:
|
||||
virtual status_t FormatSuggestionRequested(media_type type,
|
||||
int32 quality, media_format* format);
|
||||
virtual status_t FormatProposal(const media_source &output,
|
||||
media_format* format);
|
||||
virtual status_t FormatChangeRequested(const media_source& source,
|
||||
const media_destination& destination,
|
||||
media_format* ioFormat, int32* _deprecated_);
|
||||
virtual status_t GetNextOutput(int32* cookie,
|
||||
media_output* outOutput);
|
||||
virtual status_t DisposeOutputCookie(int32 cookie);
|
||||
virtual status_t SetBufferGroup(const media_source& forSource,
|
||||
BBufferGroup* group);
|
||||
virtual status_t VideoClippingChanged(const media_source& forSource,
|
||||
int16 numShorts, int16* clipData,
|
||||
const media_video_display_info& display,
|
||||
int32* _deprecated_);
|
||||
virtual status_t GetLatency(bigtime_t* out_latency);
|
||||
virtual status_t PrepareToConnect(const media_source& what,
|
||||
const media_destination& where,
|
||||
media_format* format, media_source* outSource,
|
||||
char* out_name);
|
||||
virtual void Connect(status_t error, const media_source& source,
|
||||
const media_destination& destination,
|
||||
const media_format& format, char* ioName);
|
||||
virtual void Disconnect(const media_source& what,
|
||||
const media_destination& where);
|
||||
virtual void LateNoticeReceived(const media_source& what,
|
||||
bigtime_t howMuch, bigtime_t performanceTime);
|
||||
virtual void EnableOutput(const media_source& what, bool enabled,
|
||||
int32* _deprecated_);
|
||||
virtual status_t SetPlayRate(int32 numer, int32 denom);
|
||||
virtual void AdditionalBufferRequested(
|
||||
const media_source& source,
|
||||
media_buffer_id prevBuffer,
|
||||
bigtime_t prevTime,
|
||||
const media_seek_tag* prevTag);
|
||||
virtual void LatencyChanged(const media_source& source,
|
||||
const media_destination& destination,
|
||||
bigtime_t newLatency, uint32 flags);
|
||||
|
||||
private:
|
||||
void _HandleStart(bigtime_t performance_time);
|
||||
void _HandleStop();
|
||||
void _HandleTimeWarp(bigtime_t performance_time);
|
||||
void _HandleSeek(bigtime_t performance_time);
|
||||
|
||||
static int32 _FrameGeneratorThreadEntry(void* data);
|
||||
int32 _FrameGeneratorThread();
|
||||
|
||||
status_t fInitStatus;
|
||||
|
||||
int32 fInternalID;
|
||||
BMediaAddOn* fAddOn;
|
||||
|
||||
BLocker fLock;
|
||||
BBufferGroup* fBufferGroup;
|
||||
BBufferGroup* fUsedBufferGroup;
|
||||
|
||||
thread_id fThread;
|
||||
sem_id fFrameSync;
|
||||
|
||||
// The remaining variables should be declared volatile, but they
|
||||
// are not here to improve the legibility of the sample code.
|
||||
int64 fFrame;
|
||||
int64 fFrameBase;
|
||||
bigtime_t fPerformanceTimeBase;
|
||||
media_output fOutput;
|
||||
media_raw_video_format fConnectedFormat;
|
||||
bool fRunning;
|
||||
bool fConnected;
|
||||
bool fEnabled;
|
||||
|
||||
NodeManager* fManager;
|
||||
VideoSupplier* fSupplier;
|
||||
};
|
||||
|
||||
#endif // VIDEO_PRODUCER_H
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2001-2008 Ingo Weinhold <ingo_weinhold@gmx.de>
|
||||
* Copyright 2001-2008 Stephan Aßmus <superstippi@gmx.de>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
#include "VideoSupplier.h"
|
||||
|
||||
|
||||
VideoSupplier::VideoSupplier()
|
||||
: fProcessingLatency(1000)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
VideoSupplier::~VideoSupplier()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoSupplier::DeleteCaches()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2001-2008 Ingo Weinhold <ingo_weinhold@gmx.de>
|
||||
* Copyright 2001-2008 Stephan Aßmus <superstippi@gmx.de>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
#ifndef VIDEO_SUPPLIER_H
|
||||
#define VIDEO_SUPPLIER_H
|
||||
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
struct media_format;
|
||||
|
||||
|
||||
class VideoSupplier {
|
||||
public:
|
||||
VideoSupplier();
|
||||
virtual ~VideoSupplier();
|
||||
|
||||
virtual status_t FillBuffer(int64 startFrame, void* buffer,
|
||||
const media_format* format,
|
||||
bool& wasCached) = 0;
|
||||
|
||||
virtual void DeleteCaches();
|
||||
|
||||
inline bigtime_t ProcessingLatency() const
|
||||
{ return fProcessingLatency; }
|
||||
|
||||
protected:
|
||||
bigtime_t fProcessingLatency;
|
||||
};
|
||||
|
||||
#endif // VIDEO_SUPPLIER_H
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2000-2008 Ingo Weinhold <ingo_weinhold@gmx.de> All rights reserved.
|
||||
* Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include "VideoTarget.h"
|
||||
|
||||
|
||||
VideoTarget::VideoTarget()
|
||||
: fBitmapLock(),
|
||||
fBitmap(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
VideoTarget::~VideoTarget()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VideoTarget::LockBitmap()
|
||||
{
|
||||
return fBitmapLock.Lock();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoTarget::UnlockBitmap()
|
||||
{
|
||||
fBitmapLock.Unlock();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoTarget::SetBitmap(const BBitmap* bitmap)
|
||||
{
|
||||
LockBitmap();
|
||||
fBitmap = bitmap;
|
||||
UnlockBitmap();
|
||||
}
|
||||
|
||||
|
||||
const BBitmap*
|
||||
VideoTarget::GetBitmap() const
|
||||
{
|
||||
return fBitmap;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2000-2008 Ingo Weinhold <ingo_weinhold@gmx.de> All rights reserved.
|
||||
* Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
/*! Derived classes are video consumer targets. Each time the consumer
|
||||
has received a frame (that is not late and thus dropped) it calls
|
||||
SetBitmap(). This method should immediately do whatever has to be done.
|
||||
Until the next call to SetBitmap() the bitmap can be used -- thereafter
|
||||
it is not allowed to use it any longer. Therefore the bitmap variable
|
||||
is protected by a lock. Anytime it is going to be accessed, the object
|
||||
must be locked.
|
||||
*/
|
||||
#ifndef VIDEO_TARGET_H
|
||||
#define VIDEO_TARGET_H
|
||||
|
||||
|
||||
#include <Locker.h>
|
||||
|
||||
|
||||
class BBitmap;
|
||||
|
||||
|
||||
class VideoTarget {
|
||||
public:
|
||||
VideoTarget();
|
||||
virtual ~VideoTarget();
|
||||
|
||||
bool LockBitmap();
|
||||
void UnlockBitmap();
|
||||
|
||||
virtual void SetBitmap(const BBitmap* bitmap);
|
||||
const BBitmap* GetBitmap() const;
|
||||
|
||||
protected:
|
||||
BLocker fBitmapLock;
|
||||
const BBitmap* volatile fBitmap;
|
||||
};
|
||||
|
||||
#endif // VIDEO_TARGET_H
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007, Haiku. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
#ifndef AUDIO_SUPPLIER_H
|
||||
#define AUDIO_SUPPLIER_H
|
||||
|
||||
|
||||
#include <MediaDefs.h>
|
||||
#include <MediaFormats.h>
|
||||
|
||||
class AudioSupplier {
|
||||
public:
|
||||
AudioSupplier();
|
||||
virtual ~AudioSupplier();
|
||||
|
||||
virtual media_format Format() const = 0;
|
||||
virtual status_t GetEncodedFormat(media_format* format)
|
||||
const = 0;
|
||||
virtual status_t GetCodecInfo(media_codec_info* info) const = 0;
|
||||
|
||||
virtual status_t ReadFrames(void* buffer, int64* framesRead,
|
||||
bigtime_t* performanceTime) = 0;
|
||||
virtual status_t SeekToTime(bigtime_t* performanceTime) = 0;
|
||||
|
||||
virtual bigtime_t Position() const = 0;
|
||||
virtual bigtime_t Duration() const = 0;
|
||||
};
|
||||
|
||||
#endif // AUDIO_SUPPLIER_H
|
||||
+4
-3
@@ -5,15 +5,16 @@
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
#include "AudioSupplier.h"
|
||||
#include "AudioTrackSupplier.h"
|
||||
|
||||
|
||||
AudioSupplier::AudioSupplier()
|
||||
AudioTrackSupplier::AudioTrackSupplier()
|
||||
: AudioReader()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AudioSupplier::~AudioSupplier()
|
||||
AudioTrackSupplier::~AudioTrackSupplier()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2007-2008, Haiku. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
#ifndef AUDIO_TRACK_SUPPLIER_H
|
||||
#define AUDIO_TRACK_SUPPLIER_H
|
||||
|
||||
#include <MediaDefs.h>
|
||||
#include <MediaFormats.h>
|
||||
|
||||
#include "AudioReader.h"
|
||||
|
||||
class AudioTrackSupplier : public AudioReader {
|
||||
public:
|
||||
AudioTrackSupplier();
|
||||
virtual ~AudioTrackSupplier();
|
||||
|
||||
virtual const media_format& Format() const = 0;
|
||||
virtual status_t GetEncodedFormat(media_format* format)
|
||||
const = 0;
|
||||
virtual status_t GetCodecInfo(media_codec_info* info) const = 0;
|
||||
virtual bigtime_t Duration() const = 0;
|
||||
};
|
||||
|
||||
#endif // AUDIO_TRACK_SUPPLIER_H
|
||||
@@ -1,64 +1,85 @@
|
||||
/*
|
||||
* Copyright 2007, Haiku. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
* Copyright © 2000-2004 Ingo Weinhold <ingo_weinhold@gmx.de>
|
||||
* Copyright © 2006-2008 Stephan Aßmus <superstippi@gmx.de>
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#include "MediaTrackAudioSupplier.h"
|
||||
|
||||
#include <new>
|
||||
#include <algobase.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <MediaFile.h>
|
||||
#include <MediaTrack.h>
|
||||
|
||||
using std::nothrow;
|
||||
|
||||
// constructor
|
||||
MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* track)
|
||||
: AudioSupplier()
|
||||
, fMediaTrack(track)
|
||||
|
||||
, fPerformanceTime(0)
|
||||
, fDuration(0)
|
||||
//#define TRACE_AUDIO_SUPPLIER
|
||||
#ifdef TRACE_AUDIO_SUPPLIER
|
||||
# define TRACE(x...) printf("MediaTrackAudioSupplier::"); printf(x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
#endif
|
||||
|
||||
|
||||
// #pragma mark - Buffer
|
||||
|
||||
|
||||
struct MediaTrackAudioSupplier::Buffer {
|
||||
void* data;
|
||||
int64 offset;
|
||||
int64 size;
|
||||
bigtime_t time_stamp;
|
||||
|
||||
static int CompareOffset(const void* a, const void* b);
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
MediaTrackAudioSupplier::Buffer::CompareOffset(const void* a, const void* b)
|
||||
{
|
||||
if (!fMediaTrack) {
|
||||
printf("MediaTrackAudioSupplier() - no media track\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fFormat.u.raw_audio = media_multi_audio_format::wildcard;
|
||||
#ifdef __HAIKU__
|
||||
fFormat.u.raw_audio.format = media_multi_audio_format::B_AUDIO_FLOAT;
|
||||
#endif
|
||||
status_t ret = fMediaTrack->DecodedFormat(&fFormat);
|
||||
if (ret < B_OK) {
|
||||
printf("MediaTrackAudioSupplier() - "
|
||||
"fMediaTrack->DecodedFormat(): %s\n", strerror(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
fDuration = fMediaTrack->Duration();
|
||||
//
|
||||
// for (bigtime_t time = 0; time < fDuration; time += 10000) {
|
||||
// bigtime_t keyFrameTime = time;
|
||||
// fMediaTrack->FindKeyFrameForTime(&keyFrameTime,
|
||||
// B_MEDIA_SEEK_CLOSEST_BACKWARD);
|
||||
// printf("audio keyframe time for time: %lld -> %lld\n", time, keyFrameTime);
|
||||
// }
|
||||
const Buffer* buffer1 = *(const Buffer**)a;
|
||||
const Buffer* buffer2 = *(const Buffer**)b;
|
||||
int result = 0;
|
||||
if (buffer1->offset < buffer2->offset)
|
||||
result = -1;
|
||||
else if (buffer1->offset > buffer2->offset)
|
||||
result = 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
// destructor
|
||||
|
||||
// #pragma mark - MediaTrackAudioSupplier
|
||||
|
||||
|
||||
MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* mediaTrack)
|
||||
: AudioTrackSupplier(),
|
||||
fMediaTrack(mediaTrack),
|
||||
fBuffer(NULL),
|
||||
fBufferOffset(0),
|
||||
fBufferSize(0),
|
||||
fBuffers(10),
|
||||
fHasKeyFrames(false),
|
||||
fCountFrames(0),
|
||||
fReportSeekError(true)
|
||||
{
|
||||
_InitFromTrack();
|
||||
}
|
||||
|
||||
|
||||
MediaTrackAudioSupplier::~MediaTrackAudioSupplier()
|
||||
{
|
||||
_FreeBuffers();
|
||||
delete[] fBuffer;
|
||||
}
|
||||
|
||||
|
||||
media_format
|
||||
const media_format&
|
||||
MediaTrackAudioSupplier::Format() const
|
||||
{
|
||||
return fFormat;
|
||||
return AudioReader::Format();
|
||||
}
|
||||
|
||||
|
||||
@@ -80,53 +101,530 @@ MediaTrackAudioSupplier::GetCodecInfo(media_codec_info* info) const
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MediaTrackAudioSupplier::ReadFrames(void* buffer, int64* framesRead,
|
||||
bigtime_t* performanceTime)
|
||||
bigtime_t
|
||||
MediaTrackAudioSupplier::Duration() const
|
||||
{
|
||||
if (!fMediaTrack)
|
||||
return B_NO_INIT;
|
||||
if (!buffer || !framesRead)
|
||||
return B_BAD_VALUE;
|
||||
return 0;
|
||||
|
||||
media_header mediaHeader;
|
||||
status_t ret = fMediaTrack->ReadFrames(buffer, framesRead, &mediaHeader);
|
||||
return fMediaTrack->Duration();
|
||||
}
|
||||
|
||||
if (ret < B_OK) {
|
||||
// further analyse the error
|
||||
if (fDuration == 0 || (double)fPerformanceTime / fDuration > 0.95) {
|
||||
// some codecs don't behave well, or maybe somes files are bad,
|
||||
// they don't report the end of the stream correctly
|
||||
// NOTE: "more than 95% of the stream" is of course pure guess,
|
||||
// but it fixed the problem I had with some files
|
||||
ret = B_LAST_BUFFER_ERROR;
|
||||
|
||||
// #pragma mark - AudioReader
|
||||
|
||||
|
||||
// Read
|
||||
status_t
|
||||
MediaTrackAudioSupplier::Read(void* buffer, int64 pos, int64 frames)
|
||||
{
|
||||
TRACE("Read(%p, %lld, %lld)\n", buffer, pos,
|
||||
frames);
|
||||
TRACE(" this: %p, fOutOffset: %lld\n", this, fOutOffset);
|
||||
|
||||
status_t error = InitCheck();
|
||||
if (error != B_OK) {
|
||||
TRACE("Read() done\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
// convert pos according to our offset
|
||||
pos += fOutOffset;
|
||||
// Fill the frames after the end of the track with silence.
|
||||
if (pos + frames > fCountFrames) {
|
||||
int64 size = max(0LL, fCountFrames - pos);
|
||||
ReadSilence(SkipFrames(buffer, size), frames - size);
|
||||
frames = size;
|
||||
}
|
||||
|
||||
TRACE(" after eliminating the frames after the track end: %p, %lld, %lld\n",
|
||||
buffer, pos, frames);
|
||||
|
||||
// read the cached frames
|
||||
bigtime_t time = system_time();
|
||||
if (frames > 0)
|
||||
_ReadCachedFrames(buffer, pos, frames, time);
|
||||
|
||||
TRACE(" after reading from cache: %p, %lld, %lld\n", buffer, pos, frames);
|
||||
|
||||
// read the remaining (uncached) frames
|
||||
if (frames > 0)
|
||||
_ReadUncachedFrames(buffer, pos, frames, time);
|
||||
|
||||
TRACE("Read() done\n");
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
MediaTrackAudioSupplier::InitCheck() const
|
||||
{
|
||||
status_t error = AudioReader::InitCheck();
|
||||
if (error == B_OK && (!fMediaTrack || !fBuffer))
|
||||
error = B_NO_INIT;
|
||||
return error;
|
||||
}
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
// _InitFromTrack
|
||||
void
|
||||
MediaTrackAudioSupplier::_InitFromTrack()
|
||||
{
|
||||
if (fMediaTrack && fMediaTrack->DecodedFormat(&fFormat) == B_OK
|
||||
&& fFormat.type == B_MEDIA_RAW_AUDIO) {
|
||||
|
||||
#ifdef TRACE_AUDIO_SUPPLIER
|
||||
char formatString[256];
|
||||
string_for_format(fFormat, formatString, 256);
|
||||
TRACE("MediaTrackAudioSupplier: format is: %s\n", formatString);
|
||||
#endif
|
||||
|
||||
fBuffer = new (nothrow) char[fFormat.u.raw_audio.buffer_size];
|
||||
_AllocateBuffers();
|
||||
|
||||
// Find out, if the track has key frames: as a heuristic we
|
||||
// check, if the first and the second frame have the same backward
|
||||
// key frame.
|
||||
// Note: It shouldn't harm that much, if we're wrong and the
|
||||
// track has key frame although we found out that it has not.
|
||||
int64 keyFrame0 = 0;
|
||||
int64 keyFrame1 = 1;
|
||||
fMediaTrack->FindKeyFrameForFrame(&keyFrame0,
|
||||
B_MEDIA_SEEK_CLOSEST_BACKWARD);
|
||||
fMediaTrack->FindKeyFrameForFrame(&keyFrame1,
|
||||
B_MEDIA_SEEK_CLOSEST_BACKWARD);
|
||||
fHasKeyFrames = (keyFrame0 == keyFrame1);
|
||||
|
||||
// get the length of the track
|
||||
fCountFrames = fMediaTrack->CountFrames();
|
||||
} else
|
||||
fMediaTrack = NULL;
|
||||
}
|
||||
|
||||
// _FramesPerBuffer
|
||||
int64
|
||||
MediaTrackAudioSupplier::_FramesPerBuffer() const
|
||||
{
|
||||
int64 sampleSize = fFormat.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
|
||||
return fFormat.u.raw_audio.buffer_size / frameSize;
|
||||
}
|
||||
|
||||
// _CopyFrames
|
||||
//
|
||||
// Given two buffers starting at different frame offsets, this function
|
||||
// copies /frames/ frames at position /position/ from the source to the
|
||||
// target buffer.
|
||||
// Note that no range checking is done.
|
||||
void
|
||||
MediaTrackAudioSupplier::_CopyFrames(void* source, int64 sourceOffset,
|
||||
void* target, int64 targetOffset,
|
||||
int64 position, int64 frames) const
|
||||
{
|
||||
int64 sampleSize = fFormat.u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
|
||||
source = (char*)source + frameSize * (position - sourceOffset);
|
||||
target = (char*)target + frameSize * (position - targetOffset);
|
||||
memcpy(target, source, frames * frameSize);
|
||||
}
|
||||
|
||||
// _CopyFrames
|
||||
//
|
||||
// Given two buffers starting at different frame offsets, this function
|
||||
// copies /frames/ frames at position /position/ from the source to the
|
||||
// target buffer. This version expects a cache buffer as source.
|
||||
// Note that no range checking is done.
|
||||
void
|
||||
MediaTrackAudioSupplier::_CopyFrames(Buffer* buffer,
|
||||
void* target, int64 targetOffset,
|
||||
int64 position, int64 frames) const
|
||||
{
|
||||
_CopyFrames(buffer->data, buffer->offset, target, targetOffset, position,
|
||||
frames);
|
||||
}
|
||||
|
||||
// _AllocateBuffers
|
||||
//
|
||||
// Allocates a set of buffers.
|
||||
void
|
||||
MediaTrackAudioSupplier::_AllocateBuffers()
|
||||
{
|
||||
int32 count = 10;
|
||||
_FreeBuffers();
|
||||
int32 bufferSize = fFormat.u.raw_audio.buffer_size;
|
||||
char* data = new (nothrow) char[bufferSize * count];
|
||||
for (; count > 0; count--) {
|
||||
Buffer* buffer = new (nothrow) Buffer;
|
||||
if (!buffer || !fBuffers.AddItem(buffer)) {
|
||||
delete buffer;
|
||||
return;
|
||||
}
|
||||
printf("MediaTrackAudioSupplier::ReadFrame() - "
|
||||
"error while reading frames: %s\n", strerror(ret));
|
||||
} else {
|
||||
fPerformanceTime = mediaHeader.start_time;
|
||||
buffer->data = data;
|
||||
data += bufferSize;
|
||||
buffer->offset = 0;
|
||||
buffer->size = 0;
|
||||
buffer->time_stamp = 0;
|
||||
}
|
||||
|
||||
if (performanceTime)
|
||||
*performanceTime = fPerformanceTime;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MediaTrackAudioSupplier::SeekToTime(bigtime_t* performanceTime)
|
||||
// _FreeBuffers
|
||||
//
|
||||
// Frees the allocated buffers.
|
||||
void
|
||||
MediaTrackAudioSupplier::_FreeBuffers()
|
||||
{
|
||||
if (!fMediaTrack)
|
||||
return B_NO_INIT;
|
||||
|
||||
bigtime_t _performanceTime = *performanceTime;
|
||||
status_t ret = fMediaTrack->SeekToTime(performanceTime);
|
||||
if (ret == B_OK) {
|
||||
printf("seeked: %lld -> %lld\n", _performanceTime, *performanceTime);
|
||||
fPerformanceTime = *performanceTime;
|
||||
if (fBuffers.CountItems() > 0) {
|
||||
delete[] (char*)_BufferAt(0)->data;
|
||||
for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++)
|
||||
delete buffer;
|
||||
fBuffers.MakeEmpty();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _BufferAt
|
||||
//
|
||||
// Returns the buffer at index /index/.
|
||||
MediaTrackAudioSupplier::Buffer*
|
||||
MediaTrackAudioSupplier::_BufferAt(int32 index) const
|
||||
{
|
||||
return (Buffer*)fBuffers.ItemAt(index);
|
||||
}
|
||||
|
||||
// _FindBufferAtFrame
|
||||
//
|
||||
// If any buffer starts at offset /frame/, it is returned, NULL otherwise.
|
||||
MediaTrackAudioSupplier::Buffer*
|
||||
MediaTrackAudioSupplier::_FindBufferAtFrame(int64 frame) const
|
||||
{
|
||||
Buffer* buffer = NULL;
|
||||
for (int32 i = 0;
|
||||
((buffer = _BufferAt(i))) && buffer->offset != frame;
|
||||
i++);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// _FindUnusedBuffer
|
||||
//
|
||||
// Returns the first unused buffer or NULL if all buffers are used.
|
||||
MediaTrackAudioSupplier::Buffer*
|
||||
MediaTrackAudioSupplier::_FindUnusedBuffer() const
|
||||
{
|
||||
Buffer* buffer = NULL;
|
||||
for (int32 i = 0; ((buffer = _BufferAt(i))) && buffer->size != 0; i++);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// _FindUsableBuffer
|
||||
//
|
||||
// Returns either an unused buffer or, if all buffers are used, the least
|
||||
// recently used buffer.
|
||||
// In every case a buffer is returned.
|
||||
MediaTrackAudioSupplier::Buffer*
|
||||
MediaTrackAudioSupplier::_FindUsableBuffer() const
|
||||
{
|
||||
Buffer* result = _FindUnusedBuffer();
|
||||
if (!result) {
|
||||
// find the least recently used buffer.
|
||||
result = _BufferAt(0);
|
||||
for (int32 i = 1; Buffer* buffer = _BufferAt(i); i++) {
|
||||
if (buffer->time_stamp < result->time_stamp)
|
||||
result = buffer;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// _FindUsableBufferFor
|
||||
//
|
||||
// In case there already exists a buffer that starts at position this
|
||||
// one is returned. Otherwise the function returns either an unused
|
||||
// buffer or, if all buffers are used, the least recently used buffer.
|
||||
// In every case a buffer is returned.
|
||||
MediaTrackAudioSupplier::Buffer*
|
||||
MediaTrackAudioSupplier::_FindUsableBufferFor(int64 position) const
|
||||
{
|
||||
Buffer* buffer = _FindBufferAtFrame(position);
|
||||
if (!buffer)
|
||||
buffer = _FindUsableBuffer();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// _GetBuffersFor
|
||||
//
|
||||
// Adds pointers to all buffers to the list that contain data of the
|
||||
// supplied interval.
|
||||
void
|
||||
MediaTrackAudioSupplier::_GetBuffersFor(BList& buffers, int64 position,
|
||||
int64 frames) const
|
||||
{
|
||||
buffers.MakeEmpty();
|
||||
for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++) {
|
||||
// Calculate the intersecting interval and add the buffer if it is
|
||||
// not empty.
|
||||
int32 startFrame = max(position, buffer->offset);
|
||||
int32 endFrame = min(position + frames, buffer->offset + buffer->size);
|
||||
if (startFrame < endFrame)
|
||||
buffers.AddItem(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// _TouchBuffer
|
||||
//
|
||||
// Sets a buffer's time stamp to the current system time.
|
||||
void
|
||||
MediaTrackAudioSupplier::_TouchBuffer(Buffer* buffer)
|
||||
{
|
||||
buffer->time_stamp = system_time();
|
||||
}
|
||||
|
||||
// _ReadBuffer
|
||||
//
|
||||
// Read a buffer from the current position (which is supplied in /position/)
|
||||
// into /buffer/. The buffer's time stamp is set to the current system time.
|
||||
status_t
|
||||
MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position)
|
||||
{
|
||||
return _ReadBuffer(buffer, position, system_time());
|
||||
}
|
||||
|
||||
// _ReadBuffer
|
||||
//
|
||||
// Read a buffer from the current position (which is supplied in /position/)
|
||||
// into /buffer/. The buffer's time stamp is set to the supplied time.
|
||||
status_t
|
||||
MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position,
|
||||
bigtime_t time)
|
||||
{
|
||||
status_t error = fMediaTrack->ReadFrames(buffer->data, &buffer->size);
|
||||
|
||||
TRACE("Read(%p, %lld): %s\n", buffer->data, buffer->size, strerror(error));
|
||||
|
||||
buffer->offset = position;
|
||||
buffer->time_stamp = time;
|
||||
if (error != B_OK)
|
||||
buffer->size = 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
// _ReadCachedFrames
|
||||
//
|
||||
// Tries to read as much as possible data from the cache. The supplied
|
||||
// buffer pointer as well as position and number of frames are adjusted
|
||||
// accordingly. The used cache buffers are stamped with the current
|
||||
// system time.
|
||||
void
|
||||
MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest, int64& pos,
|
||||
int64& frames)
|
||||
{
|
||||
_ReadCachedFrames(dest, pos, frames, system_time());
|
||||
}
|
||||
|
||||
// _ReadCachedFrames
|
||||
//
|
||||
// Tries to read as much as possible data from the cache. The supplied
|
||||
// buffer pointer as well as position and number of frames are adjusted
|
||||
// accordingly. The used cache buffers are stamped with the supplied
|
||||
// time.
|
||||
void
|
||||
MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest, int64& pos,
|
||||
int64& frames, bigtime_t time)
|
||||
{
|
||||
// Get a list of all cache buffers that contain data of the interval,
|
||||
// and sort it.
|
||||
BList buffers(10);
|
||||
_GetBuffersFor(buffers, pos, frames);
|
||||
buffers.SortItems(Buffer::CompareOffset);
|
||||
// Step forward through the list of cache buffers and try to read as
|
||||
// much data from the beginning as possible.
|
||||
for (int32 i = 0; Buffer* buffer = (Buffer*)buffers.ItemAt(i); i++) {
|
||||
if (buffer->offset <= pos && buffer->offset + buffer->size > pos) {
|
||||
// read from the beginning
|
||||
int64 size = min(frames, buffer->offset + buffer->size - pos);
|
||||
_CopyFrames(buffer->data, buffer->offset, dest, pos, pos, size);
|
||||
pos += size;
|
||||
frames -= size;
|
||||
dest = SkipFrames(dest, size);
|
||||
}
|
||||
buffer->time_stamp = time;
|
||||
}
|
||||
// Step backward through the list of cache buffers and try to read as
|
||||
// much data from the end as possible.
|
||||
for (int32 i = buffers.CountItems() - 1;
|
||||
Buffer* buffer = (Buffer*)buffers.ItemAt(i);
|
||||
i++) {
|
||||
if (buffer->offset < pos + frames
|
||||
&& buffer->offset + buffer->size >= pos + frames) {
|
||||
// read from the end
|
||||
int64 size = min(frames, pos + frames - buffer->offset);
|
||||
_CopyFrames(buffer->data, buffer->offset, dest, pos,
|
||||
pos + frames - size, size);
|
||||
frames -= size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _ReadUncachedFrames
|
||||
//
|
||||
// Reads /frames/ frames from /position/ into /buffer/. The frames are not
|
||||
// read from the cache, but read frames are cached, if possible.
|
||||
// New cache buffers are stamped with the system time.
|
||||
// If an error occurs, the untouched part of the buffer is set to 0.
|
||||
status_t
|
||||
MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position,
|
||||
int64 frames)
|
||||
{
|
||||
return _ReadUncachedFrames(buffer, position, frames, system_time());
|
||||
}
|
||||
|
||||
// _ReadUncachedFrames
|
||||
//
|
||||
// Reads /frames/ frames from /position/ into /buffer/. The frames are not
|
||||
// read from the cache, but read frames are cached, if possible.
|
||||
// New cache buffers are stamped with the supplied time.
|
||||
// If an error occurs, the untouched part of the buffer is set to 0.
|
||||
status_t
|
||||
MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position,
|
||||
int64 frames, bigtime_t time)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
// seek to the position
|
||||
int64 currentPos = position;
|
||||
if (frames > 0) {
|
||||
error = _SeekToKeyFrameBackward(currentPos);
|
||||
TRACE("_ReadUncachedFrames() - seeked to position: %lld\n", currentPos);
|
||||
}
|
||||
// read the frames
|
||||
while (error == B_OK && frames > 0) {
|
||||
Buffer* cacheBuffer = _FindUsableBufferFor(currentPos);
|
||||
TRACE("_ReadUncachedFrames() - usable buffer found: %p\n", cacheBuffer);
|
||||
error = _ReadBuffer(cacheBuffer, currentPos, time);
|
||||
if (error == B_OK) {
|
||||
int64 size = min(position + frames,
|
||||
cacheBuffer->offset + cacheBuffer->size)
|
||||
- position;
|
||||
if (size > 0) {
|
||||
_CopyFrames(cacheBuffer, buffer, position, position, size);
|
||||
buffer = SkipFrames(buffer, size);
|
||||
position += size;
|
||||
frames -= size;
|
||||
}
|
||||
currentPos += cacheBuffer->size;
|
||||
}
|
||||
}
|
||||
// Ensure that all frames up to the next key frame are cached.
|
||||
// This avoids, that each read
|
||||
if (error == B_OK) {
|
||||
int64 nextKeyFrame = currentPos;
|
||||
if (_FindKeyFrameForward(nextKeyFrame) == B_OK) {
|
||||
while (currentPos < nextKeyFrame) {
|
||||
// Check, if data at this position are cache.
|
||||
// If not read it.
|
||||
Buffer* cacheBuffer = _FindBufferAtFrame(currentPos);
|
||||
if (!cacheBuffer || cacheBuffer->size == 0) {
|
||||
cacheBuffer = _FindUsableBufferFor(currentPos);
|
||||
if (_ReadBuffer(cacheBuffer, currentPos, time) != B_OK)
|
||||
break;
|
||||
}
|
||||
if (cacheBuffer)
|
||||
currentPos += cacheBuffer->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
// on error fill up the buffer with silence
|
||||
if (error != B_OK && frames > 0)
|
||||
ReadSilence(buffer, frames);
|
||||
return error;
|
||||
}
|
||||
|
||||
// _FindKeyFrameForward
|
||||
status_t
|
||||
MediaTrackAudioSupplier::_FindKeyFrameForward(int64& position)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
// NOTE: the keyframe version confuses the Frauenhofer MP3 decoder,
|
||||
// it works fine with the non-keyframe version, so let's hope this
|
||||
// is the case for all other keyframe based BeOS codecs...
|
||||
// if (fHasKeyFrames) {
|
||||
// error = fMediaTrack->FindKeyFrameForFrame(
|
||||
// &position, B_MEDIA_SEEK_CLOSEST_FORWARD);
|
||||
// } else {
|
||||
int64 framesPerBuffer = _FramesPerBuffer();
|
||||
position += framesPerBuffer - 1;
|
||||
position = position % framesPerBuffer;
|
||||
// }
|
||||
return error;
|
||||
}
|
||||
|
||||
// _FindKeyFrameBackward
|
||||
status_t
|
||||
MediaTrackAudioSupplier::_FindKeyFrameBackward(int64& position)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
if (fHasKeyFrames) {
|
||||
error = fMediaTrack->FindKeyFrameForFrame(
|
||||
&position, B_MEDIA_SEEK_CLOSEST_BACKWARD);
|
||||
} else
|
||||
position -= position % _FramesPerBuffer();
|
||||
return error;
|
||||
}
|
||||
|
||||
// _SeekToKeyFrameForward
|
||||
status_t
|
||||
MediaTrackAudioSupplier::_SeekToKeyFrameForward(int64& position)
|
||||
{
|
||||
if (position == fMediaTrack->CurrentFrame())
|
||||
return B_OK;
|
||||
|
||||
status_t error = B_OK;
|
||||
if (fHasKeyFrames) {
|
||||
#ifdef TRACE_AUDIO_SUPPLIER
|
||||
int64 oldPosition = position;
|
||||
#endif
|
||||
error = fMediaTrack->SeekToFrame(&position,
|
||||
B_MEDIA_SEEK_CLOSEST_FORWARD);
|
||||
TRACE("_SeekToKeyFrameForward() - seek to key frame forward: "
|
||||
"%lld -> %lld (%lld)\n", oldPosition, position,
|
||||
fMediaTrack->CurrentFrame());
|
||||
} else {
|
||||
_FindKeyFrameForward(position);
|
||||
error = fMediaTrack->SeekToFrame(&position);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// _SeekToKeyFrameBackward
|
||||
status_t
|
||||
MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64& position)
|
||||
{
|
||||
if (position == fMediaTrack->CurrentFrame())
|
||||
return B_OK;
|
||||
|
||||
status_t error = B_OK;
|
||||
if (fHasKeyFrames) {
|
||||
int64 oldPosition = position;
|
||||
error = fMediaTrack->FindKeyFrameForFrame(&position,
|
||||
B_MEDIA_SEEK_CLOSEST_BACKWARD);
|
||||
if (error >= B_OK)
|
||||
error = fMediaTrack->SeekToFrame(&position, 0);
|
||||
if (error < B_OK) {
|
||||
position = fMediaTrack->CurrentFrame();
|
||||
if (fReportSeekError) {
|
||||
printf(" seek to key frame backward: %lld -> %lld (%lld) "
|
||||
"- %s\n", oldPosition, position,
|
||||
fMediaTrack->CurrentFrame(), strerror(error));
|
||||
fReportSeekError = false;
|
||||
}
|
||||
} else {
|
||||
fReportSeekError = true;
|
||||
}
|
||||
} else {
|
||||
_FindKeyFrameBackward(position);
|
||||
error = fMediaTrack->SeekToFrame(&position);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +1,89 @@
|
||||
/*
|
||||
* Copyright 2007, Haiku. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
* Copyright © 2000-2004 Ingo Weinhold <ingo_weinhold@gmx.de>
|
||||
* Copyright © 2006-2008 Stephan Aßmus <superstippi@gmx.de>
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef MEDIA_TRACK_AUDIO_SUPPLIER_H
|
||||
#define MEDIA_TRACK_AUDIO_SUPPLIER_H
|
||||
|
||||
#include "AudioSupplier.h"
|
||||
#include <List.h>
|
||||
|
||||
#include "AudioTrackSupplier.h"
|
||||
|
||||
class BMediaTrack;
|
||||
struct media_codec_info;
|
||||
struct media_format;
|
||||
|
||||
class MediaTrackAudioSupplier : public AudioSupplier {
|
||||
class MediaTrackAudioSupplier : public AudioTrackSupplier {
|
||||
public:
|
||||
MediaTrackAudioSupplier(BMediaTrack* track);
|
||||
virtual ~MediaTrackAudioSupplier();
|
||||
|
||||
virtual media_format Format() const;
|
||||
virtual const media_format& Format() const;
|
||||
virtual status_t GetEncodedFormat(media_format* format) const;
|
||||
virtual status_t GetCodecInfo(media_codec_info* info) const;
|
||||
virtual bigtime_t Duration() const;
|
||||
|
||||
virtual status_t ReadFrames(void* buffer, int64* framesRead,
|
||||
bigtime_t* performanceTime);
|
||||
virtual status_t SeekToTime(bigtime_t* performanceTime);
|
||||
// AudioReader interface
|
||||
// (needed to reuse the class as AudioResampler input)
|
||||
virtual status_t Read(void* buffer, int64 pos, int64 frames);
|
||||
|
||||
virtual status_t InitCheck() const;
|
||||
|
||||
private:
|
||||
struct Buffer;
|
||||
void _InitFromTrack();
|
||||
|
||||
int64 _FramesPerBuffer() const;
|
||||
|
||||
void _CopyFrames(void* source, int64 sourceOffset,
|
||||
void* target, int64 targetOffset,
|
||||
int64 position, int64 frames) const;
|
||||
void _CopyFrames(Buffer* buffer, void* target,
|
||||
int64 targetOffset, int64 position,
|
||||
int64 frames) const;
|
||||
|
||||
void _AllocateBuffers();
|
||||
void _FreeBuffers();
|
||||
Buffer* _BufferAt(int32 index) const;
|
||||
Buffer* _FindBufferAtFrame(int64 frame) const;
|
||||
Buffer* _FindUnusedBuffer() const;
|
||||
Buffer* _FindUsableBuffer() const;
|
||||
Buffer* _FindUsableBufferFor(int64 position) const;
|
||||
void _GetBuffersFor(BList& buffers, int64 position,
|
||||
int64 frames) const;
|
||||
void _TouchBuffer(Buffer* buffer);
|
||||
|
||||
status_t _ReadBuffer(Buffer* buffer, int64 position);
|
||||
status_t _ReadBuffer(Buffer* buffer, int64 position,
|
||||
bigtime_t time);
|
||||
|
||||
void _ReadCachedFrames(void*& buffer,
|
||||
int64& position, int64& frames);
|
||||
void _ReadCachedFrames(void*& buffer,
|
||||
int64& position, int64& frames,
|
||||
bigtime_t time);
|
||||
|
||||
status_t _ReadUncachedFrames(void* buffer,
|
||||
int64 position, int64 frames);
|
||||
status_t _ReadUncachedFrames(void* buffer,
|
||||
int64 position, int64 frames,
|
||||
bigtime_t time);
|
||||
|
||||
status_t _FindKeyFrameForward(int64& position);
|
||||
status_t _FindKeyFrameBackward(int64& position);
|
||||
status_t _SeekToKeyFrameForward(int64& position);
|
||||
status_t _SeekToKeyFrameBackward(int64& position);
|
||||
|
||||
virtual bigtime_t Position() const
|
||||
{ return fPerformanceTime; }
|
||||
virtual bigtime_t Duration() const
|
||||
{ return fDuration; }
|
||||
private:
|
||||
BMediaTrack* fMediaTrack;
|
||||
|
||||
media_format fFormat;
|
||||
|
||||
bigtime_t fPerformanceTime;
|
||||
bigtime_t fDuration;
|
||||
char* fBuffer;
|
||||
int64 fBufferOffset;
|
||||
int64 fBufferSize;
|
||||
BList fBuffers;
|
||||
bool fHasKeyFrames;
|
||||
int64 fCountFrames;
|
||||
bool fReportSeekError;
|
||||
};
|
||||
|
||||
#endif // MEDIA_TRACK_AUDIO_SUPPLIER_H
|
||||
#endif // MEDIA_TRACK_AUDIO_SUPPLIER_H
|
||||
|
||||
@@ -29,73 +29,19 @@ static const char* string_for_color_space(color_space format);
|
||||
// constructor
|
||||
MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack* track,
|
||||
color_space format)
|
||||
: VideoSupplier()
|
||||
: VideoTrackSupplier()
|
||||
, fVideoTrack(track)
|
||||
|
||||
, fPerformanceTime(0)
|
||||
, fDuration(0)
|
||||
, fCurrentFrame(0)
|
||||
{
|
||||
if (!fVideoTrack) {
|
||||
printf("MediaTrackVideoSupplier() - no video track\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// get the encoded format
|
||||
memset(&fFormat, 0, sizeof(media_format));
|
||||
status_t ret = fVideoTrack->EncodedFormat(&fFormat);
|
||||
if (ret < B_OK) {
|
||||
printf("MediaTrackVideoSupplier::InitCheck() - "
|
||||
"fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
// get ouput video frame size
|
||||
uint32 width = fFormat.u.encoded_video.output.display.line_width;
|
||||
uint32 height = fFormat.u.encoded_video.output.display.line_count;
|
||||
|
||||
// specifiy the decoded format. we derive this information from
|
||||
// the encoded format (width & height).
|
||||
memset(&fFormat, 0, sizeof(media_format));
|
||||
// fFormat.u.raw_video.last_active = height - 1;
|
||||
// fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
|
||||
// fFormat.u.raw_video.pixel_width_aspect = 1;
|
||||
// fFormat.u.raw_video.pixel_height_aspect = 1;
|
||||
fFormat.u.raw_video.display.format = format;
|
||||
fFormat.u.raw_video.display.line_width = width;
|
||||
fFormat.u.raw_video.display.line_count = height;
|
||||
if (format == B_RGB32 || format == B_RGBA32)
|
||||
fFormat.u.raw_video.display.bytes_per_row = width * 4;
|
||||
else if (format == B_YCbCr422)
|
||||
fFormat.u.raw_video.display.bytes_per_row = ((width * 2 + 3) / 4) * 4;
|
||||
|
||||
ret = fVideoTrack->DecodedFormat(&fFormat);
|
||||
|
||||
if (ret < B_OK) {
|
||||
printf("MediaTrackVideoSupplier() - "
|
||||
"fVideoTrack->DecodedFormat(): %s\n", strerror(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
if (fFormat.u.raw_video.display.format != format) {
|
||||
printf("MediaTrackVideoSupplier() - "
|
||||
" codec changed colorspace of decoded format (%s -> %s)!\n"
|
||||
" this is bad for performance, since colorspace conversion\n"
|
||||
" needs to happen during playback.\n",
|
||||
string_for_color_space(format),
|
||||
string_for_color_space(fFormat.u.raw_video.display.format));
|
||||
// check if the codec forgot to adjust bytes_per_row
|
||||
uint32 minBPR;
|
||||
format = fFormat.u.raw_video.display.format;
|
||||
if (format == B_YCbCr422)
|
||||
minBPR = ((width * 2 + 3) / 4) * 4;
|
||||
else
|
||||
minBPR = width * 4;
|
||||
if (minBPR != fFormat.u.raw_video.display.bytes_per_row) {
|
||||
printf(" -> stupid codec forgot to adjust bytes_per_row!\n");
|
||||
fFormat.u.raw_video.display.bytes_per_row = minBPR;
|
||||
fVideoTrack->DecodedFormat(&fFormat);
|
||||
}
|
||||
}
|
||||
_SwitchFormat(format, 0);
|
||||
|
||||
fDuration = fVideoTrack->Duration();
|
||||
|
||||
@@ -113,7 +59,7 @@ MediaTrackVideoSupplier::~MediaTrackVideoSupplier()
|
||||
}
|
||||
|
||||
|
||||
media_format
|
||||
const media_format&
|
||||
MediaTrackVideoSupplier::Format() const
|
||||
{
|
||||
return fFormat;
|
||||
@@ -139,26 +85,44 @@ MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const
|
||||
|
||||
|
||||
status_t
|
||||
MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime)
|
||||
MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime,
|
||||
const media_format* format, bool& wasCached)
|
||||
{
|
||||
if (!fVideoTrack)
|
||||
return B_NO_INIT;
|
||||
if (!buffer)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
status_t ret = B_OK;
|
||||
if (format->u.raw_video.display.format
|
||||
!= fFormat.u.raw_video.display.format
|
||||
|| fFormat.u.raw_video.display.bytes_per_row
|
||||
!= format->u.raw_video.display.bytes_per_row) {
|
||||
ret = _SwitchFormat(format->u.raw_video.display.format,
|
||||
format->u.raw_video.display.bytes_per_row);
|
||||
if (ret < B_OK) {
|
||||
fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
|
||||
"unable to switch media format: %s\n", strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// read a frame
|
||||
int64 frameCount = 1;
|
||||
// TODO: how does this work for interlaced video (field count > 1)?
|
||||
media_header mediaHeader;
|
||||
status_t ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader);
|
||||
ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader);
|
||||
|
||||
if (ret < B_OK) {
|
||||
printf("MediaTrackVideoSupplier::ReadFrame() - "
|
||||
"error while reading frame of track: %s\n", strerror(ret));
|
||||
if (ret != B_LAST_BUFFER_ERROR) {
|
||||
fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
|
||||
"error while reading frame of track: %s\n", strerror(ret));
|
||||
}
|
||||
} else {
|
||||
fPerformanceTime = mediaHeader.start_time;
|
||||
}
|
||||
|
||||
fCurrentFrame = fVideoTrack->CurrentFrame();
|
||||
if (performanceTime)
|
||||
*performanceTime = fPerformanceTime;
|
||||
|
||||
@@ -186,10 +150,52 @@ MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime)
|
||||
return B_NO_INIT;
|
||||
|
||||
bigtime_t _performanceTime = *performanceTime;
|
||||
status_t ret = fVideoTrack->SeekToTime(performanceTime);
|
||||
status_t ret = fVideoTrack->FindKeyFrameForTime(performanceTime,
|
||||
B_MEDIA_SEEK_CLOSEST_BACKWARD);
|
||||
if (ret < B_OK)
|
||||
return ret;
|
||||
|
||||
ret = fVideoTrack->SeekToTime(performanceTime);
|
||||
if (ret == B_OK) {
|
||||
printf("seeked: %lld -> %lld\n", _performanceTime, *performanceTime);
|
||||
if (_performanceTime != *performanceTime)
|
||||
printf("seeked by time: %lld -> %lld\n", _performanceTime, *performanceTime);
|
||||
fPerformanceTime = *performanceTime;
|
||||
fCurrentFrame = fVideoTrack->CurrentFrame();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MediaTrackVideoSupplier::SeekToFrame(int64* frame)
|
||||
{
|
||||
if (!fVideoTrack)
|
||||
return B_NO_INIT;
|
||||
|
||||
int64 wantFrame = *frame;
|
||||
int64 currentFrame = fVideoTrack->CurrentFrame();
|
||||
|
||||
if (wantFrame == currentFrame)
|
||||
return B_OK;
|
||||
|
||||
status_t ret = fVideoTrack->FindKeyFrameForFrame(frame,
|
||||
B_MEDIA_SEEK_CLOSEST_BACKWARD);
|
||||
if (ret < B_OK)
|
||||
return ret;
|
||||
|
||||
if (*frame < currentFrame && wantFrame > currentFrame) {
|
||||
*frame = currentFrame;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (wantFrame != *frame)
|
||||
printf("seeked by frame: %lld -> %lld\n", wantFrame, *frame);
|
||||
|
||||
ret = fVideoTrack->SeekToFrame(frame);
|
||||
if (ret == B_OK) {
|
||||
fCurrentFrame = *frame;
|
||||
fPerformanceTime = fVideoTrack->CurrentTime();
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -297,3 +303,69 @@ string_for_color_space(color_space format)
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MediaTrackVideoSupplier::_SwitchFormat(color_space format, int32 bytesPerRow)
|
||||
{
|
||||
// get the encoded format
|
||||
memset(&fFormat, 0, sizeof(media_format));
|
||||
status_t ret = fVideoTrack->EncodedFormat(&fFormat);
|
||||
if (ret < B_OK) {
|
||||
printf("MediaTrackVideoSupplier::_SwitchFormat() - "
|
||||
"fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// get ouput video frame size
|
||||
uint32 width = fFormat.u.encoded_video.output.display.line_width;
|
||||
uint32 height = fFormat.u.encoded_video.output.display.line_count;
|
||||
|
||||
// specifiy the decoded format. we derive this information from
|
||||
// the encoded format (width & height).
|
||||
memset(&fFormat, 0, sizeof(media_format));
|
||||
// fFormat.u.raw_video.last_active = height - 1;
|
||||
// fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
|
||||
// fFormat.u.raw_video.pixel_width_aspect = 1;
|
||||
// fFormat.u.raw_video.pixel_height_aspect = 1;
|
||||
fFormat.u.raw_video.display.format = format;
|
||||
fFormat.u.raw_video.display.line_width = width;
|
||||
fFormat.u.raw_video.display.line_count = height;
|
||||
int32 minBytesPerRow;
|
||||
if (format == B_RGB32 || format == B_RGBA32)
|
||||
minBytesPerRow = width * 4;
|
||||
else if (format == B_YCbCr422)
|
||||
minBytesPerRow = ((width * 2 + 3) / 4) * 4;
|
||||
fFormat.u.raw_video.display.bytes_per_row = max_c(minBytesPerRow,
|
||||
bytesPerRow);
|
||||
|
||||
ret = fVideoTrack->DecodedFormat(&fFormat);
|
||||
|
||||
if (ret < B_OK) {
|
||||
printf("MediaTrackVideoSupplier::_SwitchFormat() - "
|
||||
"fVideoTrack->DecodedFormat(): %s\n", strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fFormat.u.raw_video.display.format != format) {
|
||||
printf("MediaTrackVideoSupplier::_SwitchFormat() - "
|
||||
" codec changed colorspace of decoded format (%s -> %s)!\n"
|
||||
" this is bad for performance, since colorspace conversion\n"
|
||||
" needs to happen during playback.\n",
|
||||
string_for_color_space(format),
|
||||
string_for_color_space(fFormat.u.raw_video.display.format));
|
||||
// check if the codec forgot to adjust bytes_per_row
|
||||
uint32 minBPR;
|
||||
format = fFormat.u.raw_video.display.format;
|
||||
if (format == B_YCbCr422)
|
||||
minBPR = ((width * 2 + 3) / 4) * 4;
|
||||
else
|
||||
minBPR = width * 4;
|
||||
if (minBPR != fFormat.u.raw_video.display.bytes_per_row) {
|
||||
printf(" -> stupid codec forgot to adjust bytes_per_row!\n");
|
||||
fFormat.u.raw_video.display.bytes_per_row = minBPR;
|
||||
fVideoTrack->DecodedFormat(&fFormat);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2007, Haiku. All rights reserved.
|
||||
* Copyright 2007-2008, Haiku. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
@@ -8,40 +8,51 @@
|
||||
#ifndef MEDIA_TRACK_VIDEO_SUPPLIER_H
|
||||
#define MEDIA_TRACK_VIDEO_SUPPLIER_H
|
||||
|
||||
#include "VideoSupplier.h"
|
||||
#include "VideoTrackSupplier.h"
|
||||
|
||||
#include <MediaFormats.h>
|
||||
|
||||
class BMediaTrack;
|
||||
|
||||
class MediaTrackVideoSupplier : public VideoSupplier {
|
||||
class MediaTrackVideoSupplier : public VideoTrackSupplier {
|
||||
public:
|
||||
MediaTrackVideoSupplier(BMediaTrack* track,
|
||||
color_space preferredFormat);
|
||||
virtual ~MediaTrackVideoSupplier();
|
||||
|
||||
virtual media_format Format() const;
|
||||
virtual const media_format& Format() const;
|
||||
virtual status_t GetEncodedFormat(media_format* format) const;
|
||||
virtual status_t GetCodecInfo(media_codec_info* info) const;
|
||||
|
||||
virtual status_t ReadFrame(void* buffer,
|
||||
bigtime_t* performanceTime);
|
||||
bigtime_t* performanceTime,
|
||||
const media_format* format,
|
||||
bool& wasCached);
|
||||
virtual status_t SeekToTime(bigtime_t* performanceTime);
|
||||
virtual status_t SeekToFrame(int64* frame);
|
||||
|
||||
virtual bigtime_t Position() const
|
||||
{ return fPerformanceTime; }
|
||||
virtual bigtime_t Duration() const
|
||||
{ return fDuration; }
|
||||
virtual int64 CurrentFrame() const
|
||||
{ return fCurrentFrame; }
|
||||
|
||||
virtual BRect Bounds() const;
|
||||
virtual color_space ColorSpace() const;
|
||||
virtual uint32 BytesPerRow() const;
|
||||
|
||||
private:
|
||||
status_t _SwitchFormat(color_space format,
|
||||
int32 bytesPerRow);
|
||||
|
||||
BMediaTrack* fVideoTrack;
|
||||
|
||||
media_format fFormat;
|
||||
|
||||
bigtime_t fPerformanceTime;
|
||||
bigtime_t fDuration;
|
||||
int64 fCurrentFrame;
|
||||
};
|
||||
|
||||
#endif // MEDIA_TRACK_VIDEO_SUPPLIER_H
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright © 2008 Stephan Aßmus <superstippi@gmx.de>
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include "ProxyAudioSupplier.h"
|
||||
|
||||
#include <algobase.h>
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Autolock.h>
|
||||
#include <List.h>
|
||||
|
||||
#include "AudioAdapter.h"
|
||||
#include "AudioTrackSupplier.h"
|
||||
#include "PlaybackManager.h"
|
||||
|
||||
using std::nothrow;
|
||||
|
||||
|
||||
//#define TRACE_PROXY_AUDIO_SUPPLIER
|
||||
#ifdef TRACE_PROXY_AUDIO_SUPPLIER
|
||||
# define TRACE(x...) printf("ProxyAudioSupplier::"); printf(x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
#endif
|
||||
|
||||
|
||||
struct PlayingInterval {
|
||||
PlayingInterval(bigtime_t startTime, bigtime_t endTime)
|
||||
: start_time(startTime)
|
||||
, end_time(endTime)
|
||||
{
|
||||
}
|
||||
|
||||
bigtime_t start_time;
|
||||
bigtime_t end_time;
|
||||
bigtime_t x_start_time;
|
||||
bigtime_t x_end_time;
|
||||
float speed;
|
||||
};
|
||||
|
||||
|
||||
ProxyAudioSupplier::ProxyAudioSupplier(PlaybackManager* playbackManager)
|
||||
: fPlaybackManager(playbackManager)
|
||||
, fVideoFrameRate(25.0)
|
||||
|
||||
, fSupplier(NULL)
|
||||
, fAdapter(NULL)
|
||||
, fAudioResampler()
|
||||
{
|
||||
TRACE("ProxyAudioSupplier()\n");
|
||||
}
|
||||
|
||||
|
||||
ProxyAudioSupplier::~ProxyAudioSupplier()
|
||||
{
|
||||
TRACE("~ProxyAudioSupplier()\n");
|
||||
delete fAdapter;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ProxyAudioSupplier::GetFrames(void* buffer, int64 frameCount,
|
||||
bigtime_t startTime, bigtime_t endTime)
|
||||
{
|
||||
TRACE("GetFrames(%p, %lld, %lld, %lld)\n", buffer, frameCount,
|
||||
startTime, endTime);
|
||||
|
||||
// Create a list of playing intervals which compose the supplied
|
||||
// performance time interval.
|
||||
BList playingIntervals;
|
||||
status_t error = fPlaybackManager->LockWithTimeout(10000);
|
||||
if (error == B_OK) {
|
||||
bigtime_t intervalStartTime = startTime;
|
||||
while (intervalStartTime < endTime) {
|
||||
PlayingInterval* interval
|
||||
= new (nothrow) PlayingInterval(intervalStartTime, endTime);
|
||||
if (!interval) {
|
||||
error = B_NO_MEMORY;
|
||||
break;
|
||||
}
|
||||
fPlaybackManager->GetPlaylistTimeInterval(
|
||||
interval->start_time, interval->end_time,
|
||||
interval->x_start_time, interval->x_end_time,
|
||||
interval->speed);
|
||||
if (!playingIntervals.AddItem(interval)) {
|
||||
delete interval;
|
||||
error = B_NO_MEMORY;
|
||||
break;
|
||||
}
|
||||
intervalStartTime = interval->end_time;
|
||||
}
|
||||
fPlaybackManager->SetCurrentAudioTime(endTime);
|
||||
fPlaybackManager->Unlock();
|
||||
} else if (error == B_TIMED_OUT) {
|
||||
TRACE("GetFrames() - LOCKING THE PLAYBACK MANAGER TIMED OUT!!!\n");
|
||||
}
|
||||
|
||||
// retrieve the audio data for each interval.
|
||||
int64 framesRead = 0;
|
||||
while (!playingIntervals.IsEmpty()) {
|
||||
PlayingInterval* interval
|
||||
= (PlayingInterval*)playingIntervals.RemoveItem(0L);
|
||||
if (error != B_OK) {
|
||||
delete interval;
|
||||
continue;
|
||||
}
|
||||
|
||||
TRACE("GetFrames() - interval [%lld, %lld]: [%lld, %lld]\n",
|
||||
interval->start_time, interval->end_time,
|
||||
interval->x_start_time, interval->x_end_time);
|
||||
|
||||
// get playing direction
|
||||
int32 playingDirection = 0;
|
||||
if (interval->speed > 0)
|
||||
playingDirection = 1;
|
||||
else if (interval->speed < 0)
|
||||
playingDirection = -1;
|
||||
float absSpeed = interval->speed * playingDirection;
|
||||
int64 framesToRead = _AudioFrameForTime(interval->end_time)
|
||||
- _AudioFrameForTime(interval->start_time);
|
||||
// not playing
|
||||
if (absSpeed == 0)
|
||||
_ReadSilence(buffer, framesToRead);
|
||||
// playing
|
||||
else {
|
||||
fAudioResampler.SetInOffset(
|
||||
_AudioFrameForTime(interval->x_start_time));
|
||||
fAudioResampler.SetTimeScale(absSpeed);
|
||||
error = fAudioResampler.Read(buffer, 0, framesToRead);
|
||||
// backwards -> reverse frames
|
||||
if (error == B_OK && interval->speed < 0)
|
||||
_ReverseFrames(buffer, framesToRead);
|
||||
}
|
||||
// read silence on error
|
||||
if (error != B_OK) {
|
||||
_ReadSilence(buffer, framesToRead);
|
||||
error = B_OK;
|
||||
}
|
||||
framesRead += framesToRead;
|
||||
buffer = _SkipFrames(buffer, framesToRead);
|
||||
delete interval;
|
||||
}
|
||||
// read silence on error
|
||||
if (error != B_OK) {
|
||||
_ReadSilence(buffer, frameCount);
|
||||
error = B_OK;
|
||||
}
|
||||
|
||||
TRACE("GetFrames() done\n");
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ProxyAudioSupplier::SetFormat(const media_format& format)
|
||||
{
|
||||
//printf("ProxyAudioSupplier::SetFormat()\n");
|
||||
#ifdef TRACE_PROXY_AUDIO_SUPPLIER
|
||||
char string[256];
|
||||
string_for_format(format, string, 256);
|
||||
TRACE("SetFormat(%s)\n", string);
|
||||
#endif
|
||||
|
||||
fAudioResampler.SetFormat(format);
|
||||
|
||||
// In case SetSupplier was called before, we need
|
||||
// to adapt to the new format, or maybe the format
|
||||
// was still invalid.
|
||||
SetSupplier(fSupplier, fVideoFrameRate);
|
||||
}
|
||||
|
||||
|
||||
const media_format&
|
||||
ProxyAudioSupplier::Format() const
|
||||
{
|
||||
return fAudioResampler.Format();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ProxyAudioSupplier::InitCheck() const
|
||||
{
|
||||
status_t ret = AudioSupplier::InitCheck();
|
||||
if (ret < B_OK)
|
||||
return ret;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ProxyAudioSupplier::SetSupplier(AudioTrackSupplier* supplier,
|
||||
float videoFrameRate)
|
||||
{
|
||||
//printf("ProxyAudioSupplier::SetSupplier(%p, %.1f)\n", supplier,
|
||||
//videoFrameRate);
|
||||
TRACE("SetSupplier(%p, %.1f)\n", supplier, videoFrameRate);
|
||||
|
||||
fSupplier = supplier;
|
||||
fVideoFrameRate = videoFrameRate;
|
||||
|
||||
delete fAdapter;
|
||||
fAdapter = new AudioAdapter(fSupplier, Format());
|
||||
|
||||
fAudioResampler.SetSource(fAdapter);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - audio/video/frame/time conversion
|
||||
|
||||
int64
|
||||
ProxyAudioSupplier::_AudioFrameForVideoFrame(int64 frame) const
|
||||
{
|
||||
if (!fSupplier) {
|
||||
return (int64)((double)frame * Format().u.raw_audio.frame_rate
|
||||
/ fVideoFrameRate);
|
||||
}
|
||||
const media_format& format = fSupplier->Format();
|
||||
return (int64)((double)frame * format.u.raw_audio.frame_rate
|
||||
/ fVideoFrameRate);
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
ProxyAudioSupplier::_VideoFrameForAudioFrame(int64 frame) const
|
||||
{
|
||||
if (!fSupplier) {
|
||||
return (int64)((double)frame * fVideoFrameRate
|
||||
/ Format().u.raw_audio.frame_rate);
|
||||
}
|
||||
|
||||
const media_format& format = fSupplier->Format();
|
||||
return (int64)((double)frame * fVideoFrameRate
|
||||
/ format.u.raw_audio.frame_rate);
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
ProxyAudioSupplier::_AudioFrameForTime(bigtime_t time) const
|
||||
{
|
||||
return (int64)((double)time * Format().u.raw_audio.frame_rate
|
||||
/ 1000000.0);
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
ProxyAudioSupplier::_VideoFrameForTime(bigtime_t time) const
|
||||
{
|
||||
return (int64)((double)time * fVideoFrameRate / 1000000.0);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - utility
|
||||
|
||||
|
||||
void
|
||||
ProxyAudioSupplier::_ReadSilence(void* buffer, int64 frames) const
|
||||
{
|
||||
memset(buffer, 0, (char*)_SkipFrames(buffer, frames) - (char*)buffer);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ProxyAudioSupplier::_ReverseFrames(void* buffer, int64 frames) const
|
||||
{
|
||||
int32 sampleSize = Format().u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
int32 frameSize = sampleSize * Format().u.raw_audio.channel_count;
|
||||
char* front = (char*)buffer;
|
||||
char* back = (char*)buffer + (frames - 1) * frameSize;
|
||||
while (front < back) {
|
||||
for (int32 i = 0; i < frameSize; i++)
|
||||
swap(front[i], back[i]);
|
||||
front += frameSize;
|
||||
back -= frameSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void*
|
||||
ProxyAudioSupplier::_SkipFrames(void* buffer, int64 frames) const
|
||||
{
|
||||
int32 sampleSize = Format().u.raw_audio.format
|
||||
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
||||
int32 frameSize = sampleSize * Format().u.raw_audio.channel_count;
|
||||
return (char*)buffer + frames * frameSize;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright © 2008 Stephan Aßmus <superstippi@gmx.de>
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef PROXY_AUDIO_SUPPLIER_H
|
||||
#define PROXY_AUDIO_SUPPLIER_H
|
||||
|
||||
|
||||
#include "AudioResampler.h"
|
||||
#include "AudioSupplier.h"
|
||||
|
||||
|
||||
class AudioTrackSupplier;
|
||||
class PlaybackManager;
|
||||
|
||||
|
||||
class ProxyAudioSupplier : public AudioSupplier {
|
||||
public:
|
||||
ProxyAudioSupplier(
|
||||
PlaybackManager* playbackManager);
|
||||
virtual ~ProxyAudioSupplier();
|
||||
|
||||
// AudioSupplier interface
|
||||
virtual status_t GetFrames(void* buffer, int64 frameCount,
|
||||
bigtime_t startTime, bigtime_t endTime);
|
||||
|
||||
virtual void SetFormat(const media_format& format);
|
||||
virtual const media_format& Format() const;
|
||||
|
||||
virtual status_t InitCheck() const;
|
||||
|
||||
// ProxyAudioSupplier
|
||||
void SetSupplier(AudioTrackSupplier* supplier,
|
||||
float videoFrameRate);
|
||||
|
||||
private:
|
||||
int64 _AudioFrameForVideoFrame(int64 frame) const;
|
||||
int64 _VideoFrameForAudioFrame(int64 frame) const;
|
||||
int64 _AudioFrameForTime(bigtime_t time) const;
|
||||
int64 _VideoFrameForTime(bigtime_t time) const;
|
||||
|
||||
void _ReadSilence(void* buffer, int64 frames) const;
|
||||
void _ReverseFrames(void* buffer,
|
||||
int64 frames) const;
|
||||
void* _SkipFrames(void* buffer, int64 frames) const;
|
||||
|
||||
private:
|
||||
PlaybackManager* fPlaybackManager;
|
||||
float fVideoFrameRate;
|
||||
|
||||
AudioTrackSupplier* fSupplier;
|
||||
AudioReader* fAdapter;
|
||||
AudioResampler fAudioResampler;
|
||||
};
|
||||
|
||||
|
||||
#endif // PROXY_AUDIO_SUPPLIER_H
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright © 2008 Stephan Aßmus <superstippi@gmx.de>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
#include "ProxyVideoSupplier.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Autolock.h>
|
||||
|
||||
#include "VideoTrackSupplier.h"
|
||||
|
||||
|
||||
ProxyVideoSupplier::ProxyVideoSupplier()
|
||||
: fSupplier(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ProxyVideoSupplier::~ProxyVideoSupplier()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ProxyVideoSupplier::FillBuffer(int64 startFrame, void* buffer,
|
||||
const media_format* format, bool& wasCached)
|
||||
{
|
||||
//printf("ProxyVideoSupplier::FillBuffer(%lld)\n", startFrame);
|
||||
if (fSupplier == NULL)
|
||||
return B_NO_INIT;
|
||||
|
||||
bigtime_t performanceTime = 0;
|
||||
if (fSupplier->CurrentFrame() != startFrame) {
|
||||
int64 frame = startFrame;
|
||||
status_t ret = fSupplier->SeekToFrame(&frame);
|
||||
if (ret != B_OK)
|
||||
return ret;
|
||||
while (frame < (startFrame - 1)) {
|
||||
ret = fSupplier->ReadFrame(buffer, &performanceTime, format,
|
||||
wasCached);
|
||||
if (ret != B_OK)
|
||||
return ret;
|
||||
frame++;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: cache into intermediate buffer!
|
||||
|
||||
return fSupplier->ReadFrame(buffer, &performanceTime, format, wasCached);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ProxyVideoSupplier::DeleteCaches()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ProxyVideoSupplier::SetSupplier(VideoTrackSupplier* supplier)
|
||||
{
|
||||
fSupplier = supplier;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright © 2008 Stephan Aßmus <superstippi@gmx.de>
|
||||
* All rights reserved. Distributed under the terms of the MIT licensce.
|
||||
*/
|
||||
#ifndef PROXY_VIDEO_SUPPLIER_H
|
||||
#define PROXY_VIDEO_SUPPLIER_H
|
||||
|
||||
|
||||
#include "VideoSupplier.h"
|
||||
|
||||
|
||||
class VideoTrackSupplier;
|
||||
|
||||
|
||||
class ProxyVideoSupplier : public VideoSupplier {
|
||||
public:
|
||||
ProxyVideoSupplier();
|
||||
virtual ~ProxyVideoSupplier();
|
||||
|
||||
virtual status_t FillBuffer(int64 startFrame, void* buffer,
|
||||
const media_format* format,
|
||||
bool& wasCached);
|
||||
|
||||
virtual void DeleteCaches();
|
||||
|
||||
void SetSupplier(VideoTrackSupplier* supplier);
|
||||
|
||||
private:
|
||||
VideoTrackSupplier* fSupplier;
|
||||
};
|
||||
|
||||
#endif // PROXY_VIDEO_SUPPLIER_H
|
||||
+3
-3
@@ -5,15 +5,15 @@
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
#include "VideoSupplier.h"
|
||||
#include "VideoTrackSupplier.h"
|
||||
|
||||
|
||||
VideoSupplier::VideoSupplier()
|
||||
VideoTrackSupplier::VideoTrackSupplier()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
VideoSupplier::~VideoSupplier()
|
||||
VideoTrackSupplier::~VideoTrackSupplier()
|
||||
{
|
||||
}
|
||||
|
||||
+12
-8
@@ -5,28 +5,32 @@
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
#ifndef VIDEO_SUPPLIER_H
|
||||
#define VIDEO_SUPPLIER_H
|
||||
#ifndef VIDEO_TRACK_SUPPLIER_H
|
||||
#define VIDEO_TRACK_SUPPLIER_H
|
||||
|
||||
|
||||
#include <MediaDefs.h>
|
||||
#include <MediaFormats.h>
|
||||
|
||||
class VideoSupplier {
|
||||
class VideoTrackSupplier {
|
||||
public:
|
||||
VideoSupplier();
|
||||
virtual ~VideoSupplier();
|
||||
VideoTrackSupplier();
|
||||
virtual ~VideoTrackSupplier();
|
||||
|
||||
virtual media_format Format() const = 0;
|
||||
virtual const media_format& Format() const = 0;
|
||||
virtual status_t GetEncodedFormat(media_format* format)
|
||||
const = 0;
|
||||
virtual status_t GetCodecInfo(media_codec_info* info) const = 0;
|
||||
virtual status_t ReadFrame(void* buffer,
|
||||
bigtime_t* performanceTime) = 0;
|
||||
bigtime_t* performanceTime,
|
||||
const media_format* format,
|
||||
bool& wasCached) = 0;
|
||||
virtual status_t SeekToTime(bigtime_t* performanceTime) = 0;
|
||||
virtual status_t SeekToFrame(int64* frame) = 0;
|
||||
|
||||
virtual bigtime_t Position() const = 0;
|
||||
virtual bigtime_t Duration() const = 0;
|
||||
virtual int64 CurrentFrame() const = 0;
|
||||
};
|
||||
|
||||
#endif // VIDEO_SUPPLIER_H
|
||||
#endif // VIDEO_TRACK_SUPPLIER_H
|
||||
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2006, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* IngoWeinhold <bonefish@cs.tu-berlin.de>
|
||||
*/
|
||||
|
||||
/** Scope-based automatic deletion of objects/arrays.
|
||||
* ObjectDeleter - deletes an object
|
||||
* ArrayDeleter - deletes an array
|
||||
* MemoryDeleter - free()s malloc()ed memory
|
||||
*/
|
||||
|
||||
#ifndef AUTO_LOCKER_H
|
||||
#define AUTO_LOCKER_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
// locking
|
||||
|
||||
// AutoLockerStandardLocking
|
||||
template<typename Lockable>
|
||||
class AutoLockerStandardLocking {
|
||||
public:
|
||||
inline bool Lock(Lockable *lockable)
|
||||
{
|
||||
return lockable->Lock();
|
||||
}
|
||||
|
||||
inline void Unlock(Lockable *lockable)
|
||||
{
|
||||
lockable->Unlock();
|
||||
}
|
||||
};
|
||||
|
||||
// AutoLockerReadLocking
|
||||
template<typename Lockable>
|
||||
class AutoLockerReadLocking {
|
||||
public:
|
||||
inline bool Lock(Lockable *lockable)
|
||||
{
|
||||
return lockable->ReadLock();
|
||||
}
|
||||
|
||||
inline void Unlock(Lockable *lockable)
|
||||
{
|
||||
lockable->ReadUnlock();
|
||||
}
|
||||
};
|
||||
|
||||
// AutoLockerWriteLocking
|
||||
template<typename Lockable>
|
||||
class AutoLockerWriteLocking {
|
||||
public:
|
||||
inline bool Lock(Lockable *lockable)
|
||||
{
|
||||
return lockable->WriteLock();
|
||||
}
|
||||
|
||||
inline void Unlock(Lockable *lockable)
|
||||
{
|
||||
lockable->WriteUnlock();
|
||||
}
|
||||
};
|
||||
|
||||
// AutoLocker
|
||||
template<typename Lockable,
|
||||
typename Locking = AutoLockerStandardLocking<Lockable> >
|
||||
class AutoLocker {
|
||||
private:
|
||||
typedef AutoLocker<Lockable, Locking> ThisClass;
|
||||
public:
|
||||
inline AutoLocker(Lockable *lockable, bool alreadyLocked = false)
|
||||
: fLockable(lockable),
|
||||
fLocked(fLockable && alreadyLocked)
|
||||
{
|
||||
if (!fLocked)
|
||||
_Lock();
|
||||
}
|
||||
|
||||
inline AutoLocker(Lockable &lockable, bool alreadyLocked = false)
|
||||
: fLockable(&lockable),
|
||||
fLocked(fLockable && alreadyLocked)
|
||||
{
|
||||
if (!fLocked)
|
||||
_Lock();
|
||||
}
|
||||
|
||||
inline ~AutoLocker()
|
||||
{
|
||||
Unlock();
|
||||
}
|
||||
|
||||
inline void SetTo(Lockable *lockable, bool alreadyLocked)
|
||||
{
|
||||
Unlock();
|
||||
fLockable = lockable;
|
||||
fLocked = alreadyLocked;
|
||||
if (!fLocked)
|
||||
_Lock();
|
||||
}
|
||||
|
||||
inline void SetTo(Lockable &lockable, bool alreadyLocked)
|
||||
{
|
||||
SetTo(&lockable, alreadyLocked);
|
||||
}
|
||||
|
||||
inline void Unset()
|
||||
{
|
||||
Unlock();
|
||||
}
|
||||
|
||||
inline AutoLocker<Lockable, Locking> &operator=(Lockable *lockable)
|
||||
{
|
||||
SetTo(lockable);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline AutoLocker<Lockable, Locking> &operator=(Lockable &lockable)
|
||||
{
|
||||
SetTo(&lockable);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool IsLocked() const { return fLocked; }
|
||||
|
||||
inline void Unlock()
|
||||
{
|
||||
if (fLockable && fLocked) {
|
||||
fLocking.Unlock(fLockable);
|
||||
fLocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
inline operator bool() const { return fLocked; }
|
||||
|
||||
private:
|
||||
inline void _Lock()
|
||||
{
|
||||
if (fLockable)
|
||||
fLocked = fLocking.Lock(fLockable);
|
||||
}
|
||||
|
||||
private:
|
||||
Lockable *fLockable;
|
||||
bool fLocked;
|
||||
Locking fLocking;
|
||||
};
|
||||
|
||||
#endif // AUTO_LOCKER_H
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
|
||||
Event::Event(bool autoDelete)
|
||||
: fTime(0),
|
||||
fAutoDelete(autoDelete)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Event::Event(bigtime_t time, bool autoDelete)
|
||||
: fTime(time),
|
||||
fAutoDelete(autoDelete)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Event::~Event()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Event::SetTime(bigtime_t time)
|
||||
{
|
||||
fTime = time;
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
Event::Time() const
|
||||
{
|
||||
return fTime;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Event::SetAutoDelete(bool autoDelete)
|
||||
{
|
||||
fAutoDelete = autoDelete;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Event::Execute()
|
||||
{
|
||||
printf("Event::Execute() - %Ld\n", fTime);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef EVENT_H
|
||||
#define EVENT_H
|
||||
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
|
||||
class Event {
|
||||
public:
|
||||
Event(bool autoDelete = true);
|
||||
Event(bigtime_t time, bool autoDelete = true);
|
||||
virtual ~Event();
|
||||
|
||||
void SetTime(bigtime_t time);
|
||||
bigtime_t Time() const;
|
||||
|
||||
void SetAutoDelete(bool autoDelete);
|
||||
bool AutoDelete() const
|
||||
{ return fAutoDelete; }
|
||||
|
||||
virtual void Execute();
|
||||
|
||||
private:
|
||||
bigtime_t fTime;
|
||||
bool fAutoDelete;
|
||||
};
|
||||
|
||||
#endif // EVENT_H
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
#include "EventQueue.h"
|
||||
|
||||
|
||||
EventQueue::EventQueue()
|
||||
: fEvents(100),
|
||||
fEventExecutor(-1),
|
||||
fThreadControl(-1),
|
||||
fNextEventTime(0),
|
||||
fStatus(B_ERROR)
|
||||
|
||||
{
|
||||
fThreadControl = create_sem(0, "event queue control");
|
||||
if (fThreadControl >= B_OK)
|
||||
fStatus = B_OK;
|
||||
else
|
||||
fStatus = fThreadControl;
|
||||
if (fStatus == B_OK) {
|
||||
fEventExecutor = spawn_thread(_execute_events_, "event queue runner",
|
||||
B_NORMAL_PRIORITY, this);
|
||||
if (fEventExecutor >= B_OK) {
|
||||
fStatus = B_OK;
|
||||
resume_thread(fEventExecutor);
|
||||
} else
|
||||
fStatus = fEventExecutor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EventQueue::~EventQueue()
|
||||
{
|
||||
if (delete_sem(fThreadControl) == B_OK)
|
||||
wait_for_thread(fEventExecutor, &fEventExecutor);
|
||||
while (Event *event = (Event*)fEvents.RemoveItem(0L)) {
|
||||
if (event->AutoDelete())
|
||||
delete event;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
EventQueue::InitCheck()
|
||||
{
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
|
||||
EventQueue*
|
||||
EventQueue::CreateDefault()
|
||||
{
|
||||
if (!fDefaultQueue) {
|
||||
fDefaultQueue = new(nothrow) EventQueue;
|
||||
if (fDefaultQueue && fDefaultQueue->InitCheck() != B_OK)
|
||||
DeleteDefault();
|
||||
}
|
||||
return fDefaultQueue;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
EventQueue::DeleteDefault()
|
||||
{
|
||||
if (fDefaultQueue) {
|
||||
delete fDefaultQueue;
|
||||
fDefaultQueue = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EventQueue&
|
||||
EventQueue::Default()
|
||||
{
|
||||
return *fDefaultQueue;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
EventQueue::AddEvent(Event* event)
|
||||
{
|
||||
Lock();
|
||||
_AddEvent(event);
|
||||
_Reschedule();
|
||||
Unlock();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
EventQueue::RemoveEvent(Event* event)
|
||||
{
|
||||
bool result = false;
|
||||
Lock();
|
||||
if ((result = fEvents.RemoveItem(event)))
|
||||
_Reschedule();
|
||||
Unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
EventQueue::ChangeEvent(Event* event, bigtime_t newTime)
|
||||
{
|
||||
Lock();
|
||||
if (fEvents.RemoveItem(event)) {
|
||||
event->SetTime(newTime);
|
||||
_AddEvent(event);
|
||||
_Reschedule();
|
||||
}
|
||||
Unlock();
|
||||
}
|
||||
|
||||
|
||||
// PRE: The object must be locked.
|
||||
void
|
||||
EventQueue::_AddEvent(Event* event)
|
||||
{
|
||||
// find the insertion index
|
||||
int32 lower = 0;
|
||||
int32 upper = fEvents.CountItems();
|
||||
while (lower < upper) {
|
||||
int32 mid = (lower + upper) / 2;
|
||||
Event* midEvent = _EventAt(mid);
|
||||
if (event->Time() < midEvent->Time())
|
||||
upper = mid;
|
||||
else
|
||||
lower = mid + 1;
|
||||
}
|
||||
fEvents.AddItem(event, lower);
|
||||
}
|
||||
|
||||
|
||||
Event*
|
||||
EventQueue::_EventAt(int32 index) const
|
||||
{
|
||||
return (Event*)fEvents.ItemAtFast(index);
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
EventQueue::_execute_events_(void* cookie)
|
||||
{
|
||||
EventQueue *gc = (EventQueue*)cookie;
|
||||
return gc->_ExecuteEvents();
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
EventQueue::_ExecuteEvents()
|
||||
{
|
||||
bool running = true;
|
||||
while (running) {
|
||||
bigtime_t waitUntil = B_INFINITE_TIMEOUT;
|
||||
if (Lock()) {
|
||||
if (!fEvents.IsEmpty())
|
||||
waitUntil = _EventAt(0)->Time();
|
||||
fNextEventTime = waitUntil;
|
||||
Unlock();
|
||||
}
|
||||
status_t err = acquire_sem_etc(fThreadControl, 1, B_ABSOLUTE_TIMEOUT,
|
||||
waitUntil);
|
||||
switch (err) {
|
||||
case B_TIMED_OUT:
|
||||
// execute events, that are supposed to go off
|
||||
if (Lock()) {
|
||||
while (!fEvents.IsEmpty()
|
||||
&& system_time() >= _EventAt(0)->Time()) {
|
||||
Event* event = (Event*)fEvents.RemoveItem(0L);
|
||||
bool deleteEvent = event->AutoDelete();
|
||||
event->Execute();
|
||||
if (deleteEvent)
|
||||
delete event;
|
||||
}
|
||||
Unlock();
|
||||
}
|
||||
break;
|
||||
case B_BAD_SEM_ID:
|
||||
running = false;
|
||||
break;
|
||||
case B_OK:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// PRE: The object must be locked.
|
||||
void
|
||||
EventQueue::_Reschedule()
|
||||
{
|
||||
if (fStatus == B_OK) {
|
||||
if (!fEvents.IsEmpty() && _EventAt(0)->Time() < fNextEventTime)
|
||||
release_sem(fThreadControl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// static variables
|
||||
|
||||
EventQueue* EventQueue::fDefaultQueue = NULL;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef EVENT_QUEUE_H
|
||||
#define EVENT_QUEUE_H
|
||||
|
||||
|
||||
#include <List.h>
|
||||
#include <Locker.h>
|
||||
#include <OS.h>
|
||||
|
||||
|
||||
class Event;
|
||||
|
||||
|
||||
class EventQueue : public BLocker {
|
||||
public:
|
||||
EventQueue();
|
||||
virtual ~EventQueue();
|
||||
|
||||
status_t InitCheck();
|
||||
|
||||
static EventQueue* CreateDefault();
|
||||
static void DeleteDefault();
|
||||
static EventQueue& Default();
|
||||
|
||||
void AddEvent(Event* event);
|
||||
bool RemoveEvent(Event* event);
|
||||
void ChangeEvent(Event* event,
|
||||
bigtime_t newTime);
|
||||
|
||||
private:
|
||||
void _AddEvent(Event* event);
|
||||
Event* _EventAt(int32 index) const;
|
||||
|
||||
static int32 _execute_events_(void *cookie);
|
||||
int32 _ExecuteEvents();
|
||||
void _Reschedule();
|
||||
|
||||
BList fEvents;
|
||||
thread_id fEventExecutor;
|
||||
sem_id fThreadControl;
|
||||
volatile bigtime_t fNextEventTime;
|
||||
status_t fStatus;
|
||||
static EventQueue* fDefaultQueue;
|
||||
};
|
||||
|
||||
#endif // EVENT_QUEUE_H
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include <Message.h>
|
||||
|
||||
#include "MessageEvent.h"
|
||||
|
||||
|
||||
MessageEvent::MessageEvent(bigtime_t time, BHandler* handler, uint32 command)
|
||||
: Event(time),
|
||||
AbstractLOAdapter(handler),
|
||||
fCommand(command)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
MessageEvent::MessageEvent(bigtime_t time, const BMessenger& messenger)
|
||||
: Event(time),
|
||||
AbstractLOAdapter(messenger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
MessageEvent::~MessageEvent()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MessageEvent::Execute()
|
||||
{
|
||||
BMessage msg(fCommand);
|
||||
msg.AddInt64("time", Time());
|
||||
DeliverMessage(msg);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
|
||||
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
|
||||
* All Rights Reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef MESSAGE_EVENT_H
|
||||
#define MESSAGE_EVENT_H
|
||||
|
||||
|
||||
#include "AbstractLOAdapter.h"
|
||||
#include "Event.h"
|
||||
|
||||
|
||||
enum {
|
||||
MSG_EVENT = 'evnt',
|
||||
};
|
||||
|
||||
|
||||
class MessageEvent : public Event, public AbstractLOAdapter {
|
||||
public:
|
||||
MessageEvent(bigtime_t time,
|
||||
BHandler* handler,
|
||||
uint32 command = MSG_EVENT);
|
||||
MessageEvent(bigtime_t time,
|
||||
const BMessenger& messenger);
|
||||
virtual ~MessageEvent();
|
||||
|
||||
virtual void Execute();
|
||||
|
||||
private:
|
||||
uint32 fCommand;
|
||||
};
|
||||
|
||||
#endif // MESSAGE_EVENT_H
|
||||
Reference in New Issue
Block a user