static GstFlowReturn webkitMediaPlayReadyDecryptTransformInPlace(GstBaseTransform* base, GstBuffer* buffer) { WebKitMediaPlayReadyDecrypt* self = WEBKIT_MEDIA_PLAYREADY_DECRYPT(base); GstFlowReturn result = GST_FLOW_OK; GstMapInfo map; const GValue* value; guint sampleIndex = 0; int errorCode; uint32_t trackID = 0; GstPad* pad; GstCaps* caps; GstMapInfo boxMap; GstBuffer* box = nullptr; GstProtectionMeta* protectionMeta = 0; gboolean boxMapped = FALSE; gboolean bufferMapped = FALSE; GST_TRACE_OBJECT(self, "Processing buffer"); g_mutex_lock(&self->mutex); GST_TRACE_OBJECT(self, "Mutex acquired, stream received: %s", self->streamReceived ? "yes":"no"); // The key might not have been received yet. Wait for it. if (!self->streamReceived) g_cond_wait(&self->condition, &self->mutex); if (!self->streamReceived) { GST_DEBUG_OBJECT(self, "Condition signaled from state change transition. Aborting."); result = GST_FLOW_NOT_SUPPORTED; goto beach; } GST_TRACE_OBJECT(self, "Proceeding with decryption"); protectionMeta = reinterpret_cast<GstProtectionMeta*>(gst_buffer_get_protection_meta(buffer)); if (!protectionMeta || !buffer) { if (!protectionMeta) GST_ERROR_OBJECT(self, "Failed to get GstProtection metadata from buffer %p", buffer); if (!buffer) GST_ERROR_OBJECT(self, "Failed to get writable buffer"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } bufferMapped = gst_buffer_map(buffer, &map, static_cast<GstMapFlags>(GST_MAP_READWRITE)); if (!bufferMapped) { GST_ERROR_OBJECT(self, "Failed to map buffer"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } pad = gst_element_get_static_pad(GST_ELEMENT(self), "src"); caps = gst_pad_get_current_caps(pad); if (g_str_has_prefix(gst_structure_get_name(gst_caps_get_structure(caps, 0)), "video/")) trackID = 1; else trackID = 2; gst_caps_unref(caps); gst_object_unref(pad); GST_TRACE_OBJECT(self, "Protection meta: %" GST_PTR_FORMAT, protectionMeta->info); if (gst_structure_get_uint(protectionMeta->info, "sample-index", &sampleIndex)) { // Process the PIFF box. value = gst_structure_get_value(protectionMeta->info, "box"); if (!value) { GST_ERROR_OBJECT(self, "Failed to get encryption box for sample"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } box = gst_value_get_buffer(value); boxMapped = gst_buffer_map(box, &boxMap, GST_MAP_READ); if (!boxMapped) { GST_ERROR_OBJECT(self, "Failed to map encryption box"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } GST_TRACE_OBJECT(self, "decrypt sample %u", sampleIndex); if ((errorCode = self->sessionMetaData->decrypt(static_cast<void*>(map.data), static_cast<uint32_t>(map.size), static_cast<void*>(boxMap.data), static_cast<uint32_t>(boxMap.size), static_cast<uint32_t>(sampleIndex), trackID))) { GST_WARNING_OBJECT(self, "ERROR - packet decryption failed [%d]", errorCode); GST_MEMDUMP_OBJECT(self, "box", boxMap.data, boxMap.size); result = GST_FLOW_ERROR; goto beach; } } else { // Process CENC data. guint ivSize; gboolean encrypted; GstBuffer* ivBuffer = nullptr; GstMapInfo ivMap; unsigned position = 0; unsigned sampleIndex = 0; guint subSampleCount; GstBuffer* subsamplesBuffer = nullptr; GstMapInfo subSamplesMap; GstByteReader* reader = nullptr; if (!gst_structure_get_uint(protectionMeta->info, "iv_size", &ivSize)) { GST_ERROR_OBJECT(self, "failed to get iv_size"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } if (!gst_structure_get_boolean(protectionMeta->info, "encrypted", &encrypted)) { GST_ERROR_OBJECT(self, "failed to get encrypted flag"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } // Unencrypted sample. if (!ivSize || !encrypted) goto beach; if (!gst_structure_get_uint(protectionMeta->info, "subsample_count", &subSampleCount)) { GST_ERROR_OBJECT(self, "failed to get subsample_count"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } value = gst_structure_get_value(protectionMeta->info, "iv"); if (!value) { GST_ERROR_OBJECT(self, "Failed to get IV for sample"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } ivBuffer = gst_value_get_buffer(value); if (!gst_buffer_map(ivBuffer, &ivMap, GST_MAP_READ)) { GST_ERROR_OBJECT(self, "Failed to map IV"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } if (subSampleCount) { value = gst_structure_get_value(protectionMeta->info, "subsamples"); if (!value) { GST_ERROR_OBJECT(self, "Failed to get subsamples"); result = GST_FLOW_NOT_SUPPORTED; goto release; } subsamplesBuffer = gst_value_get_buffer(value); if (!gst_buffer_map(subsamplesBuffer, &subSamplesMap, GST_MAP_READ)) { GST_ERROR_OBJECT(self, "Failed to map subsample buffer"); result = GST_FLOW_NOT_SUPPORTED; goto release; } } reader = gst_byte_reader_new(subSamplesMap.data, subSamplesMap.size); if (!reader) { GST_ERROR_OBJECT(self, "Failed to allocate subsample reader"); result = GST_FLOW_NOT_SUPPORTED; goto release; } GST_DEBUG_OBJECT(self, "position: %d, size: %d", position, map.size); while (position < map.size) { guint16 nBytesClear = 0; guint32 nBytesEncrypted = 0; if (sampleIndex < subSampleCount) { if (!gst_byte_reader_get_uint16_be(reader, &nBytesClear) || !gst_byte_reader_get_uint32_be(reader, &nBytesEncrypted)) { result = GST_FLOW_NOT_SUPPORTED; GST_DEBUG_OBJECT(self, "unsupported"); goto release; } sampleIndex++; } else { nBytesClear = 0; nBytesEncrypted = map.size - position; } GST_TRACE_OBJECT(self, "%d bytes clear (todo=%d)", nBytesClear, map.size - position); position += nBytesClear; if (nBytesEncrypted) { GST_TRACE_OBJECT(self, "%d bytes encrypted (todo=%d)", nBytesEncrypted, map.size - position); if ((errorCode = self->sessionMetaData->processPayload(trackID, static_cast<const void*>(ivMap.data), static_cast<uint32_t>(ivMap.size), static_cast<void*>(map.data + position), static_cast<uint32_t>(nBytesEncrypted)))) { GST_WARNING_OBJECT(self, "ERROR - packet decryption failed [%d]", errorCode); result = GST_FLOW_ERROR; goto release; } position += nBytesEncrypted; } } release: gst_buffer_unmap(ivBuffer, &ivMap); if (reader) gst_byte_reader_free(reader); if (subsamplesBuffer) gst_buffer_unmap(subsamplesBuffer, &subSamplesMap); } beach: if (boxMapped) gst_buffer_unmap(box, &boxMap); if (bufferMapped) gst_buffer_unmap(buffer, &map); if (protectionMeta) gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); GST_TRACE_OBJECT(self, "Unlocking mutex"); g_mutex_unlock(&self->mutex); return result; }
static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseTransform* base, GstBuffer* buffer) { WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(base); WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self); WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self); guint subSampleCount, ivSize; gboolean encrypted; const GValue* value; GstBuffer* ivBuffer = nullptr; GstBuffer* subSamplesBuffer = nullptr; GstProtectionMeta* protectionMeta; LockHolder locker(priv->mutex); // The key might not have been received yet. Wait for it. if (!priv->keyReceived) { GST_DEBUG_OBJECT(self, "key not available yet, waiting for it"); priv->condition.wait(priv->mutex); if (!priv->keyReceived) { GST_ERROR_OBJECT(self, "key not available"); return GST_FLOW_NOT_SUPPORTED; } GST_DEBUG_OBJECT(self, "key received, continuing"); } protectionMeta = reinterpret_cast<GstProtectionMeta*>(gst_buffer_get_protection_meta(buffer)); if (!protectionMeta) { GST_ERROR_OBJECT(self, "Failed to get GstProtection metadata from buffer %p", buffer); return GST_FLOW_NOT_SUPPORTED; } if (!gst_structure_get_uint(protectionMeta->info, "iv_size", &ivSize)) { GST_ERROR_OBJECT(self, "Failed to get iv_size"); gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); return GST_FLOW_NOT_SUPPORTED; } if (!gst_structure_get_boolean(protectionMeta->info, "encrypted", &encrypted)) { GST_ERROR_OBJECT(self, "Failed to get encrypted flag"); gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); return GST_FLOW_NOT_SUPPORTED; } if (!ivSize || !encrypted) { gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); return GST_FLOW_OK; } GST_DEBUG_OBJECT(base, "protection meta: %" GST_PTR_FORMAT, protectionMeta->info); if (!gst_structure_get_uint(protectionMeta->info, "subsample_count", &subSampleCount)) { GST_ERROR_OBJECT(self, "Failed to get subsample_count"); gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); return GST_FLOW_NOT_SUPPORTED; } if (subSampleCount) { value = gst_structure_get_value(protectionMeta->info, "subsamples"); if (!value) { GST_ERROR_OBJECT(self, "Failed to get subsamples"); gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); return GST_FLOW_NOT_SUPPORTED; } subSamplesBuffer = gst_value_get_buffer(value); } if (!klass->setupCipher(self)) { GST_ERROR_OBJECT(self, "Failed to configure cipher"); gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); return GST_FLOW_NOT_SUPPORTED; } value = gst_structure_get_value(protectionMeta->info, "iv"); if (!value) { GST_ERROR_OBJECT(self, "Failed to get IV for sample"); klass->releaseCipher(self); gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); return GST_FLOW_NOT_SUPPORTED; } ivBuffer = gst_value_get_buffer(value); GST_TRACE_OBJECT(self, "decrypting"); if (!klass->decrypt(self, ivBuffer, buffer, subSampleCount, subSamplesBuffer)) { GST_ERROR_OBJECT(self, "Decryption failed"); klass->releaseCipher(self); gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); return GST_FLOW_NOT_SUPPORTED; } klass->releaseCipher(self); gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); return GST_FLOW_OK; }
static GstFlowReturn gst_cenc_decrypt_transform_ip (GstBaseTransform * base, GstBuffer * buf) { GstCencDecrypt *self = GST_CENC_DECRYPT (base); GstFlowReturn ret = GST_FLOW_OK; GstMapInfo map, iv_map; const GstCencKeyPair *keypair; const GstProtectionMeta *prot_meta = NULL; guint pos = 0; gint sample_index = 0; guint subsample_count; AesCtrState *state = NULL; guint iv_size; gboolean encrypted; const GValue *value; GstBuffer *key_id = NULL; GstBuffer *iv_buf = NULL; GBytes *iv_bytes = NULL; GstBuffer *subsamples_buf = NULL; GstMapInfo subsamples_map; GstByteReader *reader=NULL; GST_TRACE_OBJECT (self, "decrypt in-place"); prot_meta = (GstProtectionMeta*) gst_buffer_get_protection_meta (buf); if (!prot_meta || !buf) { if (!prot_meta) { GST_ERROR_OBJECT (self, "Failed to get GstProtection metadata from buffer"); } if (!buf) { GST_ERROR_OBJECT (self, "Failed to get writable buffer"); } ret = GST_FLOW_NOT_SUPPORTED; goto out; } if (!gst_buffer_map (buf, &map, GST_MAP_READWRITE)) { GST_ERROR_OBJECT (self, "Failed to map buffer"); ret = GST_FLOW_NOT_SUPPORTED; goto release; } GST_TRACE_OBJECT (self, "decrypt sample %d", (gint)map.size); if(!gst_structure_get_uint(prot_meta->info,"iv_size",&iv_size)){ GST_ERROR_OBJECT (self, "failed to get iv_size"); ret = GST_FLOW_NOT_SUPPORTED; goto release; } if(!gst_structure_get_boolean(prot_meta->info,"encrypted",&encrypted)){ GST_ERROR_OBJECT (self, "failed to get encrypted flag"); ret = GST_FLOW_NOT_SUPPORTED; goto release; } if (iv_size == 0 || !encrypted) { /* sample is not encrypted */ goto beach; } GST_DEBUG_OBJECT (base, "protection meta: %" GST_PTR_FORMAT, prot_meta->info); if(!gst_structure_get_uint(prot_meta->info,"subsample_count",&subsample_count)){ GST_ERROR_OBJECT (self, "failed to get subsample_count"); ret = GST_FLOW_NOT_SUPPORTED; goto release; } value = gst_structure_get_value (prot_meta->info, "kid"); if(!value){ GST_ERROR_OBJECT (self, "Failed to get KID for sample"); ret = GST_FLOW_NOT_SUPPORTED; goto release; } key_id = gst_value_get_buffer (value); value = gst_structure_get_value (prot_meta->info, "iv"); if(!value){ GST_ERROR_OBJECT (self, "Failed to get IV for sample"); ret = GST_FLOW_NOT_SUPPORTED; goto release; } iv_buf = gst_value_get_buffer (value); if(!gst_buffer_map (iv_buf, &iv_map, GST_MAP_READ)){ GST_ERROR_OBJECT (self, "Failed to map IV"); ret = GST_FLOW_NOT_SUPPORTED; goto release; } iv_bytes = g_bytes_new (iv_map.data, iv_map.size); gst_buffer_unmap (iv_buf, &iv_map); if(subsample_count){ value = gst_structure_get_value (prot_meta->info, "subsamples"); if(!value){ GST_ERROR_OBJECT (self, "Failed to get subsamples"); ret = GST_FLOW_NOT_SUPPORTED; goto release; } subsamples_buf = gst_value_get_buffer (value); if(!gst_buffer_map (subsamples_buf, &subsamples_map, GST_MAP_READ)){ GST_ERROR_OBJECT (self, "Failed to map subsample buffer"); ret = GST_FLOW_NOT_SUPPORTED; goto release; } } keypair = gst_cenc_decrypt_lookup_key (self,key_id); if (!keypair) { gsize sz; GST_ERROR_OBJECT (self, "Failed to lookup key"); GST_MEMDUMP_OBJECT (self, "Key ID:", g_bytes_get_data (keypair->key_id, &sz), sz); ret = GST_FLOW_NOT_SUPPORTED; goto release; } state = gst_aes_ctr_decrypt_new (keypair->key, iv_bytes); if (!state) { GST_ERROR_OBJECT (self, "Failed to init AES cipher"); ret = GST_FLOW_NOT_SUPPORTED; goto release; } if (subsample_count) { reader = gst_byte_reader_new (subsamples_map.data, subsamples_map.size); if(!reader){ GST_ERROR_OBJECT (self, "Failed to allocate subsample reader"); ret = GST_FLOW_NOT_SUPPORTED; goto release; } } while (pos < map.size) { guint16 n_bytes_clear = 0; guint32 n_bytes_encrypted = 0; if (sample_index < subsample_count) { if (!gst_byte_reader_get_uint16_be (reader, &n_bytes_clear) || !gst_byte_reader_get_uint32_be (reader, &n_bytes_encrypted)) { ret = GST_FLOW_NOT_SUPPORTED; goto release; } sample_index++; } else { n_bytes_clear = 0; n_bytes_encrypted = map.size - pos; } GST_TRACE_OBJECT (self, "%u bytes clear (todo=%d)", n_bytes_clear, (gint)map.size - pos); pos += n_bytes_clear; if (n_bytes_encrypted) { GST_TRACE_OBJECT (self, "%u bytes encrypted (todo=%d)", n_bytes_encrypted, (gint)map.size - pos); gst_aes_ctr_decrypt_ip (state, map.data + pos, n_bytes_encrypted); pos += n_bytes_encrypted; } } beach: gst_buffer_unmap (buf, &map); if (state) { gst_aes_ctr_decrypt_unref (state); } release: if (reader){ gst_byte_reader_free (reader); } if(subsamples_buf){ gst_buffer_unmap (subsamples_buf, &subsamples_map); } if (prot_meta) { gst_buffer_remove_meta (buf, (GstMeta *) prot_meta); } if (iv_bytes) { g_bytes_unref (iv_bytes); } out: return ret; }
static GstFlowReturn webkitMediaPlayReadyDecryptTransformInPlace(GstBaseTransform* base, GstBuffer* buffer) { WebKitMediaPlayReadyDecrypt* self = WEBKIT_MEDIA_PLAYREADY_DECRYPT(base); GstFlowReturn result = GST_FLOW_OK; GstMapInfo map; const GValue* value; guint sampleIndex = 0; int errorCode; uint32_t trackID = 0; GstPad* pad; GstCaps* caps; GstMapInfo boxMap; GstBuffer* box = nullptr; GstProtectionMeta* protectionMeta = 0; gboolean boxMapped = FALSE; gboolean bufferMapped = FALSE; GST_TRACE_OBJECT(self, "Processing buffer"); g_mutex_lock(&self->mutex); GST_TRACE_OBJECT(self, "Mutex acquired, stream received: %s", self->streamReceived ? "yes":"no"); // The key might not have been received yet. Wait for it. if (!self->streamReceived) g_cond_wait(&self->condition, &self->mutex); if (!self->streamReceived) { GST_DEBUG_OBJECT(self, "Condition signaled from state change transition. Aborting."); result = GST_FLOW_NOT_SUPPORTED; goto beach; } GST_TRACE_OBJECT(self, "Proceeding with decryption"); protectionMeta = reinterpret_cast<GstProtectionMeta*>(gst_buffer_get_protection_meta(buffer)); if (!protectionMeta || !buffer) { if (!protectionMeta) GST_ERROR_OBJECT(self, "Failed to get GstProtection metadata from buffer %p", buffer); if (!buffer) GST_ERROR_OBJECT(self, "Failed to get writable buffer"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } bufferMapped = gst_buffer_map(buffer, &map, static_cast<GstMapFlags>(GST_MAP_READWRITE)); if (!bufferMapped) { GST_ERROR_OBJECT(self, "Failed to map buffer"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } pad = gst_element_get_static_pad(GST_ELEMENT(self), "src"); caps = gst_pad_get_current_caps(pad); if (g_str_has_prefix(gst_structure_get_name(gst_caps_get_structure(caps, 0)), "video/")) trackID = 1; else trackID = 2; gst_caps_unref(caps); gst_object_unref(pad); if (!gst_structure_get_uint(protectionMeta->info, "sample-index", &sampleIndex)) { GST_ERROR_OBJECT(self, "failed to get sample-index"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } value = gst_structure_get_value(protectionMeta->info, "box"); if (!value) { GST_ERROR_OBJECT(self, "Failed to get encryption box for sample"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } box = gst_value_get_buffer(value); boxMapped = gst_buffer_map(box, &boxMap, GST_MAP_READ); if (!boxMapped) { GST_ERROR_OBJECT(self, "Failed to map encryption box"); result = GST_FLOW_NOT_SUPPORTED; goto beach; } GST_TRACE_OBJECT(self, "decrypt sample %u", sampleIndex); if ((errorCode = self->sessionMetaData->decrypt(static_cast<void*>(map.data), static_cast<uint32_t>(map.size), static_cast<void*>(boxMap.data), static_cast<uint32_t>(boxMap.size), static_cast<uint32_t>(sampleIndex), trackID))) { GST_WARNING_OBJECT(self, "ERROR - packet decryption failed [%d]", errorCode); GST_MEMDUMP_OBJECT(self, "box", boxMap.data, boxMap.size); result = GST_FLOW_ERROR; goto beach; } beach: if (boxMapped) gst_buffer_unmap(box, &boxMap); if (bufferMapped) gst_buffer_unmap(buffer, &map); if (protectionMeta) gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); GST_TRACE_OBJECT(self, "Unlocking mutex"); g_mutex_unlock(&self->mutex); return result; }