From ee3ea2a94d839092a5c518125dac5584a37c6c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Br=C3=BCns?= Date: Sat, 2 May 2020 02:21:00 +0200 Subject: [PATCH] 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: --- gst/isomp4/qtdemux.c | 96 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index 2929cb4a92..146f409cb6 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -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)