mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
audio/internal/readerdriver: Use the common player implementation for Android
Updates #1662
This commit is contained in:
parent
0aeb409eba
commit
78459953cf
147
audio/internal/oboe/binding_android.cpp
vendored
147
audio/internal/oboe/binding_android.cpp
vendored
@ -17,12 +17,9 @@
|
||||
#include "_cgo_export.h"
|
||||
#include "oboe_oboe_Oboe_android.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
namespace {
|
||||
|
||||
class Player;
|
||||
@ -39,9 +36,6 @@ public:
|
||||
const char *Resume();
|
||||
const char *Close();
|
||||
|
||||
void AddPlayer(Player *player);
|
||||
void RemovePlayer(Player *player);
|
||||
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboe_stream,
|
||||
void *audio_data,
|
||||
int32_t num_frames) override;
|
||||
@ -52,73 +46,9 @@ private:
|
||||
int channel_num_ = 0;
|
||||
int bit_depth_in_bytes_ = 0;
|
||||
|
||||
std::mutex mutex_;
|
||||
std::set<Player *> players_;
|
||||
std::shared_ptr<oboe::AudioStream> stream_;
|
||||
};
|
||||
|
||||
class Player {
|
||||
public:
|
||||
Player(double volume, uintptr_t go_player) : go_player_{go_player} {
|
||||
std::atomic_store(&volume_, volume);
|
||||
Stream::GetInstance().AddPlayer(this);
|
||||
}
|
||||
|
||||
~Player() { Stream::GetInstance().RemovePlayer(this); }
|
||||
|
||||
void SetVolume(double volume) { std::atomic_store(&volume_, volume); }
|
||||
|
||||
void Play() { std::atomic_store(&playing_, true); }
|
||||
|
||||
void Pause() { std::atomic_store(&playing_, false); }
|
||||
|
||||
bool IsPlaying() { return std::atomic_load(&playing_); }
|
||||
|
||||
void AppendBuffer(uint8_t *data, int length) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
buf_.insert(buf_.end(), data, data + length);
|
||||
}
|
||||
|
||||
size_t GetUnplayedBufferSize() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return buf_.size();
|
||||
}
|
||||
|
||||
size_t Read(std::vector<float> &buf) {
|
||||
if (!std::atomic_load(&playing_)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const double volume = std::atomic_load(&volume_);
|
||||
size_t copy_num = 0;
|
||||
{
|
||||
// TODO: Do not use a lock in onAudioReady.
|
||||
// https://google.github.io/oboe/reference/classoboe_1_1_audio_stream_data_callback.html#ad8a3a9f609df5fd3a5d885cbe1b2204d
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
copy_num = std::min(buf.size(), buf_.size() / 2);
|
||||
for (size_t i = 0; i < copy_num; i++) {
|
||||
int16_t v = static_cast<int16_t>(buf_[2 * i]) |
|
||||
(static_cast<int16_t>(buf_[2 * i + 1]) << 8);
|
||||
buf[i] = static_cast<float>(v) / (1 << 15) * volume;
|
||||
}
|
||||
buf_.erase(buf_.begin(), buf_.begin() + copy_num * 2);
|
||||
}
|
||||
|
||||
if (copy_num) {
|
||||
ebiten_oboe_onWrittenCallback(go_player_);
|
||||
}
|
||||
return copy_num;
|
||||
}
|
||||
|
||||
private:
|
||||
const uintptr_t go_player_;
|
||||
|
||||
std::atomic<double> volume_{1.0};
|
||||
std::atomic<bool> playing_;
|
||||
std::vector<uint8_t> buf_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
Stream &Stream::GetInstance() {
|
||||
static Stream *stream = new Stream();
|
||||
return *stream;
|
||||
@ -144,6 +74,7 @@ const char *Stream::Play(int sample_rate, int channel_num,
|
||||
->setFormat(oboe::AudioFormat::Float)
|
||||
->setChannelCount(channel_num_)
|
||||
->setSampleRate(sample_rate_)
|
||||
->setBufferCapacityInFrames(1024)
|
||||
->setDataCallback(this)
|
||||
->openStream(stream_);
|
||||
if (result != oboe::Result::OK) {
|
||||
@ -195,41 +126,14 @@ const char *Stream::Close() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Stream::AddPlayer(Player *player) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
players_.insert(player);
|
||||
}
|
||||
|
||||
void Stream::RemovePlayer(Player *player) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
players_.erase(player);
|
||||
}
|
||||
|
||||
oboe::DataCallbackResult Stream::onAudioReady(oboe::AudioStream *oboe_stream,
|
||||
void *audio_data,
|
||||
int32_t num_frames) {
|
||||
size_t num = num_frames * channel_num_;
|
||||
std::vector<std::vector<float>> bufs;
|
||||
{
|
||||
// TODO: Do not use a lock in onAudioReady.
|
||||
// https://google.github.io/oboe/reference/classoboe_1_1_audio_stream_data_callback.html#ad8a3a9f609df5fd3a5d885cbe1b2204d
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
bufs.resize(players_.size());
|
||||
size_t i = 0;
|
||||
for (Player *player : players_) {
|
||||
bufs[i].resize(num);
|
||||
player->Read(bufs[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
float *dst = reinterpret_cast<float *>(audio_data);
|
||||
for (int i = 0; i < num; i++) {
|
||||
dst[i] = 0;
|
||||
for (const std::vector<float> buf : bufs) {
|
||||
dst[i] += buf[i];
|
||||
}
|
||||
}
|
||||
// TODO: Do not use a lock in onAudioReady.
|
||||
// https://google.github.io/oboe/reference/classoboe_1_1_audio_stream_data_callback.html#ad8a3a9f609df5fd3a5d885cbe1b2204d
|
||||
ebiten_oboe_read(dst, num);
|
||||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
@ -249,45 +153,4 @@ const char *ebiten_oboe_Suspend() { return Stream::GetInstance().Pause(); }
|
||||
|
||||
const char *ebiten_oboe_Resume() { return Stream::GetInstance().Resume(); }
|
||||
|
||||
PlayerID ebiten_oboe_Player_Create(double volume, uintptr_t go_player) {
|
||||
Player *p = new Player(volume, go_player);
|
||||
return reinterpret_cast<PlayerID>(p);
|
||||
}
|
||||
|
||||
bool ebiten_oboe_Player_IsPlaying(PlayerID audio_player) {
|
||||
Player *p = reinterpret_cast<Player *>(audio_player);
|
||||
return p->IsPlaying();
|
||||
}
|
||||
|
||||
void ebiten_oboe_Player_AppendBuffer(PlayerID audio_player, uint8_t *data,
|
||||
int length) {
|
||||
Player *p = reinterpret_cast<Player *>(audio_player);
|
||||
p->AppendBuffer(data, length);
|
||||
}
|
||||
|
||||
void ebiten_oboe_Player_Play(PlayerID audio_player) {
|
||||
Player *p = reinterpret_cast<Player *>(audio_player);
|
||||
p->Play();
|
||||
}
|
||||
|
||||
void ebiten_oboe_Player_Pause(PlayerID audio_player) {
|
||||
Player *p = reinterpret_cast<Player *>(audio_player);
|
||||
return p->Pause();
|
||||
}
|
||||
|
||||
void ebiten_oboe_Player_SetVolume(PlayerID audio_player, double volume) {
|
||||
Player *p = reinterpret_cast<Player *>(audio_player);
|
||||
p->SetVolume(volume);
|
||||
}
|
||||
|
||||
void ebiten_oboe_Player_Close(PlayerID audio_player) {
|
||||
Player *p = reinterpret_cast<Player *>(audio_player);
|
||||
delete p;
|
||||
}
|
||||
|
||||
int ebiten_oboe_Player_UnplayedBufferSize(PlayerID audio_player) {
|
||||
Player *p = reinterpret_cast<Player *>(audio_player);
|
||||
return p->GetUnplayedBufferSize();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
103
audio/internal/oboe/binding_android.go
vendored
103
audio/internal/oboe/binding_android.go
vendored
@ -26,12 +26,15 @@ import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func Play(sampleRate, channelNum, bitDepthInBytes int) error {
|
||||
var theReadFunc func(buf []float32)
|
||||
|
||||
func Play(sampleRate, channelNum, bitDepthInBytes int, readFunc func(buf []float32)) error {
|
||||
// Play can invoke the callback. Set the callback before Play.
|
||||
theReadFunc = readFunc
|
||||
if msg := C.ebiten_oboe_Play(C.int(sampleRate), C.int(channelNum), C.int(bitDepthInBytes)); msg != nil {
|
||||
return fmt.Errorf("oboe: Play failed: %s", C.GoString(msg))
|
||||
}
|
||||
@ -52,89 +55,15 @@ func Resume() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Player struct {
|
||||
player C.PlayerID
|
||||
onWritten func()
|
||||
|
||||
// m is the mutex for this player.
|
||||
// This is necessary as Close can be invoked from the finalizer goroutine.
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
func NewPlayer(volume float64, onWritten func()) *Player {
|
||||
p := &Player{
|
||||
onWritten: onWritten,
|
||||
//export ebiten_oboe_read
|
||||
func ebiten_oboe_read(buf *C.float, len C.size_t) {
|
||||
s := []float32{}
|
||||
h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
|
||||
h.Data = uintptr(unsafe.Pointer(buf))
|
||||
h.Len = int(len)
|
||||
h.Cap = int(len)
|
||||
for i := range s {
|
||||
s[i] = 0
|
||||
}
|
||||
p.player = C.ebiten_oboe_Player_Create(C.double(volume), C.uintptr_t(uintptr(unsafe.Pointer(p))))
|
||||
runtime.SetFinalizer(p, (*Player).Close)
|
||||
return p
|
||||
}
|
||||
|
||||
//export ebiten_oboe_onWrittenCallback
|
||||
func ebiten_oboe_onWrittenCallback(player C.uintptr_t) {
|
||||
p := (*Player)(unsafe.Pointer(uintptr(player)))
|
||||
p.onWritten()
|
||||
}
|
||||
|
||||
func (p *Player) IsPlaying() bool {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
return bool(C.ebiten_oboe_Player_IsPlaying(p.player))
|
||||
}
|
||||
|
||||
func (p *Player) AppendBuffer(buf []byte) {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
|
||||
ptr := C.CBytes(buf)
|
||||
defer C.free(ptr)
|
||||
|
||||
C.ebiten_oboe_Player_AppendBuffer(p.player, (*C.uint8_t)(ptr), C.int(len(buf)))
|
||||
}
|
||||
|
||||
func (p *Player) Play() error {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
|
||||
if p.player == 0 {
|
||||
return fmt.Errorf("oboe: player is already closed at Play")
|
||||
}
|
||||
C.ebiten_oboe_Player_Play(p.player)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Player) Pause() error {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
|
||||
if p.player == 0 {
|
||||
return fmt.Errorf("oboe: player is already closed at Pause")
|
||||
}
|
||||
C.ebiten_oboe_Player_Pause(p.player)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Player) SetVolume(volume float64) {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
C.ebiten_oboe_Player_SetVolume(p.player, C.double(volume))
|
||||
}
|
||||
|
||||
func (p *Player) Close() error {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
|
||||
runtime.SetFinalizer(p, nil)
|
||||
if p.player == 0 {
|
||||
return fmt.Errorf("oboe: player is already closed at Close")
|
||||
}
|
||||
C.ebiten_oboe_Player_Close(p.player)
|
||||
p.player = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Player) UnplayedBufferSize() int {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
return int(C.ebiten_oboe_Player_UnplayedBufferSize(p.player))
|
||||
theReadFunc(s)
|
||||
}
|
||||
|
9
audio/internal/oboe/binding_android.h
vendored
9
audio/internal/oboe/binding_android.h
vendored
@ -29,15 +29,6 @@ const char *ebiten_oboe_Play(int sample_rate, int channel_num,
|
||||
int bit_depth_in_bytes);
|
||||
const char *ebiten_oboe_Suspend();
|
||||
const char *ebiten_oboe_Resume();
|
||||
PlayerID ebiten_oboe_Player_Create(double volume, uintptr_t go_player);
|
||||
bool ebiten_oboe_Player_IsPlaying(PlayerID audio_player);
|
||||
void ebiten_oboe_Player_AppendBuffer(PlayerID audio_player, uint8_t *data,
|
||||
int length);
|
||||
void ebiten_oboe_Player_Play(PlayerID audio_player);
|
||||
void ebiten_oboe_Player_Pause(PlayerID audio_player);
|
||||
void ebiten_oboe_Player_Close(PlayerID audio_player);
|
||||
void ebiten_oboe_Player_SetVolume(PlayerID audio_player, double volume);
|
||||
int ebiten_oboe_Player_UnplayedBufferSize(PlayerID audio_player);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -15,10 +15,6 @@
|
||||
package readerdriver
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/audio/internal/oboe"
|
||||
)
|
||||
|
||||
@ -30,6 +26,8 @@ type context struct {
|
||||
sampleRate int
|
||||
channelNum int
|
||||
bitDepthInBytes int
|
||||
|
||||
players *players
|
||||
}
|
||||
|
||||
func NewContext(sampleRate int, channelNum int, bitDepthInBytes int) (Context, chan struct{}, error) {
|
||||
@ -40,24 +38,15 @@ func NewContext(sampleRate int, channelNum int, bitDepthInBytes int) (Context, c
|
||||
sampleRate: sampleRate,
|
||||
channelNum: channelNum,
|
||||
bitDepthInBytes: bitDepthInBytes,
|
||||
players: newPlayers(),
|
||||
}
|
||||
if err := oboe.Play(sampleRate, channelNum, bitDepthInBytes); err != nil {
|
||||
if err := oboe.Play(sampleRate, channelNum, bitDepthInBytes, c.players.read); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
go c.players.loop()
|
||||
return c, ready, nil
|
||||
}
|
||||
|
||||
func (c *context) NewPlayer(src io.Reader) Player {
|
||||
p := &player{
|
||||
context: c,
|
||||
src: src,
|
||||
cond: sync.NewCond(&sync.Mutex{}),
|
||||
volume: 1,
|
||||
}
|
||||
runtime.SetFinalizer(p, (*player).Close)
|
||||
return p
|
||||
}
|
||||
|
||||
func (c *context) Suspend() error {
|
||||
return oboe.Suspend()
|
||||
}
|
||||
@ -65,247 +54,3 @@ func (c *context) Suspend() error {
|
||||
func (c *context) Resume() error {
|
||||
return oboe.Resume()
|
||||
}
|
||||
|
||||
type player struct {
|
||||
context *context
|
||||
p *oboe.Player
|
||||
src io.Reader
|
||||
err error
|
||||
cond *sync.Cond
|
||||
closed bool
|
||||
volume float64
|
||||
eof bool
|
||||
}
|
||||
|
||||
func (p *player) Pause() {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
|
||||
if p.err != nil {
|
||||
return
|
||||
}
|
||||
if p.closed {
|
||||
return
|
||||
}
|
||||
if p.p == nil {
|
||||
return
|
||||
}
|
||||
if err := p.p.Pause(); err != nil {
|
||||
p.setErrorImpl(err)
|
||||
return
|
||||
}
|
||||
p.cond.Signal()
|
||||
}
|
||||
|
||||
func (p *player) Play() {
|
||||
// Call Play asynchronously since Oboe's Play might take long.
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
close(ch)
|
||||
p.playImpl()
|
||||
}()
|
||||
|
||||
// Wait until the mutex is locked in the above goroutine.
|
||||
<-ch
|
||||
}
|
||||
|
||||
func (p *player) playImpl() {
|
||||
if p.err != nil {
|
||||
return
|
||||
}
|
||||
if p.p != nil && p.p.IsPlaying() {
|
||||
return
|
||||
}
|
||||
defer p.cond.Signal()
|
||||
var runLoop bool
|
||||
if p.p == nil {
|
||||
p.p = oboe.NewPlayer(p.volume, func() {
|
||||
p.cond.Signal()
|
||||
})
|
||||
runLoop = true
|
||||
}
|
||||
|
||||
buf := make([]byte, p.context.maxBufferSize())
|
||||
for p.p.UnplayedBufferSize() < p.context.maxBufferSize() {
|
||||
n, err := p.src.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
p.setErrorImpl(err)
|
||||
return
|
||||
}
|
||||
p.p.AppendBuffer(buf[:n])
|
||||
if err == io.EOF {
|
||||
p.eof = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.p.Play(); err != nil {
|
||||
p.setErrorImpl(err)
|
||||
return
|
||||
}
|
||||
if runLoop {
|
||||
go p.loop()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *player) IsPlaying() bool {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
if p.p == nil {
|
||||
return false
|
||||
}
|
||||
return p.p.IsPlaying()
|
||||
}
|
||||
|
||||
func (p *player) Reset() {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
p.resetImpl()
|
||||
}
|
||||
|
||||
func (p *player) resetImpl() {
|
||||
if p.err != nil {
|
||||
return
|
||||
}
|
||||
if p.closed {
|
||||
return
|
||||
}
|
||||
if p.p == nil {
|
||||
return
|
||||
}
|
||||
if err := p.p.Close(); err != nil {
|
||||
p.setErrorImpl(err)
|
||||
return
|
||||
}
|
||||
p.p = nil
|
||||
p.eof = false
|
||||
p.cond.Signal()
|
||||
}
|
||||
|
||||
func (p *player) Volume() float64 {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
return p.volume
|
||||
}
|
||||
|
||||
func (p *player) SetVolume(volume float64) {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
p.volume = volume
|
||||
if p.p == nil {
|
||||
return
|
||||
}
|
||||
p.p.SetVolume(volume)
|
||||
}
|
||||
|
||||
func (p *player) UnplayedBufferSize() int {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
|
||||
if p.p == nil {
|
||||
return 0
|
||||
}
|
||||
return p.p.UnplayedBufferSize()
|
||||
}
|
||||
|
||||
func (p *player) Err() error {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
return p.err
|
||||
}
|
||||
|
||||
func (p *player) Close() error {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
return p.closeImpl()
|
||||
}
|
||||
|
||||
func (p *player) closeImpl() error {
|
||||
defer p.cond.Signal()
|
||||
|
||||
runtime.SetFinalizer(p, nil)
|
||||
p.closed = true
|
||||
if p.p == nil {
|
||||
return p.err
|
||||
}
|
||||
if err := p.p.Close(); err != nil && p.err == nil {
|
||||
// Do not call setErrorImpl, or this can cause infinite recursive.
|
||||
p.err = err
|
||||
return p.err
|
||||
}
|
||||
p.p = nil
|
||||
return p.err
|
||||
}
|
||||
|
||||
func (p *player) setError(err error) {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
p.setErrorImpl(err)
|
||||
}
|
||||
|
||||
func (p *player) setErrorImpl(err error) {
|
||||
p.err = err
|
||||
p.closeImpl()
|
||||
}
|
||||
|
||||
func (p *player) shouldWait() bool {
|
||||
if p.closed {
|
||||
return false
|
||||
}
|
||||
if p.p == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Wait when the player is paused.
|
||||
if !p.p.IsPlaying() {
|
||||
return true
|
||||
}
|
||||
|
||||
// When the source reaches EOF, wait until all the data is consumed.
|
||||
if p.eof {
|
||||
return p.p.UnplayedBufferSize() > 0
|
||||
}
|
||||
|
||||
return p.p.UnplayedBufferSize() >= p.context.maxBufferSize()
|
||||
}
|
||||
|
||||
func (p *player) wait() bool {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
|
||||
for p.shouldWait() {
|
||||
p.cond.Wait()
|
||||
}
|
||||
return p.p != nil && p.p.IsPlaying()
|
||||
}
|
||||
|
||||
func (p *player) loop() {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
if !p.wait() {
|
||||
return
|
||||
}
|
||||
|
||||
n, err := p.src.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
p.setError(err)
|
||||
return
|
||||
}
|
||||
|
||||
p.cond.L.Lock()
|
||||
p.p.AppendBuffer(buf[:n])
|
||||
if err == io.EOF {
|
||||
p.eof = true
|
||||
}
|
||||
|
||||
// Now p.resetImpl() doesn't close the stream gracefully. Then buffer size check is necessary here.
|
||||
if p.eof && p.p.UnplayedBufferSize() == 0 {
|
||||
p.resetImpl()
|
||||
p.cond.L.Unlock()
|
||||
return
|
||||
}
|
||||
p.cond.L.Unlock()
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
// +build aix dragonfly freebsd hurd illumos linux netbsd openbsd solaris
|
||||
// +build !android
|
||||
|
||||
package readerdriver
|
||||
|
||||
@ -153,9 +152,15 @@ func (p *player) Play() {
|
||||
}
|
||||
|
||||
func (p *playerImpl) Play() {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
p.playImpl()
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
|
||||
close(ch)
|
||||
p.playImpl()
|
||||
}()
|
||||
<-ch
|
||||
}
|
||||
|
||||
func (p *playerImpl) playImpl() {
|
||||
@ -193,10 +198,7 @@ func (p *player) Pause() {
|
||||
func (p *playerImpl) Pause() {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
p.pauseImpl()
|
||||
}
|
||||
|
||||
func (p *playerImpl) pauseImpl() {
|
||||
if p.state != playerPlay {
|
||||
return
|
||||
}
|
||||
@ -335,15 +337,22 @@ func (p *playerImpl) readSourceToBuffer() {
|
||||
return
|
||||
}
|
||||
|
||||
if len(p.buf) >= p.context.maxBufferSize() {
|
||||
maxBufferSize := p.context.maxBufferSize()
|
||||
if len(p.buf) >= maxBufferSize {
|
||||
return
|
||||
}
|
||||
buf := make([]byte, p.context.maxBufferSize())
|
||||
n, err := p.src.Read(buf)
|
||||
|
||||
src := p.src
|
||||
p.m.Unlock()
|
||||
buf := make([]byte, maxBufferSize)
|
||||
n, err := src.Read(buf)
|
||||
p.m.Lock()
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
p.setErrorImpl(err)
|
||||
return
|
||||
}
|
||||
|
||||
p.buf = append(p.buf, buf[:n]...)
|
||||
if err == io.EOF && len(p.buf) == 0 {
|
||||
p.resetImpl()
|
||||
|
Loading…
Reference in New Issue
Block a user