wavenc: Support RF64 format

https://bugzilla.gnome.org/show_bug.cgi?id=725145
This commit is contained in:
Peter G. Baum 2014-10-05 21:24:27 +02:00 committed by Sebastian Dröge
parent 8154c90c9b
commit b5e46c05d7
2 changed files with 90 additions and 15 deletions

View file

@ -96,6 +96,9 @@ typedef struct
"rate = (int) [ 8000, 192000 ], " \ "rate = (int) [ 8000, 192000 ], " \
"channels = (int) [ 1, 2 ]" "channels = (int) [ 1, 2 ]"
#define SRC_CAPS \
"audio/x-wav; " \
"audio/x-rf64"
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
@ -106,7 +109,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC, GST_PAD_SRC,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-wav") GST_STATIC_CAPS (SRC_CAPS)
); );
#define gst_wavenc_parent_class parent_class #define gst_wavenc_parent_class parent_class
@ -165,6 +168,7 @@ gst_wavenc_init (GstWavEnc * wavenc)
#define FMT_EXT_CHUNK_LEN 48 #define FMT_EXT_CHUNK_LEN 48
#define FACT_CHUNK_LEN 12 #define FACT_CHUNK_LEN 12
#define DATA_HEADER_LEN 8 #define DATA_HEADER_LEN 8
#define DS64_CHUNK_LEN 36
static gboolean static gboolean
use_format_ext (GstWavEnc * wavenc) use_format_ext (GstWavEnc * wavenc)
@ -172,16 +176,28 @@ use_format_ext (GstWavEnc * wavenc)
return wavenc->channels > 2; return wavenc->channels > 2;
} }
static gboolean
use_fact_chunk (GstWavEnc * wavenc)
{
return use_format_ext (wavenc) && !wavenc->use_rf64;
}
static int static int
get_header_len (GstWavEnc * wavenc) get_header_len (GstWavEnc * wavenc)
{ {
int len = RIFF_CHUNK_LEN; int len = RIFF_CHUNK_LEN;
if (use_format_ext (wavenc)) if (use_format_ext (wavenc))
len += FMT_EXT_CHUNK_LEN + FACT_CHUNK_LEN; len += FMT_EXT_CHUNK_LEN;
else else
len += FMT_WAV_CHUNK_LEN; len += FMT_WAV_CHUNK_LEN;
if (use_fact_chunk (wavenc))
len += FACT_CHUNK_LEN;
if (wavenc->use_rf64)
len += DS64_CHUNK_LEN;
return len + DATA_HEADER_LEN; return len + DATA_HEADER_LEN;
} }
@ -295,10 +311,17 @@ write_fmt_chunk (GstWavEnc * wavenc, guint8 * header)
header += FMT_WAV_CHUNK_LEN; header += FMT_WAV_CHUNK_LEN;
} }
return header; return header;
} }
static guint64
get_num_frames (GstWavEnc * wavenc)
{
if (wavenc->channels == 0 || wavenc->width == 0)
return 0;
return wavenc->audio_length / (wavenc->width / 8) / wavenc->channels;
}
static guint8 * static guint8 *
write_fact_chunk (GstWavEnc * wavenc, guint8 * header) write_fact_chunk (GstWavEnc * wavenc, guint8 * header)
{ {
@ -306,18 +329,47 @@ write_fact_chunk (GstWavEnc * wavenc, guint8 * header)
GST_WRITE_UINT32_LE (header + 4, FACT_CHUNK_LEN - 8); GST_WRITE_UINT32_LE (header + 4, FACT_CHUNK_LEN - 8);
/* compressed files are only supported up to 2 channels, /* compressed files are only supported up to 2 channels,
* that means we never write a fact chunk for them */ * that means we never write a fact chunk for them */
GST_WRITE_UINT32_LE (header + 8, if (wavenc->use_rf64)
wavenc->audio_length / (wavenc->width / 8) / wavenc->channels); GST_WRITE_UINT32_LE (header + 8, 0xFFFFFFFF);
else
GST_WRITE_UINT32_LE (header + 8, (guint32) get_num_frames (wavenc));
return header + FACT_CHUNK_LEN; return header + FACT_CHUNK_LEN;
} }
static guint8 *
write_ds64_chunk (GstWavEnc * wavenc, guint64 riffLen, guint8 * header)
{
guint64 numFrames = get_num_frames (wavenc);
GST_DEBUG_OBJECT (wavenc, "riffLen=%" G_GUINT64_FORMAT
", audio length=%" G_GUINT64_FORMAT ", numFrames=%" G_GUINT64_FORMAT,
riffLen, wavenc->audio_length, numFrames);
memcpy (header, "ds64", 4);
GST_WRITE_UINT32_LE (header + 4, DS64_CHUNK_LEN - 8);
/* riffSize */
GST_WRITE_UINT32_LE (header + 8, (guint32) (riffLen & 0xFFFFFFFF));
GST_WRITE_UINT32_LE (header + 12, (guint32) (riffLen >> 32));
/* dataSize */
GST_WRITE_UINT32_LE (header + 16,
(guint32) (wavenc->audio_length & 0xFFFFFFFF));
GST_WRITE_UINT32_LE (header + 20, (guint32) (wavenc->audio_length >> 32));
/* sampleCount */
GST_WRITE_UINT32_LE (header + 24, (guint32) (numFrames & 0xFFFFFFFF));
GST_WRITE_UINT32_LE (header + 28, (guint32) (numFrames >> 32));
/* tableLength always zero for now */
GST_WRITE_UINT32_LE (header + 32, 0);
return header + DS64_CHUNK_LEN;
}
static GstBuffer * static GstBuffer *
gst_wavenc_create_header_buf (GstWavEnc * wavenc) gst_wavenc_create_header_buf (GstWavEnc * wavenc)
{ {
GstBuffer *buf; GstBuffer *buf;
GstMapInfo map; GstMapInfo map;
guint8 *header; guint8 *header;
guint32 riffLen; guint64 riffLen;
GST_DEBUG_OBJECT (wavenc, "Header size: %d", get_header_len (wavenc)); GST_DEBUG_OBJECT (wavenc, "Header size: %d", get_header_len (wavenc));
buf = gst_buffer_new_and_alloc (get_header_len (wavenc)); buf = gst_buffer_new_and_alloc (get_header_len (wavenc));
@ -329,18 +381,30 @@ gst_wavenc_create_header_buf (GstWavEnc * wavenc)
+ get_header_len (wavenc) - 8; + get_header_len (wavenc) - 8;
/* RIFF chunk */ /* RIFF chunk */
memcpy (header, "RIFF", 4); if (wavenc->use_rf64) {
GST_WRITE_UINT32_LE (header + 4, riffLen); GST_DEBUG_OBJECT (wavenc, "Using RF64");
memcpy (header, "RF64", 4);
GST_WRITE_UINT32_LE (header + 4, 0xFFFFFFFF);
} else {
memcpy (header, "RIFF", 4);
GST_WRITE_UINT32_LE (header + 4, (guint32) riffLen);
}
memcpy (header + 8, "WAVE", 4); memcpy (header + 8, "WAVE", 4);
header += RIFF_CHUNK_LEN; header += RIFF_CHUNK_LEN;
if (wavenc->use_rf64)
header = write_ds64_chunk (wavenc, riffLen, header);
header = write_fmt_chunk (wavenc, header); header = write_fmt_chunk (wavenc, header);
if (use_format_ext (wavenc)) if (use_fact_chunk (wavenc))
header = write_fact_chunk (wavenc, header); header = write_fact_chunk (wavenc, header);
/* data chunk */ /* data chunk */
memcpy (header, "data ", 4); memcpy (header, "data ", 4);
GST_WRITE_UINT32_LE (header + 4, wavenc->audio_length); if (wavenc->use_rf64)
GST_WRITE_UINT32_LE (header + 4, 0xFFFFFFFF);
else
GST_WRITE_UINT32_LE (header + 4, (guint32) wavenc->audio_length);
gst_buffer_unmap (buf, &map); gst_buffer_unmap (buf, &map);
@ -361,8 +425,8 @@ gst_wavenc_push_header (GstWavEnc * wavenc)
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
GST_DEBUG_OBJECT (wavenc, "writing header, meta_size=%u, audio_size=%u", GST_DEBUG_OBJECT (wavenc, "writing header, meta_size=%u, audio_size=%"
wavenc->meta_length, wavenc->audio_length); G_GUINT64_FORMAT, wavenc->meta_length, wavenc->audio_length);
outbuf = gst_wavenc_create_header_buf (wavenc); outbuf = gst_wavenc_create_header_buf (wavenc);
GST_BUFFER_OFFSET (outbuf) = 0; GST_BUFFER_OFFSET (outbuf) = 0;
@ -961,8 +1025,18 @@ gst_wavenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
} }
if (G_UNLIKELY (!wavenc->sent_header)) { if (G_UNLIKELY (!wavenc->sent_header)) {
gst_pad_set_caps (wavenc->srcpad, GstStructure *s;
gst_static_pad_template_get_caps (&src_factory)); GstCaps *caps = gst_pad_get_allowed_caps (wavenc->srcpad);
GST_DEBUG_OBJECT (wavenc, "allowed src caps: %" GST_PTR_FORMAT, caps);
if (!gst_caps_is_fixed (caps)) {
caps = gst_caps_truncate (caps);
}
s = gst_caps_get_structure (caps, 0);
wavenc->use_rf64 = gst_structure_has_name (s, "audio/x-rf64");
gst_pad_set_caps (wavenc->srcpad, caps);
gst_caps_unref (caps);
/* starting a file, means we have to finish it properly */ /* starting a file, means we have to finish it properly */
wavenc->finished_properly = FALSE; wavenc->finished_properly = FALSE;

View file

@ -64,9 +64,10 @@ struct _GstWavEnc {
GstAudioChannelPosition destPos[64]; GstAudioChannelPosition destPos[64];
/* data sizes */ /* data sizes */
guint32 audio_length; guint64 audio_length;
guint32 meta_length; guint32 meta_length;
gboolean use_rf64;
gboolean sent_header; gboolean sent_header;
gboolean finished_properly; gboolean finished_properly;
}; };