qtdemux: Add support for AAX encrypted audio streams

This is modelled after the DASH Common Encryption scheme, but is somewhat
simpler as more parts are fixed, i.e. just one encryption scheme.

The output caps are fixed to 'application/x-aavd'. All information
required for decryption are part of the 'adrm' atom, which is passed
on as a property. The property is attached to the buffer.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/577>
This commit is contained in:
Stefan Brüns 2020-05-02 02:21:00 +02:00 committed by StefanBruens
parent 6e68873d7f
commit ee3ea2a94d

View file

@ -111,6 +111,7 @@ GST_DEBUG_CATEGORY (qtdemux_debug);
#define GST_CAT_DEFAULT qtdemux_debug
typedef struct _QtDemuxCencSampleSetInfo QtDemuxCencSampleSetInfo;
typedef struct _QtDemuxAavdEncryptionInfo QtDemuxAavdEncryptionInfo;
/* Macros for converting to/from timescale */
#define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
@ -234,6 +235,11 @@ struct _QtDemuxCencSampleSetInfo
GPtrArray *crypto_info;
};
struct _QtDemuxAavdEncryptionInfo
{
GstStructure *default_properties;
};
static const gchar *
qt_demux_state_string (enum QtDemuxState state)
{
@ -2526,6 +2532,12 @@ gst_qtdemux_stream_clear (QtDemuxStream * stream)
if (info->crypto_info)
g_ptr_array_free (info->crypto_info, TRUE);
}
if (stream->protection_scheme_type == FOURCC_aavd) {
QtDemuxAavdEncryptionInfo *info =
(QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
if (info->default_properties)
gst_structure_free (info->default_properties);
}
g_free (stream->protection_scheme_info);
stream->protection_scheme_info = NULL;
}
@ -5694,6 +5706,16 @@ gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad));
if (stream->protected && stream->protection_scheme_type == FOURCC_aavd) {
GstStructure *crypto_info;
QtDemuxAavdEncryptionInfo *info =
(QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
crypto_info = gst_structure_copy (info->default_properties);
if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
GST_ERROR_OBJECT (qtdemux, "failed to attach aavd metadata to buffer");
}
if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
GstStructure *crypto_info;
QtDemuxCencSampleSetInfo *info =
@ -8271,6 +8293,17 @@ gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
g_return_val_if_fail (gst_caps_get_size (CUR_STREAM (stream)->caps) == 1,
FALSE);
if (stream->protection_scheme_type == FOURCC_aavd) {
s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
if (!gst_structure_has_name (s, "application/x-aavd")) {
gst_structure_set (s,
"original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
NULL);
gst_structure_set_name (s, "application/x-aavd");
}
return TRUE;
}
if (stream->protection_scheme_type != FOURCC_cenc) {
GST_ERROR_OBJECT (qtdemux,
"unsupported protection scheme: %" GST_FOURCC_FORMAT,
@ -10249,6 +10282,42 @@ qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
}
}
static gboolean
qtdemux_parse_protection_aavd (GstQTDemux * qtdemux,
QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
{
GNode *adrm;
guint32 adrm_size;
GstBuffer *adrm_buf = NULL;
QtDemuxAavdEncryptionInfo *info;
adrm = qtdemux_tree_get_child_by_type (container, FOURCC_adrm);
if (G_UNLIKELY (!adrm)) {
GST_ERROR_OBJECT (qtdemux, "aavd box does not contain mandatory adrm box");
return FALSE;
}
adrm_size = QT_UINT32 (adrm->data);
adrm_buf =
gst_buffer_new_wrapped (g_memdup (adrm->data, adrm_size), adrm_size);
stream->protection_scheme_type = FOURCC_aavd;
if (!stream->protection_scheme_info)
stream->protection_scheme_info = g_new0 (QtDemuxAavdEncryptionInfo, 1);
info = (QtDemuxAavdEncryptionInfo *) stream->protection_scheme_info;
if (info->default_properties)
gst_structure_free (info->default_properties);
info->default_properties = gst_structure_new ("application/x-aavd",
"encrypted", G_TYPE_BOOLEAN, TRUE,
"adrm", GST_TYPE_BUFFER, adrm_buf, NULL);
gst_buffer_unref (adrm_buf);
*original_fmt = FOURCC_mp4a;
return TRUE;
}
/* Parses the boxes defined in ISO/IEC 14496-12 that enable support for
* protected streams (sinf, frma, schm and schi); if the protection scheme is
* Common Encryption (cenc), the function will also parse the tenc box (defined
@ -10697,6 +10766,19 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
goto error_encrypted;
if (fourcc == FOURCC_aavd) {
if (stream->subtype != FOURCC_soun) {
GST_ERROR_OBJECT (qtdemux,
"Unexpeced stsd type 'aavd' outside 'soun' track");
} else {
/* encrypted audio with sound sample description v0 */
GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
stream->protected = TRUE;
if (!qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc))
GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
}
}
if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
/* FIXME this looks wrong, there might be multiple children
* with the same type */
@ -12245,16 +12327,22 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
GST_TAG_BITRATE, bitrate, NULL);
}
esds = NULL;
mp4a = qtdemux_tree_get_child_by_index (stsd, stsd_index);
if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != fourcc) {
if (stream->protected && QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca)
mp4a = NULL;
else if (!stream->protected)
if (stream->protected) {
if (QTDEMUX_TREE_NODE_FOURCC (mp4a) == FOURCC_aavd) {
esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
}
if (QTDEMUX_TREE_NODE_FOURCC (mp4a) != FOURCC_enca) {
mp4a = NULL;
}
} else {
mp4a = NULL;
}
}
wave = NULL;
esds = NULL;
if (mp4a) {
wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
if (wave)