diff --git a/src/output/alsa_output_plugin.c b/src/output/alsa_output_plugin.c
index cf3370d01..5107d0a94 100644
--- a/src/output/alsa_output_plugin.c
+++ b/src/output/alsa_output_plugin.c
@@ -674,9 +674,7 @@ alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **e
 	}
 
 	ad->in_frame_size = audio_format_frame_size(audio_format);
-	ad->out_frame_size = ad->export.pack24
-		? (size_t)(audio_format->channels * 3)
-		: ad->in_frame_size;
+	ad->out_frame_size = pcm_export_frame_size(&ad->export, audio_format);
 
 	return true;
 }
diff --git a/src/pcm_export.c b/src/pcm_export.c
index e586b51d2..824b41b18 100644
--- a/src/pcm_export.c
+++ b/src/pcm_export.c
@@ -70,6 +70,27 @@ pcm_export_open(struct pcm_export_state *state,
 	}
 }
 
+size_t
+pcm_export_frame_size(const struct pcm_export_state *state,
+		      const struct audio_format *audio_format)
+{
+	assert(state != NULL);
+	assert(audio_format != NULL);
+
+	if (state->pack24)
+		/* packed 24 bit samples (3 bytes per sample) */
+		return audio_format->channels * 3;
+
+	if (state->dsd_usb)
+		/* the DSD-over-USB draft says that DSD 1-bit samples
+		   are enclosed within 24 bit samples, and MPD's
+		   representation of 24 bit is padded to 32 bit (4
+		   bytes per sample) */
+		return audio_format->channels * 4;
+
+	return audio_format_frame_size(audio_format);
+}
+
 const void *
 pcm_export(struct pcm_export_state *state, const void *data, size_t size,
 	   size_t *dest_size_r)
diff --git a/src/pcm_export.h b/src/pcm_export.h
index 418dcdfa3..a7e7c3f68 100644
--- a/src/pcm_export.h
+++ b/src/pcm_export.h
@@ -114,6 +114,14 @@ pcm_export_open(struct pcm_export_state *state,
 		enum sample_format sample_format, unsigned channels,
 		bool dsd_usb, bool shift8, bool pack, bool reverse_endian);
 
+/**
+ * Calculate the size of one output frame.
+ */
+G_GNUC_PURE
+size_t
+pcm_export_frame_size(const struct pcm_export_state *state,
+		      const struct audio_format *audio_format);
+
 /**
  * Export a PCM buffer.
  *