Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ao: EOF handling for pull-based AO #13967

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

ruihe774
Copy link
Contributor

@ruihe774 ruihe774 commented Apr 22, 2024

As we've previously discussed in #13902, a mechanism for pull-based AO to know EOF is needed. This PR addresses this by adding an eof out param to ao_read_data. The AO can know whether EOF has been reached by checking its value and do proper EOF handling like stop requesting more data.

Meanwhile, to reduce code duplication, this PR merges ao_read_data and ao_read_data_nonblocking into a unified interface. ao_read_data now accepts three more arguments: bool *eof, bool pad_silence, bool blocking. I have not touched ao_read_data_converted yet; maybe it can also be unified.

This is a draft: I have only implemented EOF handling in ao_avfoundation. Work need to be done for other AOs. Also, proper handling of audio frames after EOF is WIP.

Copy link

github-actions bot commented Apr 22, 2024

Download the artifacts for this pull request:

Windows
macOS

@ruihe774
Copy link
Contributor Author

I've added ao_stop_streaming to handle data after EOF. Now consecutive playback works fine.

@ruihe774 ruihe774 marked this pull request as ready for review April 24, 2024 01:17
@ruihe774
Copy link
Contributor Author

ruihe774 commented Apr 24, 2024

@sfan5 I added support for set_pause and EOF handling in ao_audiotrack. I'm sorry that I have no idea how to build mpv for Android. Would you plz test my implementation?

@sunpenghao I'm not familiar with WASAPI and wonder whether ao_wasapi needs EOF handling. Would you plz have a look?

@sfan5 sfan5 self-requested a review April 25, 2024 20:30
@sunpenghao
Copy link
Contributor

whether ao_wasapi needs EOF handling

Integrating EOF handling into ao_wasapi might not be that easy. The problem is there in no API to tell WASAPI to "play all remaining data but stop requesting any more". When EOF is caught, we'll need to stop the client at some point in the future to ensure no samples are lost, and only then can we inform the AO to stop streaming. This IMO offsets much if not all the benefit of having EOF handling.

Also, I'm not sure I'm grasping the full context here. Is EOF handling needed so that the AO doesn't spin around EOF? If so, WASAPI doesn't have this issue in the first place because it pulls data at a stable interval (the device scheduling period, usually ~10ms) and each time requests a fixed size. Padding silence after EOF should suffice.

@ruihe774
Copy link
Contributor Author

ruihe774 commented May 3, 2024

@sfan5 I wonder if there are any updates. 😃

@llyyr
Copy link
Contributor

llyyr commented May 5, 2024

This breaks --loop on ao_pipewire, I can't test ao_audiotrack or ao_avfoundation.

@ruihe774
Copy link
Contributor Author

ruihe774 commented May 6, 2024

This breaks --loop on ao_pipewire, I can't test ao_audiotrack or ao_avfoundation.

Weird. It works sometimes and doesn't work in other times. I'm looking into this.

@ruihe774
Copy link
Contributor Author

ruihe774 commented May 6, 2024

This breaks --loop on ao_pipewire, I can't test ao_audiotrack or ao_avfoundation.

Weird. It works sometimes and doesn't work in other times. I'm looking into this.

@llyyr Fixed in cb7de4d. Could you confirm that?

@ruihe774 ruihe774 requested a review from t-8ch May 7, 2024 02:59
@llyyr
Copy link
Contributor

llyyr commented May 7, 2024

@llyyr Fixed in cb7de4d. Could you confirm that?

Works

Copy link
Contributor

@t-8ch t-8ch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM for ao_pipewire.

@sfan5
Copy link
Member

sfan5 commented May 7, 2024

@sfan5 I wonder if there are any updates. 😃

I'm a bit occupied atm, I'll try to get to it tomorrow.

@ruihe774
Copy link
Contributor Author

ruihe774 commented May 9, 2024

@sfan5 BTW I wonder why ao_audiotrack is a pull-based AO. It uses a thread to feed data to AudioTrack_write. IMO a push-based AO is more suitable.

}

mp_cond_timedwait(&p->wakeup, &p->lock, MP_TIME_MS_TO_NS(300));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't that mean it sleeps after every write?

Copy link
Contributor Author

@ruihe774 ruihe774 May 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad. Fixed in 5e8ef9d.


JNIEnv *env = MP_JNI_GET_ENV(ao);
if (paused) {
MP_JNI_CALL_VOID(p->audiotrack, AudioTrack.pause);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought I'd have to verify: since the feed thread and not synchronized could this cause audio to be paused before the intended point (cut off)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd expect ao->driver->set_pause to perform an immediate pause, just like what we do in the push-based AO.

@sfan5
Copy link
Member

sfan5 commented May 10, 2024

To reiterate: We noticed that for pull AOs between EOF and reset or stop, the AO thread would be spinning calling ao_read_data.
Additionally it only applies to AOs that use the return value from ao_read_data. (If you ignore it you will be streaming silence, which means the callback won't immediately be called again.)
Effectively that is avfoundation, pipewire and audiotrack.

What I'm not sure about is how avfoundation and audiotrack (which both use a thread) normally block. AudioTrack_write might block, maybe??

I know I suggested this myself but given how fragile the audio code is maybe we shouldn't do this. (also since the suboptimal behavior is very minor)

Another thought:
Doesn't this situation happen every time you listen a media file to its end?
Then if restarting the streaming introduces additional latency, is gapless playback even still possible?

@ruihe774
Copy link
Contributor Author

What I'm not sure about is how avfoundation and audiotrack (which both use a thread) normally block. AudioTrack_write might block, maybe??

I didn't get you. What's the problem if AudioTrack_write might block?

In ao_avfoundation, enqueueSampleBuffer does not block. It just appends the buffer to its internal buffer. I have tested, and it can accept arbitrary duration of audio data.

I know I suggested this myself but given how fragile the audio code is maybe we shouldn't do this.

I did it carefully. BTW I'm also making other improvements to the AO codebase (e.g. #14058). IMO I don't consider the audio code a frozen codebase.

Then if restarting the streaming introduces additional latency, is gapless playback even still possible?

I'm not very familiar with ao_audiotrack. In ao_pipewire and ao_avfoundation, it involves merely the de-registrate and re-registrate of the processing callback, which is an instant operation. Giving there are still samples queued inside the internal buffer of pipewire and avfoundation when mpv is restarting playback (I have confirmed that when debugging), it won't introduce additional latency.

@ruihe774 ruihe774 requested a review from sfan5 May 11, 2024 00:34
@sfan5
Copy link
Member

sfan5 commented May 20, 2024

@ruihe774 can you split the ao_audiotrack change into a separate PR?
We can then consider this ready for merging.

I didn't get you. What's the problem if AudioTrack_write might block?

It isn't. Inversely it would be a problem if it didn't block.
Or does ao_read_data block? One of the functions during normal AO playback must be blocking or waiting, otherwise the thread would be spinning at 100% constantly...

@ruihe774
Copy link
Contributor Author

@ruihe774 can you split the ao_audiotrack change into a separate PR?
We can then consider this ready for merging.

Ok.

It isn't. Inversely it would be a problem if it didn't block.
Or does ao_read_data block? One of the functions during normal AO playback must be blocking or waiting, otherwise the thread would be spinning at 100% constantly...

Giving this, I'm wondering why ao_audiotrack is not a push-based AO. The logic in ao_thread in ao_audiotrack.c looks similar to ao_thread in buffer.c, imo.

When the stream is draining, setting stream to active has no effect.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants