* 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:
+256
-758
File diff suppressed because it is too large
Load Diff
+107
-158
@@ -2,7 +2,7 @@
|
|||||||
* Controller.h - Media Player for the Haiku Operating System
|
* Controller.h - Media Player for the Haiku Operating System
|
||||||
*
|
*
|
||||||
* Copyright (C) 2006 Marcus Overhagen <[email protected]>
|
* 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
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -29,207 +29,156 @@
|
|||||||
#include <Locker.h>
|
#include <Locker.h>
|
||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
class AudioSupplier;
|
#include "NodeManager.h"
|
||||||
|
|
||||||
|
class AudioTrackSupplier;
|
||||||
class BBitmap;
|
class BBitmap;
|
||||||
class BMediaFile;
|
class BMediaFile;
|
||||||
class BMediaTrack;
|
class BMediaTrack;
|
||||||
|
class ProxyAudioSupplier;
|
||||||
|
class ProxyVideoSupplier;
|
||||||
class SoundOutput;
|
class SoundOutput;
|
||||||
class VideoSupplier;
|
class VideoTrackSupplier;
|
||||||
class VideoView;
|
class VideoView;
|
||||||
|
|
||||||
class Controller {
|
class Controller : public NodeManager {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
class Listener {
|
class Listener {
|
||||||
public:
|
public:
|
||||||
Listener();
|
Listener();
|
||||||
virtual ~Listener();
|
virtual ~Listener();
|
||||||
|
|
||||||
virtual void FileFinished();
|
virtual void FileFinished();
|
||||||
virtual void FileChanged();
|
virtual void FileChanged();
|
||||||
|
|
||||||
virtual void VideoTrackChanged(int32 index);
|
virtual void VideoTrackChanged(int32 index);
|
||||||
virtual void AudioTrackChanged(int32 index);
|
virtual void AudioTrackChanged(int32 index);
|
||||||
|
|
||||||
virtual void VideoStatsChanged();
|
virtual void VideoStatsChanged();
|
||||||
virtual void AudioStatsChanged();
|
virtual void AudioStatsChanged();
|
||||||
|
|
||||||
virtual void PlaybackStateChanged(uint32 state);
|
virtual void PlaybackStateChanged(uint32 state);
|
||||||
virtual void PositionChanged(float position);
|
virtual void PositionChanged(float position);
|
||||||
virtual void VolumeChanged(float volume);
|
virtual void VolumeChanged(float volume);
|
||||||
virtual void MutedChanged(bool muted);
|
virtual void MutedChanged(bool muted);
|
||||||
};
|
};
|
||||||
|
|
||||||
Controller();
|
Controller();
|
||||||
virtual ~Controller();
|
virtual ~Controller();
|
||||||
|
|
||||||
bool Lock();
|
// PlaybackManager interface
|
||||||
status_t LockWithTimeout(bigtime_t timeout);
|
virtual int64 Duration();
|
||||||
void Unlock();
|
|
||||||
|
|
||||||
status_t SetTo(const entry_ref &ref);
|
// NodeManager interface
|
||||||
void GetSize(int *width, int *height);
|
virtual VideoTarget* CreateVideoTarget();
|
||||||
|
virtual VideoSupplier* CreateVideoSupplier();
|
||||||
|
virtual AudioSupplier* CreateAudioSupplier();
|
||||||
|
|
||||||
int AudioTrackCount();
|
// Controller
|
||||||
int VideoTrackCount();
|
status_t SetTo(const entry_ref &ref);
|
||||||
|
void GetSize(int *width, int *height);
|
||||||
|
|
||||||
status_t SelectAudioTrack(int n);
|
int AudioTrackCount();
|
||||||
status_t SelectVideoTrack(int n);
|
int VideoTrackCount();
|
||||||
|
|
||||||
void Stop();
|
status_t SelectAudioTrack(int n);
|
||||||
void Play();
|
status_t SelectVideoTrack(int n);
|
||||||
void Pause();
|
|
||||||
void TogglePlaying();
|
|
||||||
|
|
||||||
bool IsPaused() const;
|
void Stop();
|
||||||
bool IsStopped() const;
|
void Play();
|
||||||
uint32 PlaybackState() const;
|
void Pause();
|
||||||
|
void TogglePlaying();
|
||||||
|
|
||||||
bigtime_t Duration();
|
uint32 PlaybackState();
|
||||||
bigtime_t Position();
|
|
||||||
|
|
||||||
void SetVolume(float value);
|
bigtime_t TimeDuration();
|
||||||
float Volume() const;
|
bigtime_t TimePosition();
|
||||||
void VolumeUp();
|
|
||||||
void VolumeDown();
|
|
||||||
void ToggleMute();
|
|
||||||
void SetPosition(float value);
|
|
||||||
|
|
||||||
bool HasFile();
|
virtual void SetVolume(float percent);
|
||||||
status_t GetFileFormatInfo(media_file_format* fileFormat);
|
float Volume();
|
||||||
status_t GetCopyright(BString* copyright);
|
void VolumeUp();
|
||||||
status_t GetLocation(BString* location);
|
void VolumeDown();
|
||||||
status_t GetName(BString* name);
|
void ToggleMute();
|
||||||
status_t GetEncodedVideoFormat(media_format* format);
|
void SetPosition(float value);
|
||||||
status_t GetVideoCodecInfo(media_codec_info* info);
|
|
||||||
status_t GetEncodedAudioFormat(media_format* format);
|
bool HasFile();
|
||||||
status_t GetAudioCodecInfo(media_codec_info* info);
|
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
|
// video view
|
||||||
void SetVideoView(VideoView *view);
|
void SetVideoView(VideoView *view);
|
||||||
|
|
||||||
bool IsOverlayActive();
|
bool IsOverlayActive();
|
||||||
|
|
||||||
bool LockBitmap();
|
|
||||||
void UnlockBitmap();
|
|
||||||
BBitmap * Bitmap();
|
|
||||||
|
|
||||||
// notification support
|
// notification support
|
||||||
bool AddListener(Listener* listener);
|
bool AddListener(Listener* listener);
|
||||||
void RemoveListener(Listener* listener);
|
void RemoveListener(Listener* listener);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _AudioDecodeThread();
|
uint32 _PlaybackState(int32 playingMode) const;
|
||||||
void _AudioPlayThread();
|
void _EndOfStreamReached(bool isVideo = false);
|
||||||
|
|
||||||
void _VideoDecodeThread();
|
void _NotifyFileChanged() const;
|
||||||
void _VideoPlayThread();
|
void _NotifyFileFinished() const;
|
||||||
|
void _NotifyVideoTrackChanged(int32 index) const;
|
||||||
|
void _NotifyAudioTrackChanged(int32 index) const;
|
||||||
|
|
||||||
void _StartThreads();
|
void _NotifyVideoStatsChanged() const;
|
||||||
void _StopThreads();
|
void _NotifyAudioStatsChanged() const;
|
||||||
|
|
||||||
void _EndOfStreamReached(bool isVideo = false);
|
void _NotifyPlaybackStateChanged(uint32 state) const;
|
||||||
void _UpdatePosition(bigtime_t position,
|
void _NotifyPositionChanged(float position) const;
|
||||||
bool isVideoPosition = false,
|
void _NotifyVolumeChanged(float volume) const;
|
||||||
bool force = false);
|
void _NotifyMutedChanged(bool muted) const;
|
||||||
|
|
||||||
static int32 _VideoDecodeThreadEntry(void *self);
|
// overridden from PlaybackManager so that we
|
||||||
static int32 _VideoPlayThreadEntry(void *self);
|
// can use our own Listener mechanism
|
||||||
static int32 _AudioDecodeThreadEntry(void *self);
|
virtual void NotifyPlayModeChanged(int32 mode) const;
|
||||||
static int32 _AudioPlayThreadEntry(void *self);
|
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();
|
VideoView* fVideoView;
|
||||||
void _NotifyAudioStatsChanged();
|
volatile bool fPaused;
|
||||||
|
volatile bool fStopped;
|
||||||
|
float fVolume;
|
||||||
|
bool fMuted;
|
||||||
|
|
||||||
void _NotifyPlaybackStateChanged();
|
entry_ref fRef;
|
||||||
void _NotifyPositionChanged(float position);
|
BMediaFile* fMediaFile;
|
||||||
void _NotifyVolumeChanged(float volume);
|
|
||||||
void _NotifyMutedChanged(bool muted);
|
|
||||||
|
|
||||||
friend class InfoWin;
|
ProxyVideoSupplier* fVideoSupplier;
|
||||||
|
ProxyAudioSupplier* fAudioSupplier;
|
||||||
|
VideoTrackSupplier* fVideoTrackSupplier;
|
||||||
|
AudioTrackSupplier* fAudioTrackSupplier;
|
||||||
|
|
||||||
enum {
|
BList fAudioTrackList;
|
||||||
MAX_AUDIO_BUFFERS = 8,
|
BList fVideoTrackList;
|
||||||
MAX_VIDEO_BUFFERS = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct buffer_info {
|
mutable bigtime_t fPosition;
|
||||||
char * buffer;
|
bigtime_t fDuration;
|
||||||
BBitmap * bitmap;
|
float fVideoFrameRate;
|
||||||
size_t sizeUsed;
|
|
||||||
size_t sizeMax;
|
|
||||||
bigtime_t startTime;
|
|
||||||
bool formatChanged;
|
|
||||||
bool endOfStream;
|
|
||||||
media_format mediaFormat;
|
|
||||||
};
|
|
||||||
|
|
||||||
VideoView * fVideoView;
|
bool fAutoplay;
|
||||||
volatile bool fPaused;
|
volatile bool fPauseAtEndOfStream;
|
||||||
volatile bool fStopped;
|
volatile bool fSeekToStartAfterPause;
|
||||||
float fVolume;
|
|
||||||
bool fMuted;
|
|
||||||
|
|
||||||
entry_ref fRef;
|
BList fListeners;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -227,6 +227,9 @@ printf("InfoWin::Update(0x%08lx)\n", which);
|
|||||||
fContentsView->SetFontAndColor(be_plain_font, B_FONT_ALL);
|
fContentsView->SetFontAndColor(be_plain_font, B_FONT_ALL);
|
||||||
// fContentsView->Insert("");
|
// fContentsView->Insert("");
|
||||||
|
|
||||||
|
if (!fController->Lock())
|
||||||
|
return;
|
||||||
|
|
||||||
fLabelsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kRed);
|
fLabelsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kRed);
|
||||||
|
|
||||||
status_t err;
|
status_t err;
|
||||||
|
|||||||
@@ -2,13 +2,16 @@ SubDir HAIKU_TOP src apps mediaplayer ;
|
|||||||
|
|
||||||
SetSubDirSupportedPlatformsBeOSCompatible ;
|
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||||
|
|
||||||
AddSubDirSupportedPlatforms libbe_test ;
|
|
||||||
|
|
||||||
# for BRecentItems
|
# for BRecentItems
|
||||||
UsePublicHeaders [ FDirName be_apps Tracker ] ;
|
UsePublicHeaders [ FDirName be_apps Tracker ] ;
|
||||||
|
UsePrivateHeaders shared ;
|
||||||
|
|
||||||
# source directories
|
# source directories
|
||||||
local sourceDirs =
|
local sourceDirs =
|
||||||
|
interface
|
||||||
|
media_node_framework
|
||||||
|
media_node_framework/audio
|
||||||
|
media_node_framework/video
|
||||||
playlist
|
playlist
|
||||||
supplier
|
supplier
|
||||||
support
|
support
|
||||||
@@ -21,6 +24,33 @@ for sourceDir in $(sourceDirs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Application MediaPlayer :
|
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
|
# playlist
|
||||||
CopyPLItemsCommand.cpp
|
CopyPLItemsCommand.cpp
|
||||||
ImportPLItemsCommand.cpp
|
ImportPLItemsCommand.cpp
|
||||||
@@ -32,22 +62,27 @@ Application MediaPlayer :
|
|||||||
PlaylistWindow.cpp
|
PlaylistWindow.cpp
|
||||||
RemovePLItemsCommand.cpp
|
RemovePLItemsCommand.cpp
|
||||||
|
|
||||||
# supplier
|
# settings
|
||||||
Settings.cpp
|
Settings.cpp
|
||||||
SettingsWindow.cpp
|
SettingsWindow.cpp
|
||||||
|
|
||||||
# supplier
|
# supplier
|
||||||
AudioSupplier.cpp
|
AudioTrackSupplier.cpp
|
||||||
MediaTrackAudioSupplier.cpp
|
MediaTrackAudioSupplier.cpp
|
||||||
MediaTrackVideoSupplier.cpp
|
MediaTrackVideoSupplier.cpp
|
||||||
VideoSupplier.cpp
|
ProxyAudioSupplier.cpp
|
||||||
|
ProxyVideoSupplier.cpp
|
||||||
|
VideoTrackSupplier.cpp
|
||||||
|
|
||||||
# support
|
# support
|
||||||
AbstractLOAdapter.cpp
|
AbstractLOAdapter.cpp
|
||||||
Command.cpp
|
Command.cpp
|
||||||
CommandStack.cpp
|
CommandStack.cpp
|
||||||
|
Event.cpp
|
||||||
|
EventQueue.cpp
|
||||||
Listener.cpp
|
Listener.cpp
|
||||||
ListenerAdapter.cpp
|
ListenerAdapter.cpp
|
||||||
|
MessageEvent.cpp
|
||||||
Notifier.cpp
|
Notifier.cpp
|
||||||
RWLocker.cpp
|
RWLocker.cpp
|
||||||
FileReadWrite.cpp
|
FileReadWrite.cpp
|
||||||
@@ -57,23 +92,13 @@ Application MediaPlayer :
|
|||||||
Controller.cpp
|
Controller.cpp
|
||||||
ControllerObserver.cpp
|
ControllerObserver.cpp
|
||||||
ControllerView.cpp
|
ControllerView.cpp
|
||||||
DrawingTidbits.cpp
|
|
||||||
InfoWin.cpp
|
InfoWin.cpp
|
||||||
MainApp.cpp
|
MainApp.cpp
|
||||||
MainWin.cpp
|
MainWin.cpp
|
||||||
SoundOutput.cpp
|
|
||||||
TransportButton.cpp
|
|
||||||
TransportControlGroup.cpp
|
TransportControlGroup.cpp
|
||||||
SeekSlider.cpp
|
|
||||||
VideoNode.cpp
|
|
||||||
VideoView.cpp
|
VideoView.cpp
|
||||||
VolumeSlider.cpp
|
|
||||||
|
|
||||||
: be media tracker translation textencoding $(TARGET_LIBSTDC++)
|
: be media tracker translation textencoding $(TARGET_LIBSTDC++)
|
||||||
: MediaPlayer.rdef
|
: 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 <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "EventQueue.h"
|
||||||
|
|
||||||
|
|
||||||
MainApp *gMainApp;
|
MainApp *gMainApp;
|
||||||
const char* kAppSig = "application/x-vnd.Haiku-MediaPlayer";
|
const char* kAppSig = "application/x-vnd.Haiku-MediaPlayer";
|
||||||
@@ -221,8 +223,13 @@ MainApp::_BroadcastMessage(const BMessage& _message)
|
|||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
|
EventQueue::CreateDefault();
|
||||||
|
|
||||||
gMainApp = new MainApp;
|
gMainApp = new MainApp;
|
||||||
gMainApp->Run();
|
gMainApp->Run();
|
||||||
delete gMainApp;
|
delete gMainApp;
|
||||||
|
|
||||||
|
EventQueue::DeleteDefault();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,8 +150,7 @@ MainWin::MainWin()
|
|||||||
// video view
|
// video view
|
||||||
rect = BRect(0, fMenuBarHeight, fBackground->Bounds().right,
|
rect = BRect(0, fMenuBarHeight, fBackground->Bounds().right,
|
||||||
fMenuBarHeight + 10);
|
fMenuBarHeight + 10);
|
||||||
fVideoView = new VideoView(rect, "video display", B_FOLLOW_NONE,
|
fVideoView = new VideoView(rect, "video display", B_FOLLOW_NONE);
|
||||||
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
|
|
||||||
fBackground->AddChild(fVideoView);
|
fBackground->AddChild(fVideoView);
|
||||||
|
|
||||||
// controls
|
// controls
|
||||||
@@ -171,7 +170,6 @@ MainWin::MainWin()
|
|||||||
fPlaylist->AddListener(fPlaylistObserver);
|
fPlaylist->AddListener(fPlaylistObserver);
|
||||||
fController->SetVideoView(fVideoView);
|
fController->SetVideoView(fVideoView);
|
||||||
fController->AddListener(fControllerObserver);
|
fController->AddListener(fControllerObserver);
|
||||||
fVideoView->IsOverlaySupported();
|
|
||||||
|
|
||||||
// printf("fMenuBarHeight %d\n", fMenuBarHeight);
|
// printf("fMenuBarHeight %d\n", fMenuBarHeight);
|
||||||
// printf("fControlsHeight %d\n", fControlsHeight);
|
// printf("fControlsHeight %d\n", fControlsHeight);
|
||||||
@@ -224,8 +222,13 @@ MainWin::~MainWin()
|
|||||||
}
|
}
|
||||||
|
|
||||||
delete fPlaylist;
|
delete fPlaylist;
|
||||||
delete fController;
|
|
||||||
delete fFilePanel;
|
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) {
|
switch (raw_char) {
|
||||||
case B_SPACE:
|
case B_SPACE:
|
||||||
if (fController->IsPaused() || fController->IsStopped())
|
fController->TogglePlaying();
|
||||||
fController->Play();
|
|
||||||
else
|
|
||||||
fController->Pause();
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
case B_ESCAPE:
|
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
|
|
||||||
+117
-217
@@ -1,38 +1,27 @@
|
|||||||
/*
|
/*
|
||||||
* VideoView.cpp - Media Player for the Haiku Operating System
|
* Copyright © 2006-2008 Stephan Aßmus <supersti[email protected]>
|
||||||
*
|
* All rights reserved. Distributed under the terms of the MIT license.
|
||||||
* 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 <Message.h>
|
|
||||||
#include <Bitmap.h>
|
|
||||||
#include "VideoView.h"
|
#include "VideoView.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
VideoView::VideoView(BRect frame, const char *name, uint32 resizeMask, uint32 flags)
|
#include <Bitmap.h>
|
||||||
: BView(frame, name, resizeMask, flags)
|
|
||||||
, fController(NULL)
|
|
||||||
, fOverlayActive(false)
|
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(B_TRANSPARENT_COLOR);
|
||||||
// SetViewColor(127,255,127);
|
// might be reset to overlay key color if overlays are used
|
||||||
rgb_color r = {255, 0, 0, 255};
|
SetHighColor(0, 0, 0);
|
||||||
fOverlayKeyColor = r;
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -42,228 +31,139 @@ VideoView::~VideoView()
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
VideoView::SetController(Controller *controller)
|
VideoView::Draw(BRect updateRect)
|
||||||
{
|
{
|
||||||
fController = controller;
|
bool fillBlack = true;
|
||||||
}
|
|
||||||
|
|
||||||
|
if (LockBitmap()) {
|
||||||
void
|
BRect r(Bounds());
|
||||||
VideoView::AttachedToWindow()
|
if (const BBitmap* bitmap = GetBitmap()) {
|
||||||
{
|
fillBlack = false;
|
||||||
}
|
if (!fOverlayMode)
|
||||||
|
DrawBitmap(bitmap, bitmap->Bounds(), r);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
UnlockBitmap();
|
||||||
}
|
}
|
||||||
fController->UnlockBitmap();
|
|
||||||
*/
|
if (fillBlack)
|
||||||
printf("OverlayScreenshotPrepare leave\n");
|
FillRect(updateRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
VideoView::OverlayScreenshotCleanup()
|
VideoView::SetBitmap(const BBitmap* bitmap)
|
||||||
{
|
{
|
||||||
printf("OverlayScreenshotCleanup enter\n");
|
VideoTarget::SetBitmap(bitmap);
|
||||||
/*
|
// Attention: Don't lock the window, if the bitmap is NULL. Otherwise
|
||||||
snooze(50000); // give app server some time to take the screenshot
|
// we're going to deadlock when the window tells the node manager to
|
||||||
fController->LockBitmap();
|
// stop the nodes (Window -> NodeManager -> VideoConsumer -> VideoView
|
||||||
if (fOverlayActive) {
|
// -> Window).
|
||||||
BBitmap *bmp = fController->Bitmap();
|
if (bitmap && LockLooperWithTimeout(10000) == B_OK) {
|
||||||
if (bmp) {
|
if (LockBitmap()) {
|
||||||
DrawBitmap(bmp, Bounds());
|
// if (fOverlayMode || bitmap->Flags() & B_BITMAP_WILL_OVERLAY) {
|
||||||
SetViewOverlay(bmp, bmp->Bounds(), Bounds(), &fOverlayKeyColor,
|
if (fOverlayMode || bitmap->ColorSpace() == B_YCbCr422) {
|
||||||
B_FOLLOW_ALL, B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL);
|
if (!fOverlayMode) {
|
||||||
Invalidate();
|
// 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;
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
SetViewColor(B_TRANSPARENT_COLOR);
|
||||||
|
}
|
||||||
|
if (!fOverlayMode)
|
||||||
|
DrawBitmap(bitmap, bitmap->Bounds(), Bounds());
|
||||||
|
|
||||||
|
UnlockBitmap();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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();
|
UnlockLooper();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
VideoView::Draw(BRect updateRect)
|
VideoView::GetOverlayScaleLimits(float* minScale, float* maxScale) const
|
||||||
{
|
{
|
||||||
if (fOverlayActive) {
|
*minScale = max_c(fOverlayRestrictions.min_width_scale,
|
||||||
SetHighColor(fOverlayKeyColor);
|
fOverlayRestrictions.min_height_scale);
|
||||||
FillRect(updateRect);
|
*maxScale = max_c(fOverlayRestrictions.max_width_scale,
|
||||||
} else {
|
fOverlayRestrictions.max_height_scale);
|
||||||
fController->LockBitmap();
|
|
||||||
BBitmap *bmp = fController->Bitmap();
|
|
||||||
if (bmp)
|
|
||||||
DrawBitmap(bmp, Bounds());
|
|
||||||
fController->UnlockBitmap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
VideoView::DrawFrame()
|
VideoView::OverlayScreenshotPrepare()
|
||||||
{
|
{
|
||||||
// printf("VideoView::DrawFrame\n");
|
// TODO: Do nothing if the current bitmap is in RGB color space
|
||||||
|
// and no overlay. Otherwise, convert current bitmap to RGB color
|
||||||
if (LockLooperWithTimeout(50000) != B_OK)
|
// space an draw it in place of the normal display.
|
||||||
return;
|
|
||||||
|
|
||||||
fController->LockBitmap();
|
|
||||||
BBitmap *bmp = fController->Bitmap();
|
|
||||||
|
|
||||||
if (bmp) {
|
|
||||||
bool want_overlay = bmp->ColorSpace() == B_YCbCr422;
|
|
||||||
|
|
||||||
if (!want_overlay && fOverlayActive) {
|
|
||||||
if (LockLooperWithTimeout(50000) == B_OK) {
|
|
||||||
ClearViewOverlay();
|
|
||||||
UnlockLooper();
|
|
||||||
fOverlayActive = false;
|
|
||||||
} else {
|
|
||||||
printf("can't ClearViewOverlay, as LockLooperWithTimeout failed\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fController->UnlockBitmap();
|
|
||||||
|
|
||||||
UnlockLooper();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
VideoView::MessageReceived(BMessage *msg)
|
VideoView::OverlayScreenshotCleanup()
|
||||||
{
|
{
|
||||||
switch (msg->what) {
|
// TODO: Do nothing if the current bitmap is in RGB color space
|
||||||
|
// and no overlay. Otherwise clean view area with overlay color.
|
||||||
default:
|
|
||||||
BView::MessageReceived(msg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VideoView::IsOverlaySupported()
|
VideoView::IsOverlayActive()
|
||||||
{
|
{
|
||||||
struct colorcombo {
|
bool active = false;
|
||||||
color_space colspace;
|
if (LockBitmap()) {
|
||||||
const char *name;
|
active = fOverlayMode;
|
||||||
} colspace[] = {
|
UnlockBitmap();
|
||||||
{ 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;
|
|
||||||
}
|
}
|
||||||
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 © 2006-2008 Stephan Aßmus <supersti[email protected]>
|
||||||
*
|
* All rights reserved. Distributed under the terms of the MIT license.
|
||||||
* 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 __VIDEO_VIEW_H
|
#ifndef VIDEO_VIEW_H
|
||||||
#define __VIDEO_VIEW_H
|
#define VIDEO_VIEW_H
|
||||||
|
|
||||||
|
|
||||||
#include <View.h>
|
#include <View.h>
|
||||||
#include "Controller.h"
|
|
||||||
|
|
||||||
class VideoView : public BView
|
#include "VideoTarget.h"
|
||||||
{
|
|
||||||
|
|
||||||
|
class VideoView : public BView, public VideoTarget {
|
||||||
public:
|
public:
|
||||||
VideoView(BRect frame, const char *name, uint32 resizeMask, uint32 flags);
|
VideoView(BRect frame, const char* name,
|
||||||
~VideoView();
|
uint32 resizeMask);
|
||||||
|
virtual ~VideoView();
|
||||||
|
|
||||||
void SetController(Controller *controller);
|
// BView interface
|
||||||
|
virtual void Draw(BRect updateRect);
|
||||||
|
|
||||||
void RemoveVideoDisplay();
|
// VideoTarget interface
|
||||||
void RemoveOverlay();
|
virtual void SetBitmap(const BBitmap* bitmap);
|
||||||
|
|
||||||
bool IsOverlaySupported();
|
// VideoView
|
||||||
|
void GetOverlayScaleLimits(float* minScale,
|
||||||
|
float* maxScale) const;
|
||||||
|
|
||||||
void OverlayLockAcquire();
|
void OverlayScreenshotPrepare();
|
||||||
void OverlayLockRelease();
|
void OverlayScreenshotCleanup();
|
||||||
|
|
||||||
void OverlayScreenshotPrepare();
|
bool IsOverlayActive();
|
||||||
void OverlayScreenshotCleanup();
|
void DisableOverlay();
|
||||||
|
|
||||||
void DrawFrame();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AttachedToWindow();
|
bool fOverlayMode;
|
||||||
void MessageReceived(BMessage *msg);
|
overlay_restrictions fOverlayRestrictions;
|
||||||
void Draw(BRect updateRect);
|
rgb_color fOverlayKeyColor;
|
||||||
|
|
||||||
private:
|
|
||||||
Controller * fController;
|
|
||||||
volatile bool fOverlayActive;
|
|
||||||
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 <[email protected]>,
|
||||||
|
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||||
|
* 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 <[email protected]>,
|
||||||
|
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||||
|
* 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 <[email protected]>,
|
||||||
|
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||||
|
* 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 <[email protected]>
|
||||||
|
* Copyright 2001-2008 Stephan Aßmus <[email protected]>
|
||||||
|
* 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 <[email protected]>
|
||||||
|
* Copyright 2001-2008 Stephan Aßmus <[email protected]>
|
||||||
|
* 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 <[email protected]> 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 <[email protected]> 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 <[email protected]>
|
|
||||||
*/
|
|
||||||
#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:
|
* Authors:
|
||||||
* Stephan Aßmus <superstippi@gmx.de>
|
* 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 <[email protected]>
|
||||||
|
*/
|
||||||
|
#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.
|
* Copyright © 2000-2004 Ingo Weinhold <[email protected]>
|
||||||
* Distributed under the terms of the MIT License.
|
* Copyright © 2006-2008 Stephan Aßmus <[email protected]>
|
||||||
*
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
* Authors:
|
|
||||||
* Stephan Aßmus <[email protected]>
|
|
||||||
*/
|
*/
|
||||||
#include "MediaTrackAudioSupplier.h"
|
#include "MediaTrackAudioSupplier.h"
|
||||||
|
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <algobase.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <MediaFile.h>
|
||||||
#include <MediaTrack.h>
|
#include <MediaTrack.h>
|
||||||
|
|
||||||
using std::nothrow;
|
using std::nothrow;
|
||||||
|
|
||||||
// constructor
|
|
||||||
MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* track)
|
|
||||||
: AudioSupplier()
|
|
||||||
, fMediaTrack(track)
|
|
||||||
|
|
||||||
, fPerformanceTime(0)
|
//#define TRACE_AUDIO_SUPPLIER
|
||||||
, fDuration(0)
|
#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) {
|
const Buffer* buffer1 = *(const Buffer**)a;
|
||||||
printf("MediaTrackAudioSupplier() - no media track\n");
|
const Buffer* buffer2 = *(const Buffer**)b;
|
||||||
return;
|
int result = 0;
|
||||||
}
|
if (buffer1->offset < buffer2->offset)
|
||||||
|
result = -1;
|
||||||
fFormat.u.raw_audio = media_multi_audio_format::wildcard;
|
else if (buffer1->offset > buffer2->offset)
|
||||||
#ifdef __HAIKU__
|
result = 1;
|
||||||
fFormat.u.raw_audio.format = media_multi_audio_format::B_AUDIO_FLOAT;
|
return result;
|
||||||
#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);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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()
|
MediaTrackAudioSupplier::~MediaTrackAudioSupplier()
|
||||||
{
|
{
|
||||||
|
_FreeBuffers();
|
||||||
|
delete[] fBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
media_format
|
const media_format&
|
||||||
MediaTrackAudioSupplier::Format() const
|
MediaTrackAudioSupplier::Format() const
|
||||||
{
|
{
|
||||||
return fFormat;
|
return AudioReader::Format();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -80,53 +101,530 @@ MediaTrackAudioSupplier::GetCodecInfo(media_codec_info* info) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
bigtime_t
|
||||||
MediaTrackAudioSupplier::ReadFrames(void* buffer, int64* framesRead,
|
MediaTrackAudioSupplier::Duration() const
|
||||||
bigtime_t* performanceTime)
|
|
||||||
{
|
{
|
||||||
if (!fMediaTrack)
|
if (!fMediaTrack)
|
||||||
return B_NO_INIT;
|
return 0;
|
||||||
if (!buffer || !framesRead)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
|
|
||||||
media_header mediaHeader;
|
return fMediaTrack->Duration();
|
||||||
status_t ret = fMediaTrack->ReadFrames(buffer, framesRead, &mediaHeader);
|
}
|
||||||
|
|
||||||
if (ret < B_OK) {
|
|
||||||
// further analyse the error
|
// #pragma mark - AudioReader
|
||||||
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
|
// Read
|
||||||
// NOTE: "more than 95% of the stream" is of course pure guess,
|
status_t
|
||||||
// but it fixed the problem I had with some files
|
MediaTrackAudioSupplier::Read(void* buffer, int64 pos, int64 frames)
|
||||||
ret = B_LAST_BUFFER_ERROR;
|
{
|
||||||
|
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() - "
|
buffer->data = data;
|
||||||
"error while reading frames: %s\n", strerror(ret));
|
data += bufferSize;
|
||||||
} else {
|
buffer->offset = 0;
|
||||||
fPerformanceTime = mediaHeader.start_time;
|
buffer->size = 0;
|
||||||
|
buffer->time_stamp = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (performanceTime)
|
|
||||||
*performanceTime = fPerformanceTime;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _FreeBuffers
|
||||||
status_t
|
//
|
||||||
MediaTrackAudioSupplier::SeekToTime(bigtime_t* performanceTime)
|
// Frees the allocated buffers.
|
||||||
|
void
|
||||||
|
MediaTrackAudioSupplier::_FreeBuffers()
|
||||||
{
|
{
|
||||||
if (!fMediaTrack)
|
if (fBuffers.CountItems() > 0) {
|
||||||
return B_NO_INIT;
|
delete[] (char*)_BufferAt(0)->data;
|
||||||
|
for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++)
|
||||||
bigtime_t _performanceTime = *performanceTime;
|
delete buffer;
|
||||||
status_t ret = fMediaTrack->SeekToTime(performanceTime);
|
fBuffers.MakeEmpty();
|
||||||
if (ret == B_OK) {
|
|
||||||
printf("seeked: %lld -> %lld\n", _performanceTime, *performanceTime);
|
|
||||||
fPerformanceTime = *performanceTime;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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.
|
* Copyright © 2000-2004 Ingo Weinhold <[email protected]>
|
||||||
* Distributed under the terms of the MIT License.
|
* Copyright © 2006-2008 Stephan Aßmus <[email protected]>
|
||||||
*
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
* Authors:
|
|
||||||
* Stephan Aßmus <[email protected]>
|
|
||||||
*/
|
*/
|
||||||
#ifndef MEDIA_TRACK_AUDIO_SUPPLIER_H
|
#ifndef MEDIA_TRACK_AUDIO_SUPPLIER_H
|
||||||
#define MEDIA_TRACK_AUDIO_SUPPLIER_H
|
#define MEDIA_TRACK_AUDIO_SUPPLIER_H
|
||||||
|
|
||||||
#include "AudioSupplier.h"
|
#include <List.h>
|
||||||
|
|
||||||
|
#include "AudioTrackSupplier.h"
|
||||||
|
|
||||||
class BMediaTrack;
|
class BMediaTrack;
|
||||||
|
struct media_codec_info;
|
||||||
|
struct media_format;
|
||||||
|
|
||||||
class MediaTrackAudioSupplier : public AudioSupplier {
|
class MediaTrackAudioSupplier : public AudioTrackSupplier {
|
||||||
public:
|
public:
|
||||||
MediaTrackAudioSupplier(BMediaTrack* track);
|
MediaTrackAudioSupplier(BMediaTrack* track);
|
||||||
virtual ~MediaTrackAudioSupplier();
|
virtual ~MediaTrackAudioSupplier();
|
||||||
|
|
||||||
virtual media_format Format() const;
|
virtual const media_format& Format() const;
|
||||||
virtual status_t GetEncodedFormat(media_format* format) const;
|
virtual status_t GetEncodedFormat(media_format* format) const;
|
||||||
virtual status_t GetCodecInfo(media_codec_info* info) const;
|
virtual status_t GetCodecInfo(media_codec_info* info) const;
|
||||||
|
virtual bigtime_t Duration() const;
|
||||||
|
|
||||||
virtual status_t ReadFrames(void* buffer, int64* framesRead,
|
// AudioReader interface
|
||||||
bigtime_t* performanceTime);
|
// (needed to reuse the class as AudioResampler input)
|
||||||
virtual status_t SeekToTime(bigtime_t* performanceTime);
|
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:
|
private:
|
||||||
BMediaTrack* fMediaTrack;
|
BMediaTrack* fMediaTrack;
|
||||||
|
char* fBuffer;
|
||||||
media_format fFormat;
|
int64 fBufferOffset;
|
||||||
|
int64 fBufferSize;
|
||||||
bigtime_t fPerformanceTime;
|
BList fBuffers;
|
||||||
bigtime_t fDuration;
|
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
|
// constructor
|
||||||
MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack* track,
|
MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack* track,
|
||||||
color_space format)
|
color_space format)
|
||||||
: VideoSupplier()
|
: VideoTrackSupplier()
|
||||||
, fVideoTrack(track)
|
, fVideoTrack(track)
|
||||||
|
|
||||||
, fPerformanceTime(0)
|
, fPerformanceTime(0)
|
||||||
, fDuration(0)
|
, fDuration(0)
|
||||||
|
, fCurrentFrame(0)
|
||||||
{
|
{
|
||||||
if (!fVideoTrack) {
|
if (!fVideoTrack) {
|
||||||
printf("MediaTrackVideoSupplier() - no video track\n");
|
printf("MediaTrackVideoSupplier() - no video track\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the encoded format
|
_SwitchFormat(format, 0);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fDuration = fVideoTrack->Duration();
|
fDuration = fVideoTrack->Duration();
|
||||||
|
|
||||||
@@ -113,7 +59,7 @@ MediaTrackVideoSupplier::~MediaTrackVideoSupplier()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
media_format
|
const media_format&
|
||||||
MediaTrackVideoSupplier::Format() const
|
MediaTrackVideoSupplier::Format() const
|
||||||
{
|
{
|
||||||
return fFormat;
|
return fFormat;
|
||||||
@@ -139,26 +85,44 @@ MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime)
|
MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime,
|
||||||
|
const media_format* format, bool& wasCached)
|
||||||
{
|
{
|
||||||
if (!fVideoTrack)
|
if (!fVideoTrack)
|
||||||
return B_NO_INIT;
|
return B_NO_INIT;
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return B_BAD_VALUE;
|
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
|
// read a frame
|
||||||
int64 frameCount = 1;
|
int64 frameCount = 1;
|
||||||
// TODO: how does this work for interlaced video (field count > 1)?
|
// TODO: how does this work for interlaced video (field count > 1)?
|
||||||
media_header mediaHeader;
|
media_header mediaHeader;
|
||||||
status_t ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader);
|
ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader);
|
||||||
|
|
||||||
if (ret < B_OK) {
|
if (ret < B_OK) {
|
||||||
printf("MediaTrackVideoSupplier::ReadFrame() - "
|
if (ret != B_LAST_BUFFER_ERROR) {
|
||||||
"error while reading frame of track: %s\n", strerror(ret));
|
fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
|
||||||
|
"error while reading frame of track: %s\n", strerror(ret));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fPerformanceTime = mediaHeader.start_time;
|
fPerformanceTime = mediaHeader.start_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fCurrentFrame = fVideoTrack->CurrentFrame();
|
||||||
if (performanceTime)
|
if (performanceTime)
|
||||||
*performanceTime = fPerformanceTime;
|
*performanceTime = fPerformanceTime;
|
||||||
|
|
||||||
@@ -186,10 +150,52 @@ MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime)
|
|||||||
return B_NO_INIT;
|
return B_NO_INIT;
|
||||||
|
|
||||||
bigtime_t _performanceTime = *performanceTime;
|
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) {
|
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;
|
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;
|
return ret;
|
||||||
@@ -297,3 +303,69 @@ string_for_color_space(color_space format)
|
|||||||
}
|
}
|
||||||
return name;
|
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.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@@ -8,40 +8,51 @@
|
|||||||
#ifndef MEDIA_TRACK_VIDEO_SUPPLIER_H
|
#ifndef MEDIA_TRACK_VIDEO_SUPPLIER_H
|
||||||
#define MEDIA_TRACK_VIDEO_SUPPLIER_H
|
#define MEDIA_TRACK_VIDEO_SUPPLIER_H
|
||||||
|
|
||||||
#include "VideoSupplier.h"
|
#include "VideoTrackSupplier.h"
|
||||||
|
|
||||||
|
#include <MediaFormats.h>
|
||||||
|
|
||||||
class BMediaTrack;
|
class BMediaTrack;
|
||||||
|
|
||||||
class MediaTrackVideoSupplier : public VideoSupplier {
|
class MediaTrackVideoSupplier : public VideoTrackSupplier {
|
||||||
public:
|
public:
|
||||||
MediaTrackVideoSupplier(BMediaTrack* track,
|
MediaTrackVideoSupplier(BMediaTrack* track,
|
||||||
color_space preferredFormat);
|
color_space preferredFormat);
|
||||||
virtual ~MediaTrackVideoSupplier();
|
virtual ~MediaTrackVideoSupplier();
|
||||||
|
|
||||||
virtual media_format Format() const;
|
virtual const media_format& Format() const;
|
||||||
virtual status_t GetEncodedFormat(media_format* format) const;
|
virtual status_t GetEncodedFormat(media_format* format) const;
|
||||||
virtual status_t GetCodecInfo(media_codec_info* info) const;
|
virtual status_t GetCodecInfo(media_codec_info* info) const;
|
||||||
|
|
||||||
virtual status_t ReadFrame(void* buffer,
|
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 SeekToTime(bigtime_t* performanceTime);
|
||||||
|
virtual status_t SeekToFrame(int64* frame);
|
||||||
|
|
||||||
virtual bigtime_t Position() const
|
virtual bigtime_t Position() const
|
||||||
{ return fPerformanceTime; }
|
{ return fPerformanceTime; }
|
||||||
virtual bigtime_t Duration() const
|
virtual bigtime_t Duration() const
|
||||||
{ return fDuration; }
|
{ return fDuration; }
|
||||||
|
virtual int64 CurrentFrame() const
|
||||||
|
{ return fCurrentFrame; }
|
||||||
|
|
||||||
virtual BRect Bounds() const;
|
virtual BRect Bounds() const;
|
||||||
virtual color_space ColorSpace() const;
|
virtual color_space ColorSpace() const;
|
||||||
virtual uint32 BytesPerRow() const;
|
virtual uint32 BytesPerRow() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
status_t _SwitchFormat(color_space format,
|
||||||
|
int32 bytesPerRow);
|
||||||
|
|
||||||
BMediaTrack* fVideoTrack;
|
BMediaTrack* fVideoTrack;
|
||||||
|
|
||||||
media_format fFormat;
|
media_format fFormat;
|
||||||
|
|
||||||
bigtime_t fPerformanceTime;
|
bigtime_t fPerformanceTime;
|
||||||
bigtime_t fDuration;
|
bigtime_t fDuration;
|
||||||
|
int64 fCurrentFrame;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MEDIA_TRACK_VIDEO_SUPPLIER_H
|
#endif // MEDIA_TRACK_VIDEO_SUPPLIER_H
|
||||||
|
|||||||
@@ -0,0 +1,291 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2008 Stephan Aßmus <[email protected]>
|
||||||
|
* 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 <[email protected]>
|
||||||
|
* 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 <[email protected]>
|
||||||
|
* 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 <[email protected]>
|
||||||
|
* 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:
|
* Authors:
|
||||||
* Stephan Aßmus <superstippi@gmx.de>
|
* 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:
|
* Authors:
|
||||||
* Stephan Aßmus <superstippi@gmx.de>
|
* Stephan Aßmus <superstippi@gmx.de>
|
||||||
*/
|
*/
|
||||||
#ifndef VIDEO_SUPPLIER_H
|
#ifndef VIDEO_TRACK_SUPPLIER_H
|
||||||
#define VIDEO_SUPPLIER_H
|
#define VIDEO_TRACK_SUPPLIER_H
|
||||||
|
|
||||||
|
|
||||||
#include <MediaDefs.h>
|
#include <MediaDefs.h>
|
||||||
#include <MediaFormats.h>
|
#include <MediaFormats.h>
|
||||||
|
|
||||||
class VideoSupplier {
|
class VideoTrackSupplier {
|
||||||
public:
|
public:
|
||||||
VideoSupplier();
|
VideoTrackSupplier();
|
||||||
virtual ~VideoSupplier();
|
virtual ~VideoTrackSupplier();
|
||||||
|
|
||||||
virtual media_format Format() const = 0;
|
virtual const media_format& Format() const = 0;
|
||||||
virtual status_t GetEncodedFormat(media_format* format)
|
virtual status_t GetEncodedFormat(media_format* format)
|
||||||
const = 0;
|
const = 0;
|
||||||
virtual status_t GetCodecInfo(media_codec_info* info) const = 0;
|
virtual status_t GetCodecInfo(media_codec_info* info) const = 0;
|
||||||
virtual status_t ReadFrame(void* buffer,
|
virtual status_t ReadFrame(void* buffer,
|
||||||
bigtime_t* performanceTime) = 0;
|
bigtime_t* performanceTime,
|
||||||
|
const media_format* format,
|
||||||
|
bool& wasCached) = 0;
|
||||||
virtual status_t SeekToTime(bigtime_t* performanceTime) = 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 Position() const = 0;
|
||||||
virtual bigtime_t Duration() 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 <[email protected]>
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** 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 <[email protected]>,
|
||||||
|
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||||
|
* 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 <[email protected]>,
|
||||||
|
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||||
|
* 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 <[email protected]>,
|
||||||
|
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||||
|
* 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 <[email protected]>,
|
||||||
|
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||||
|
* 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 <[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 "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 <[email protected]>,
|
||||||
|
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
|
||||||
|
* 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