From faee5bbb78aa3a855fcecd49d111467dfbd40167 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Wed, 1 Jul 2020 21:21:38 +0200
Subject: [PATCH] decoder/opus: implement End Trimming (RFC7845 4.4)

Closes https://github.com/MusicPlayerDaemon/MPD/issues/867
---
 NEWS                                      |  2 +-
 src/decoder/plugins/OpusDecoderPlugin.cxx | 17 +++++++++++++++++
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 0f86d2431..cfab38190 100644
--- a/NEWS
+++ b/NEWS
@@ -5,7 +5,7 @@ ver 0.21.25 (not yet released)
   - file: detect premature end of file
   - smbclient: don't send credentials to MPD clients
 * decoder
-  - opus: apply pre-skip
+  - opus: apply pre-skip and end trimming
   - opus: fix memory leak
 * output
   - osx: improve sample rate selection
diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx
index ed4ec3d01..fa448a2ff 100644
--- a/src/decoder/plugins/OpusDecoderPlugin.cxx
+++ b/src/decoder/plugins/OpusDecoderPlugin.cxx
@@ -286,6 +286,23 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
 	AddGranulepos(skip);
 	skip = 0;
 
+	if (packet.e_o_s && packet.granulepos > 0 && granulepos >= 0) {
+		/* End Trimming (RFC7845 4.4): "The page with the 'end
+		   of stream' flag set MAY have a granule position
+		   that indicates the page contains less audio data
+		   than would normally be returned by decoding up
+		   through the final packet.  This is used to end the
+		   stream somewhere other than an even frame
+		   boundary. [...] The remaining samples are
+		   discarded. */
+		ogg_int64_t remaining = packet.granulepos - granulepos;
+		if (remaining <= 0)
+			return;
+
+		if (remaining < nframes)
+			nframes = remaining;
+	}
+
 	/* submit decoded samples to the DecoderClient */
 	const size_t nbytes = nframes * frame_size;
 	auto cmd = client.SubmitData(input_stream,