Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mpd
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Мажукин
mpd
Commits
bcb2db62
Commit
bcb2db62
authored
Sep 26, 2013
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
PlayerThread: move code into the player class
parent
079ef931
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
277 additions
and
269 deletions
+277
-269
PlayerThread.cxx
src/PlayerThread.cxx
+277
-269
No files found.
src/PlayerThread.cxx
View file @
bcb2db62
...
...
@@ -142,6 +142,132 @@ struct player {
cross_fade_chunks
(
0
),
cross_fade_tag
(
NULL
),
elapsed_time
(
0.0
)
{}
void
ClearAndDeletePipe
()
{
pipe
->
Clear
(
buffer
);
delete
pipe
;
}
void
ClearAndReplacePipe
(
MusicPipe
*
_pipe
)
{
ClearAndDeletePipe
();
pipe
=
_pipe
;
}
void
ReplacePipe
(
MusicPipe
*
_pipe
)
{
delete
pipe
;
pipe
=
_pipe
;
}
/**
* Start the decoder.
*
* Player lock is not held.
*/
void
StartDecoder
(
MusicPipe
&
pipe
);
/**
* The decoder has acknowledged the "START" command (see
* player::WaitForDecoder()). This function checks if the decoder
* initialization has completed yet.
*
* The player lock is not held.
*/
bool
CheckDecoderStartup
();
/**
* Stop the decoder and clears (and frees) its music pipe.
*
* Player lock is not held.
*/
void
StopDecoder
();
/**
* Is the decoder still busy on the same song as the player?
*
* Note: this function does not check if the decoder is already
* finished.
*/
gcc_pure
bool
IsDecoderAtCurrentSong
()
const
{
assert
(
pipe
!=
nullptr
);
return
dc
.
pipe
==
pipe
;
}
/**
* Returns true if the decoder is decoding the next song (or has begun
* decoding it, or has finished doing it), and the player hasn't
* switched to that song yet.
*/
gcc_pure
bool
IsDecoderAtNextSong
()
const
{
return
dc
.
pipe
!=
nullptr
&&
!
IsDecoderAtCurrentSong
();
}
/**
* This is the handler for the #PLAYER_COMMAND_SEEK command.
*
* The player lock is not held.
*/
bool
SeekDecoder
();
/**
* After the decoder has been started asynchronously, wait for
* the "START" command to finish. The decoder may not be
* initialized yet, i.e. there is no audio_format information
* yet.
*
* The player lock is not held.
*/
bool
WaitForDecoder
();
/**
* Wrapper for audio_output_all_open(). Upon failure, it pauses the
* player.
*
* @return true on success
*/
bool
OpenOutput
();
/**
* Obtains the next chunk from the music pipe, optionally applies
* cross-fading, and sends it to all audio outputs.
*
* @return true on success, false on error (playback will be stopped)
*/
bool
PlayNextChunk
();
/**
* Sends a chunk of silence to the audio outputs. This is
* called when there is not enough decoded data in the pipe
* yet, to prevent underruns in the hardware buffers.
*
* The player lock is not held.
*/
bool
SendSilence
();
/**
* Player lock must be held before calling.
*/
void
ProcessCommand
();
/**
* This is called at the border between two songs: the audio output
* has consumed all chunks of the current song, and we should start
* sending chunks from the next one.
*
* The player lock is not held.
*
* @return true on success, false on error (playback will be stopped)
*/
bool
SongBorder
();
/*
* The main loop of the player thread, during playback. This
* is basically a state machine, which multiplexes data
* between the decoder thread and the output threads.
*/
void
Run
();
};
static
void
...
...
@@ -161,18 +287,10 @@ player_command_finished(player_control &pc)
pc
.
Unlock
();
}
/**
* Start the decoder.
*
* Player lock is not held.
*/
static
void
player_dc_start
(
player
&
player
,
MusicPipe
&
pipe
)
void
player
::
StartDecoder
(
MusicPipe
&
_pipe
)
{
player_control
&
pc
=
player
.
pc
;
decoder_control
&
dc
=
player
.
dc
;
assert
(
player
.
queued
||
pc
.
command
==
PLAYER_COMMAND_SEEK
);
assert
(
queued
||
pc
.
command
==
PLAYER_COMMAND_SEEK
);
assert
(
pc
.
next_song
!=
NULL
);
unsigned
start_ms
=
pc
.
next_song
->
start_ms
;
...
...
@@ -181,75 +299,33 @@ player_dc_start(player &player, MusicPipe &pipe)
dc
.
Start
(
pc
.
next_song
->
DupDetached
(),
start_ms
,
pc
.
next_song
->
end_ms
,
player
.
buffer
,
pipe
);
}
/**
* Is the decoder still busy on the same song as the player?
*
* Note: this function does not check if the decoder is already
* finished.
*/
static
bool
player_dc_at_current_song
(
const
player
&
player
)
{
assert
(
player
.
pipe
!=
NULL
);
return
player
.
dc
.
pipe
==
player
.
pipe
;
buffer
,
_pipe
);
}
/**
* Returns true if the decoder is decoding the next song (or has begun
* decoding it, or has finished doing it), and the player hasn't
* switched to that song yet.
*/
static
bool
player_dc_at_next_song
(
const
player
&
player
)
{
return
player
.
dc
.
pipe
!=
NULL
&&
!
player_dc_at_current_song
(
player
);
}
/**
* Stop the decoder and clears (and frees) its music pipe.
*
* Player lock is not held.
*/
static
void
player_dc_stop
(
player
&
player
)
void
player
::
StopDecoder
()
{
decoder_control
&
dc
=
player
.
dc
;
dc
.
Stop
();
if
(
dc
.
pipe
!=
NULL
)
{
/* clear and free the decoder pipe */
dc
.
pipe
->
Clear
(
player
.
buffer
);
dc
.
pipe
->
Clear
(
buffer
);
if
(
dc
.
pipe
!=
p
layer
.
p
ipe
)
if
(
dc
.
pipe
!=
pipe
)
delete
dc
.
pipe
;
dc
.
pipe
=
NULL
;
}
}
/**
* After the decoder has been started asynchronously, wait for the
* "START" command to finish. The decoder may not be initialized yet,
* i.e. there is no audio_format information yet.
*
* The player lock is not held.
*/
static
bool
player_wait_for_decoder
(
player
&
player
)
bool
player
::
WaitForDecoder
()
{
player_control
&
pc
=
player
.
pc
;
decoder_control
&
dc
=
player
.
dc
;
assert
(
player
.
queued
||
pc
.
command
==
PLAYER_COMMAND_SEEK
);
assert
(
queued
||
pc
.
command
==
PLAYER_COMMAND_SEEK
);
assert
(
pc
.
next_song
!=
NULL
);
player
.
queued
=
false
;
queued
=
false
;
Error
error
=
dc
.
LockGetError
();
if
(
error
.
IsDefined
())
{
...
...
@@ -264,15 +340,15 @@ player_wait_for_decoder(player &player)
return
false
;
}
if
(
player
.
song
!=
NULL
)
player
.
song
->
Free
();
if
(
song
!=
nullptr
)
song
->
Free
();
player
.
song
=
pc
.
next_song
;
player
.
elapsed_time
=
0.0
;
song
=
pc
.
next_song
;
elapsed_time
=
0.0
;
/* set the "starting" flag, which will be cleared by
player_check_decoder_startup() */
player
.
decoder_starting
=
true
;
decoder_starting
=
true
;
pc
.
Lock
();
...
...
@@ -312,26 +388,17 @@ real_song_duration(const Song *song, double decoder_duration)
return
decoder_duration
-
song
->
start_ms
/
1000.0
;
}
/**
* Wrapper for audio_output_all_open(). Upon failure, it pauses the
* player.
*
* @return true on success
*/
static
bool
player_open_output
(
player
&
player
)
bool
player
::
OpenOutput
()
{
player_control
&
pc
=
player
.
pc
;
assert
(
player
.
play_audio_format
.
IsDefined
());
assert
(
play_audio_format
.
IsDefined
());
assert
(
pc
.
state
==
PLAYER_STATE_PLAY
||
pc
.
state
==
PLAYER_STATE_PAUSE
);
Error
error
;
if
(
audio_output_all_open
(
player
.
play_audio_format
,
player
.
buffer
,
error
))
{
player
.
output_open
=
true
;
player
.
paused
=
false
;
if
(
audio_output_all_open
(
play_audio_format
,
buffer
,
error
))
{
output_open
=
true
;
paused
=
false
;
pc
.
Lock
();
pc
.
state
=
PLAYER_STATE_PLAY
;
...
...
@@ -343,11 +410,11 @@ player_open_output(player &player)
}
else
{
g_warning
(
"%s"
,
error
.
GetMessage
());
player
.
output_open
=
false
;
output_open
=
false
;
/* pause: the user may resume playback as soon as an
audio output becomes available */
p
layer
.
p
aused
=
true
;
paused
=
true
;
pc
.
Lock
();
pc
.
SetError
(
PLAYER_ERROR_OUTPUT
,
std
::
move
(
error
));
...
...
@@ -360,20 +427,10 @@ player_open_output(player &player)
}
}
/**
* The decoder has acknowledged the "START" command (see
* player_wait_for_decoder()). This function checks if the decoder
* initialization has completed yet.
*
* The player lock is not held.
*/
static
bool
player_check_decoder_startup
(
player
&
player
)
bool
player
::
CheckDecoderStartup
()
{
player_control
&
pc
=
player
.
pc
;
decoder_control
&
dc
=
player
.
dc
;
assert
(
player
.
decoder_starting
);
assert
(
decoder_starting
);
dc
.
Lock
();
...
...
@@ -392,7 +449,7 @@ player_check_decoder_startup(player &player)
dc
.
Unlock
();
if
(
player
.
output_open
&&
if
(
output_open
&&
!
audio_output_all_wait
(
&
pc
,
1
))
/* the output devices havn't finished playing
all chunks yet - wait for that */
...
...
@@ -405,10 +462,10 @@ player_check_decoder_startup(player &player)
idle_add
(
IDLE_PLAYER
);
play
er
.
play
_audio_format
=
dc
.
out_audio_format
;
player
.
decoder_starting
=
false
;
play_audio_format
=
dc
.
out_audio_format
;
decoder_starting
=
false
;
if
(
!
p
layer
.
paused
&&
!
player_open_output
(
player
))
{
if
(
!
p
aused
&&
!
OpenOutput
(
))
{
char
*
uri
=
dc
.
song
->
GetURI
();
g_warning
(
"problems opening audio device "
"while playing
\"
%s
\"
"
,
uri
);
...
...
@@ -428,30 +485,23 @@ player_check_decoder_startup(player &player)
}
}
/**
* Sends a chunk of silence to the audio outputs. This is called when
* there is not enough decoded data in the pipe yet, to prevent
* underruns in the hardware buffers.
*
* The player lock is not held.
*/
static
bool
player_send_silence
(
player
&
player
)
bool
player
::
SendSilence
()
{
assert
(
player
.
output_open
);
assert
(
play
er
.
play
_audio_format
.
IsDefined
());
assert
(
output_open
);
assert
(
play_audio_format
.
IsDefined
());
struct
music_chunk
*
chunk
=
player
.
buffer
.
Allocate
();
struct
music_chunk
*
chunk
=
buffer
.
Allocate
();
if
(
chunk
==
NULL
)
{
g_warning
(
"Failed to allocate silence buffer"
);
return
false
;
}
#ifndef NDEBUG
chunk
->
audio_format
=
play
er
.
play
_audio_format
;
chunk
->
audio_format
=
play_audio_format
;
#endif
const
size_t
frame_size
=
play
er
.
play
_audio_format
.
GetFrameSize
();
const
size_t
frame_size
=
play_audio_format
.
GetFrameSize
();
/* this formula ensures that we don't send
partial frames */
unsigned
num_frames
=
sizeof
(
chunk
->
data
)
/
frame_size
;
...
...
@@ -463,63 +513,53 @@ player_send_silence(player &player)
Error
error
;
if
(
!
audio_output_all_play
(
chunk
,
error
))
{
g_warning
(
"%s"
,
error
.
GetMessage
());
player
.
buffer
.
Return
(
chunk
);
buffer
.
Return
(
chunk
);
return
false
;
}
return
true
;
}
/**
* This is the handler for the #PLAYER_COMMAND_SEEK command.
*
* The player lock is not held.
*/
static
bool
player_seek_decoder
(
player
&
player
)
inline
bool
player
::
SeekDecoder
()
{
player_control
&
pc
=
player
.
pc
;
Song
*
song
=
pc
.
next_song
;
decoder_control
&
dc
=
player
.
dc
;
assert
(
pc
.
next_song
!=
NULL
);
const
unsigned
start_ms
=
song
->
start_ms
;
const
unsigned
start_ms
=
pc
.
next_
song
->
start_ms
;
if
(
!
dc
.
LockIsCurrentSong
(
song
))
{
if
(
!
dc
.
LockIsCurrentSong
(
pc
.
next_
song
))
{
/* the decoder is already decoding the "next" song -
stop it and start the previous song again */
player_dc_stop
(
player
);
StopDecoder
(
);
/* clear music chunks which might still reside in the
pipe */
p
layer
.
pipe
->
Clear
(
player
.
buffer
);
p
ipe
->
Clear
(
buffer
);
/* re-start the decoder */
player_dc_start
(
player
,
*
player
.
pipe
);
if
(
!
player_wait_for_decoder
(
player
))
{
StartDecoder
(
*
pipe
);
if
(
!
WaitForDecoder
(
))
{
/* decoder failure */
player_command_finished
(
pc
);
return
false
;
}
}
else
{
if
(
!
player_dc_at_current_song
(
player
))
{
if
(
!
IsDecoderAtCurrentSong
(
))
{
/* the decoder is already decoding the "next" song,
but it is the same song file; exchange the pipe */
player
.
pipe
->
Clear
(
player
.
buffer
);
delete
player
.
pipe
;
player
.
pipe
=
dc
.
pipe
;
ClearAndReplacePipe
(
dc
.
pipe
);
}
pc
.
next_song
->
Free
();
pc
.
next_song
=
NULL
;
player
.
queued
=
false
;
queued
=
false
;
}
/* wait for the decoder to complete initialization */
while
(
player
.
decoder_starting
)
{
if
(
!
player_check_decoder_startup
(
player
))
{
while
(
decoder_starting
)
{
if
(
!
CheckDecoderStartup
(
))
{
/* decoder failure */
player_command_finished
(
pc
);
return
false
;
...
...
@@ -540,28 +580,23 @@ static bool player_seek_decoder(player &player)
return
false
;
}
player
.
elapsed_time
=
where
;
elapsed_time
=
where
;
player_command_finished
(
pc
);
player
.
xfade
=
XFADE_UNKNOWN
;
xfade
=
XFADE_UNKNOWN
;
/* re-fill the buffer after seeking */
player
.
buffering
=
true
;
buffering
=
true
;
audio_output_all_cancel
();
return
true
;
}
/**
* Player lock must be held before calling.
*/
static
void
player_process_command
(
player
&
player
)
inline
void
player
::
ProcessCommand
()
{
player_control
&
pc
=
player
.
pc
;
gcc_unused
decoder_control
&
dc
=
player
.
dc
;
switch
(
pc
.
command
)
{
case
PLAYER_COMMAND_NONE
:
case
PLAYER_COMMAND_STOP
:
...
...
@@ -578,30 +613,30 @@ static void player_process_command(player &player)
case
PLAYER_COMMAND_QUEUE
:
assert
(
pc
.
next_song
!=
NULL
);
assert
(
!
player
.
queued
);
assert
(
!
player_dc_at_next_song
(
player
));
assert
(
!
queued
);
assert
(
!
IsDecoderAtNextSong
(
));
player
.
queued
=
true
;
queued
=
true
;
player_command_finished_locked
(
pc
);
break
;
case
PLAYER_COMMAND_PAUSE
:
pc
.
Unlock
();
p
layer
.
paused
=
!
player
.
paused
;
if
(
p
layer
.
p
aused
)
{
p
aused
=
!
paused
;
if
(
paused
)
{
audio_output_all_pause
();
pc
.
Lock
();
pc
.
state
=
PLAYER_STATE_PAUSE
;
}
else
if
(
!
play
er
.
play
_audio_format
.
IsDefined
())
{
}
else
if
(
!
play_audio_format
.
IsDefined
())
{
/* the decoder hasn't provided an audio format
yet - don't open the audio device yet */
pc
.
Lock
();
pc
.
state
=
PLAYER_STATE_PLAY
;
}
else
{
player_open_output
(
player
);
OpenOutput
(
);
pc
.
Lock
();
}
...
...
@@ -611,7 +646,7 @@ static void player_process_command(player &player)
case
PLAYER_COMMAND_SEEK
:
pc
.
Unlock
();
player_seek_decoder
(
player
);
SeekDecoder
(
);
pc
.
Lock
();
break
;
...
...
@@ -624,22 +659,22 @@ static void player_process_command(player &player)
return
;
}
if
(
player_dc_at_next_song
(
player
))
{
if
(
IsDecoderAtNextSong
(
))
{
/* the decoder is already decoding the song -
stop it and reset the position */
pc
.
Unlock
();
player_dc_stop
(
player
);
StopDecoder
(
);
pc
.
Lock
();
}
pc
.
next_song
->
Free
();
pc
.
next_song
=
NULL
;
player
.
queued
=
false
;
queued
=
false
;
player_command_finished_locked
(
pc
);
break
;
case
PLAYER_COMMAND_REFRESH
:
if
(
player
.
output_open
&&
!
player
.
paused
)
{
if
(
output_open
&&
!
paused
)
{
pc
.
Unlock
();
audio_output_all_check
();
pc
.
Lock
();
...
...
@@ -647,7 +682,7 @@ static void player_process_command(player &player)
pc
.
elapsed_time
=
audio_output_all_get_elapsed_time
();
if
(
pc
.
elapsed_time
<
0.0
)
pc
.
elapsed_time
=
player
.
elapsed_time
;
pc
.
elapsed_time
=
elapsed_time
;
player_command_finished_locked
(
pc
);
break
;
...
...
@@ -714,18 +749,9 @@ play_chunk(player_control &pc,
return
true
;
}
/**
* Obtains the next chunk from the music pipe, optionally applies
* cross-fading, and sends it to all audio outputs.
*
* @return true on success, false on error (playback will be stopped)
*/
static
bool
play_next_chunk
(
player
&
player
)
inline
bool
player
::
PlayNextChunk
()
{
player_control
&
pc
=
player
.
pc
;
decoder_control
&
dc
=
player
.
dc
;
if
(
!
audio_output_all_wait
(
&
pc
,
64
))
/* the output pipe is still large enough, don't send
another chunk */
...
...
@@ -733,38 +759,36 @@ play_next_chunk(player &player)
unsigned
cross_fade_position
;
struct
music_chunk
*
chunk
=
NULL
;
if
(
player
.
xfade
==
XFADE_ENABLED
&&
player_dc_at_next_song
(
player
)
&&
(
cross_fade_position
=
player
.
pipe
->
GetSize
())
<=
player
.
cross_fade_chunks
)
{
if
(
xfade
==
XFADE_ENABLED
&&
IsDecoderAtNextSong
()
&&
(
cross_fade_position
=
pipe
->
GetSize
())
<=
cross_fade_chunks
)
{
/* perform cross fade */
music_chunk
*
other_chunk
=
dc
.
pipe
->
Shift
();
if
(
!
player
.
cross_fading
)
{
if
(
!
cross_fading
)
{
/* beginning of the cross fade - adjust
crossFadeChunks which might be bigger than
the remaining number of chunks in the old
song */
player
.
cross_fade_chunks
=
cross_fade_position
;
player
.
cross_fading
=
true
;
cross_fade_chunks
=
cross_fade_position
;
cross_fading
=
true
;
}
if
(
other_chunk
!=
NULL
)
{
chunk
=
p
layer
.
p
ipe
->
Shift
();
chunk
=
pipe
->
Shift
();
assert
(
chunk
!=
NULL
);
assert
(
chunk
->
other
==
NULL
);
/* don't send the tags of the new song (which
is being faded in) yet; postpone it until
the current song is faded out */
player
.
cross_fade_tag
=
Tag
::
MergeReplace
(
player
.
cross_fade_tag
,
cross_fade_tag
=
Tag
::
MergeReplace
(
cross_fade_tag
,
other_chunk
->
tag
);
other_chunk
->
tag
=
NULL
;
if
(
std
::
isnan
(
pc
.
mixramp_delay_seconds
))
{
chunk
->
mix_ratio
=
((
float
)
cross_fade_position
)
/
player
.
cross_fade_chunks
;
/
cross_fade_chunks
;
}
else
{
chunk
->
mix_ratio
=
nan
(
""
);
}
...
...
@@ -777,7 +801,7 @@ play_next_chunk(player &player)
beginning of the new song, we can
easily recover by throwing it away
now */
player
.
buffer
.
Return
(
other_chunk
);
buffer
.
Return
(
other_chunk
);
other_chunk
=
NULL
;
}
...
...
@@ -792,7 +816,7 @@ play_next_chunk(player &player)
cross fading */
dc
.
Unlock
();
player
.
xfade
=
XFADE_DISABLED
;
xfade
=
XFADE_DISABLED
;
}
else
{
/* wait for the decoder */
dc
.
Signal
();
...
...
@@ -805,26 +829,24 @@ play_next_chunk(player &player)
}
if
(
chunk
==
NULL
)
chunk
=
p
layer
.
p
ipe
->
Shift
();
chunk
=
pipe
->
Shift
();
assert
(
chunk
!=
NULL
);
/* insert the postponed tag if cross-fading is finished */
if
(
player
.
xfade
!=
XFADE_ENABLED
&&
player
.
cross_fade_tag
!=
NULL
)
{
chunk
->
tag
=
Tag
::
MergeReplace
(
chunk
->
tag
,
player
.
cross_fade_tag
);
player
.
cross_fade_tag
=
NULL
;
if
(
xfade
!=
XFADE_ENABLED
&&
cross_fade_tag
!=
nullptr
)
{
chunk
->
tag
=
Tag
::
MergeReplace
(
chunk
->
tag
,
cross_fade_tag
);
cross_fade_tag
=
nullptr
;
}
/* play the current chunk */
Error
error
;
if
(
!
play_chunk
(
player
.
pc
,
player
.
song
,
chunk
,
player
.
buffer
,
player
.
play_audio_format
,
error
))
{
if
(
!
play_chunk
(
pc
,
song
,
chunk
,
buffer
,
play_audio_format
,
error
))
{
g_warning
(
"%s"
,
error
.
GetMessage
());
player
.
buffer
.
Return
(
chunk
);
buffer
.
Return
(
chunk
);
pc
.
Lock
();
...
...
@@ -833,7 +855,7 @@ play_next_chunk(player &player)
/* pause: the user may resume playback as soon as an
audio output becomes available */
pc
.
state
=
PLAYER_STATE_PAUSE
;
p
layer
.
p
aused
=
true
;
paused
=
true
;
pc
.
Unlock
();
...
...
@@ -848,45 +870,34 @@ play_next_chunk(player &player)
dc
.
Lock
();
if
(
!
dc
.
IsIdle
()
&&
dc
.
pipe
->
GetSize
()
<=
(
pc
.
buffered_before_play
+
player
.
buffer
.
GetSize
()
*
3
)
/
4
)
buffer
.
GetSize
()
*
3
)
/
4
)
dc
.
Signal
();
dc
.
Unlock
();
return
true
;
}
/**
* This is called at the border between two songs: the audio output
* has consumed all chunks of the current song, and we should start
* sending chunks from the next one.
*
* The player lock is not held.
*
* @return true on success, false on error (playback will be stopped)
*/
static
bool
player_song_border
(
player
&
player
)
inline
bool
player
::
SongBorder
()
{
player
.
xfade
=
XFADE_UNKNOWN
;
xfade
=
XFADE_UNKNOWN
;
char
*
uri
=
player
.
song
->
GetURI
();
char
*
uri
=
song
->
GetURI
();
g_message
(
"played
\"
%s
\"
"
,
uri
);
g_free
(
uri
);
delete
player
.
pipe
;
player
.
pipe
=
player
.
dc
.
pipe
;
ReplacePipe
(
dc
.
pipe
);
audio_output_all_song_border
();
if
(
!
player_wait_for_decoder
(
player
))
if
(
!
WaitForDecoder
(
))
return
false
;
player_control
&
pc
=
player
.
pc
;
pc
.
Lock
();
const
bool
border_pause
=
pc
.
border_pause
;
if
(
border_pause
)
{
p
layer
.
p
aused
=
true
;
paused
=
true
;
pc
.
state
=
PLAYER_STATE_PAUSE
;
}
...
...
@@ -898,28 +909,20 @@ player_song_border(player &player)
return
true
;
}
/*
* The main loop of the player thread, during playback. This is
* basically a state machine, which multiplexes data between the
* decoder thread and the output threads.
*/
static
void
do_play
(
player_control
&
pc
,
decoder_control
&
dc
,
MusicBuffer
&
buffer
)
inline
void
player
::
Run
()
{
player
player
(
pc
,
dc
,
buffer
);
pc
.
Unlock
();
p
layer
.
p
ipe
=
new
MusicPipe
();
pipe
=
new
MusicPipe
();
player_dc_start
(
player
,
*
player
.
pipe
);
if
(
!
player_wait_for_decoder
(
player
))
{
assert
(
player
.
song
==
NULL
);
StartDecoder
(
*
pipe
);
if
(
!
WaitForDecoder
(
))
{
assert
(
song
==
NULL
);
player_dc_stop
(
player
);
StopDecoder
(
);
player_command_finished
(
pc
);
delete
p
layer
.
p
ipe
;
delete
pipe
;
GlobalEvents
::
Emit
(
GlobalEvents
::
PLAYLIST
);
pc
.
Lock
();
return
;
...
...
@@ -929,12 +932,12 @@ do_play(player_control &pc, decoder_control &dc,
pc
.
state
=
PLAYER_STATE_PLAY
;
if
(
pc
.
command
==
PLAYER_COMMAND_SEEK
)
player
.
elapsed_time
=
pc
.
seek_where
;
elapsed_time
=
pc
.
seek_where
;
player_command_finished_locked
(
pc
);
while
(
true
)
{
player_process_command
(
player
);
ProcessCommand
(
);
if
(
pc
.
command
==
PLAYER_COMMAND_STOP
||
pc
.
command
==
PLAYER_COMMAND_EXIT
||
pc
.
command
==
PLAYER_COMMAND_CLOSE_AUDIO
)
{
...
...
@@ -945,19 +948,18 @@ do_play(player_control &pc, decoder_control &dc,
pc
.
Unlock
();
if
(
player
.
buffering
)
{
if
(
buffering
)
{
/* buffering at the start of the song - wait
until the buffer is large enough, to
prevent stuttering on slow machines */
if
(
p
layer
.
p
ipe
->
GetSize
()
<
pc
.
buffered_before_play
&&
if
(
pipe
->
GetSize
()
<
pc
.
buffered_before_play
&&
!
dc
.
LockIsIdle
())
{
/* not enough decoded buffer space yet */
if
(
!
player
.
paused
&&
player
.
output_open
&&
if
(
!
paused
&&
output_open
&&
audio_output_all_check
()
<
4
&&
!
player_send_silence
(
player
))
!
SendSilence
(
))
break
;
dc
.
Lock
();
...
...
@@ -968,14 +970,14 @@ do_play(player_control &pc, decoder_control &dc,
continue
;
}
else
{
/* buffering is complete */
player
.
buffering
=
false
;
buffering
=
false
;
}
}
if
(
player
.
decoder_starting
)
{
if
(
decoder_starting
)
{
/* wait until the decoder is initialized completely */
if
(
!
player_check_decoder_startup
(
player
))
if
(
!
CheckDecoderStartup
(
))
break
;
pc
.
Lock
();
...
...
@@ -985,31 +987,30 @@ do_play(player_control &pc, decoder_control &dc,
#ifndef NDEBUG
/*
music_pipe_check_format(&play_audio_format,
player.
next_song_chunk,
next_song_chunk,
&dc.out_audio_format);
*/
#endif
if
(
dc
.
LockIsIdle
()
&&
player
.
queued
&&
dc
.
pipe
==
player
.
pipe
)
{
if
(
dc
.
LockIsIdle
()
&&
queued
&&
dc
.
pipe
==
pipe
)
{
/* the decoder has finished the current song;
make it decode the next song */
assert
(
dc
.
pipe
==
NULL
||
dc
.
pipe
==
p
layer
.
p
ipe
);
assert
(
dc
.
pipe
==
NULL
||
dc
.
pipe
==
pipe
);
player_dc_start
(
player
,
*
new
MusicPipe
());
StartDecoder
(
*
new
MusicPipe
());
}
if
(
/* no cross-fading if MPD is going to pause at the
end of the current song */
!
pc
.
border_pause
&&
player_dc_at_next_song
(
player
)
&&
player
.
xfade
==
XFADE_UNKNOWN
&&
IsDecoderAtNextSong
(
)
&&
xfade
==
XFADE_UNKNOWN
&&
!
dc
.
LockIsStarting
())
{
/* enable cross fading in this song? if yes,
calculate how many chunks will be required
for it */
player
.
cross_fade_chunks
=
cross_fade_chunks
=
cross_fade_calc
(
pc
.
cross_fade_seconds
,
dc
.
total_time
,
pc
.
mixramp_db
,
pc
.
mixramp_delay_seconds
,
...
...
@@ -1018,29 +1019,29 @@ do_play(player_control &pc, decoder_control &dc,
dc
.
mixramp_start
,
dc
.
mixramp_prev_end
,
dc
.
out_audio_format
,
play
er
.
play
_audio_format
,
player
.
buffer
.
GetSize
()
-
play_audio_format
,
buffer
.
GetSize
()
-
pc
.
buffered_before_play
);
if
(
player
.
cross_fade_chunks
>
0
)
{
player
.
xfade
=
XFADE_ENABLED
;
player
.
cross_fading
=
false
;
if
(
cross_fade_chunks
>
0
)
{
xfade
=
XFADE_ENABLED
;
cross_fading
=
false
;
}
else
/* cross fading is disabled or the
next song is too short */
player
.
xfade
=
XFADE_DISABLED
;
xfade
=
XFADE_DISABLED
;
}
if
(
p
layer
.
p
aused
)
{
if
(
paused
)
{
pc
.
Lock
();
if
(
pc
.
command
==
PLAYER_COMMAND_NONE
)
pc
.
Wait
();
continue
;
}
else
if
(
!
p
layer
.
p
ipe
->
IsEmpty
())
{
}
else
if
(
!
pipe
->
IsEmpty
())
{
/* at least one music chunk is ready - send it
to the audio output */
play_next_chunk
(
player
);
PlayNextChunk
(
);
}
else
if
(
audio_output_all_check
()
>
0
)
{
/* not enough data from decoder, but the
output thread is still busy, so it's
...
...
@@ -1048,45 +1049,44 @@ do_play(player_control &pc, decoder_control &dc,
/* XXX synchronize in a better way */
g_usleep
(
10000
);
}
else
if
(
player_dc_at_next_song
(
player
))
{
}
else
if
(
IsDecoderAtNextSong
(
))
{
/* at the beginning of a new song */
if
(
!
player_song_border
(
player
))
if
(
!
SongBorder
(
))
break
;
}
else
if
(
dc
.
LockIsIdle
())
{
/* check the size of the pipe again, because
the decoder thread may have added something
since we last checked */
if
(
p
layer
.
p
ipe
->
IsEmpty
())
{
if
(
pipe
->
IsEmpty
())
{
/* wait for the hardware to finish
playback */
audio_output_all_drain
();
break
;
}
}
else
if
(
player
.
output_open
)
{
}
else
if
(
output_open
)
{
/* the decoder is too busy and hasn't provided
new PCM data in time: send silence (if the
output pipe is empty) */
if
(
!
player_send_silence
(
player
))
if
(
!
SendSilence
(
))
break
;
}
pc
.
Lock
();
}
player_dc_stop
(
player
);
StopDecoder
(
);
player
.
pipe
->
Clear
(
player
.
buffer
);
delete
player
.
pipe
;
ClearAndDeletePipe
();
delete
player
.
cross_fade_tag
;
delete
cross_fade_tag
;
if
(
player
.
song
!=
NULL
)
player
.
song
->
Free
();
if
(
song
!=
nullptr
)
song
->
Free
();
pc
.
Lock
();
if
(
player
.
queued
)
{
if
(
queued
)
{
assert
(
pc
.
next_song
!=
NULL
);
pc
.
next_song
->
Free
();
pc
.
next_song
=
NULL
;
...
...
@@ -1101,6 +1101,14 @@ do_play(player_control &pc, decoder_control &dc,
pc
.
Lock
();
}
static
void
do_play
(
player_control
&
pc
,
decoder_control
&
dc
,
MusicBuffer
&
buffer
)
{
player
player
(
pc
,
dc
,
buffer
);
player
.
Run
();
}
static
gpointer
player_task
(
gpointer
arg
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment