static gboolean parse_exif_tag_header (GstByteReader * reader, gint byte_order, GstExifTagData * _tagdata) { g_assert (_tagdata); /* read the fields */ if (byte_order == G_LITTLE_ENDIAN) { if (!gst_byte_reader_get_uint16_le (reader, &_tagdata->tag) || !gst_byte_reader_get_uint16_le (reader, &_tagdata->tag_type) || !gst_byte_reader_get_uint32_le (reader, &_tagdata->count) || !gst_byte_reader_get_data (reader, 4, &_tagdata->offset_as_data)) { return FALSE; } _tagdata->offset = GST_READ_UINT32_LE (_tagdata->offset_as_data); } else { if (!gst_byte_reader_get_uint16_be (reader, &_tagdata->tag) || !gst_byte_reader_get_uint16_be (reader, &_tagdata->tag_type) || !gst_byte_reader_get_uint32_be (reader, &_tagdata->count) || !gst_byte_reader_get_data (reader, 4, &_tagdata->offset_as_data)) { return FALSE; } _tagdata->offset = GST_READ_UINT32_BE (_tagdata->offset_as_data); } return TRUE; }
/** * gst_tag_list_from_exif_buffer_with_tiff_header: * @buffer: The exif buffer * * Parses the exif tags starting with a tiff header structure. * * Returns: The taglist * * Since: 0.10.30 */ GstTagList * gst_tag_list_from_exif_buffer_with_tiff_header (const GstBuffer * buffer) { GstByteReader reader; guint16 fortytwo = 42; guint16 endianness = 0; guint32 offset; GstTagList *taglist = NULL; GstBuffer *subbuffer; GST_LOG ("Parsing exif tags with tiff header of size %u", GST_BUFFER_SIZE (buffer)); gst_byte_reader_init_from_buffer (&reader, buffer); GST_LOG ("Parsing the tiff header"); if (!gst_byte_reader_get_uint16_be (&reader, &endianness)) { goto byte_reader_fail; } if (endianness == TIFF_LITTLE_ENDIAN) { if (!gst_byte_reader_get_uint16_le (&reader, &fortytwo) || !gst_byte_reader_get_uint32_le (&reader, &offset)) goto byte_reader_fail; } else if (endianness == TIFF_BIG_ENDIAN) { if (!gst_byte_reader_get_uint16_be (&reader, &fortytwo) || !gst_byte_reader_get_uint32_be (&reader, &offset)) goto byte_reader_fail; } else { GST_WARNING ("Invalid endianness number %u", endianness); return NULL; } if (fortytwo != 42) { GST_WARNING ("Invalid magic number %u, should be 42", fortytwo); return NULL; } subbuffer = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buffer) - (TIFF_HEADER_SIZE - 2)); memcpy (GST_BUFFER_DATA (subbuffer), GST_BUFFER_DATA (buffer) + TIFF_HEADER_SIZE, GST_BUFFER_SIZE (buffer) - TIFF_HEADER_SIZE); taglist = gst_tag_list_from_exif_buffer (subbuffer, endianness == TIFF_LITTLE_ENDIAN ? G_LITTLE_ENDIAN : G_BIG_ENDIAN, 8); gst_buffer_unref (subbuffer); return taglist; byte_reader_fail: { GST_WARNING ("Failed to read values from buffer"); return NULL; } }
static inline gboolean gst_jpeg_parse_skip_marker (GstJpegParse * parse, GstByteReader * reader, guint8 marker) { guint16 size = 0; if (!gst_byte_reader_get_uint16_be (reader, &size)) return FALSE; #ifndef GST_DISABLE_DEBUG /* We'd pry the id of the skipped application segment */ if (marker >= APP0 && marker <= APP15) { const gchar *id_str = NULL; if (gst_byte_reader_peek_string_utf8 (reader, &id_str)) { GST_DEBUG_OBJECT (parse, "unhandled marker %x: '%s' skiping %u bytes", marker, id_str ? id_str : "(NULL)", size); } else { GST_DEBUG_OBJECT (parse, "unhandled marker %x skiping %u bytes", marker, size); } } #else GST_DEBUG_OBJECT (parse, "unhandled marker %x skiping %u bytes", marker, size); #endif // GST_DISABLE_DEBUG if (!gst_byte_reader_skip (reader, size - 2)) return FALSE; return TRUE; }
/* read comment and post as tag */ static inline gboolean gst_jpeg_parse_com (GstJpegParse * parse, GstByteReader * reader) { const guint8 *data = NULL; guint16 size = 0; gchar *comment; if (!gst_byte_reader_get_uint16_be (reader, &size)) return FALSE; size -= 2; if (!gst_byte_reader_get_data (reader, size, &data)) return FALSE; comment = get_utf8_from_data (data, size); if (comment) { GstTagList *taglist = get_tag_list (parse); gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COMMENT, comment, NULL); GST_DEBUG_OBJECT (parse, "collected tags: %" GST_PTR_FORMAT, taglist); g_free (comment); } return TRUE; }
static inline gboolean gst_jpeg_parse_app1 (GstJpegParse * parse, GstByteReader * reader) { guint16 size = 0; const gchar *id_str; const guint8 *data = NULL; if (!gst_byte_reader_get_uint16_be (reader, &size)) return FALSE; size -= 2; /* 2 bytes for the mark */ if (!gst_byte_reader_peek_string_utf8 (reader, &id_str)) return FALSE; if (!strncmp (id_str, "Exif", 4)) { /* skip id + NUL + padding */ if (!gst_byte_reader_skip (reader, 6)) return FALSE; size -= 6; /* handle exif metadata */ if (!gst_byte_reader_get_data (reader, size, &data)) return FALSE; extract_and_queue_tags (parse, size, (guint8 *) data, gst_tag_list_from_exif_buffer_with_tiff_header); GST_LOG_OBJECT (parse, "parsed marker %x: '%s' %u bytes", APP1, id_str, size); } else if (!strncmp (id_str, "http://ns.adobe.com/xap/1.0/", 28)) { /* skip the id + NUL */ if (!gst_byte_reader_skip (reader, 29)) return FALSE; size -= 29; /* handle xmp metadata */ if (!gst_byte_reader_get_data (reader, size, &data)) return FALSE; extract_and_queue_tags (parse, size, (guint8 *) data, gst_tag_list_from_xmp_buffer); GST_LOG_OBJECT (parse, "parsed marker %x: '%s' %u bytes", APP1, id_str, size); } else { if (!gst_jpeg_parse_skip_marker (parse, reader, APP1)) return FALSE; } return TRUE; }
static gboolean parse_exif_ifd (GstExifReader * exif_reader, gint buf_offset, const GstExifTagMatch * tag_map) { GstByteReader reader; guint16 entries = 0; guint16 i; g_return_val_if_fail (exif_reader->byte_order == G_LITTLE_ENDIAN || exif_reader->byte_order == G_BIG_ENDIAN, FALSE); gst_byte_reader_init_from_buffer (&reader, exif_reader->buffer); if (!gst_byte_reader_set_pos (&reader, buf_offset)) { GST_WARNING ("Buffer offset invalid when parsing exif ifd"); return FALSE; } /* read the IFD entries number */ if (exif_reader->byte_order == G_LITTLE_ENDIAN) { if (!gst_byte_reader_get_uint16_le (&reader, &entries)) goto read_error; } else { if (!gst_byte_reader_get_uint16_be (&reader, &entries)) goto read_error; } GST_DEBUG ("Read number of entries: %u", entries); /* iterate over the buffer and find the tags and stuff */ for (i = 0; i < entries; i++) { GstExifTagData tagdata; gint map_index; GST_LOG ("Reading entry: %u", i); if (!parse_exif_tag_header (&reader, exif_reader->byte_order, &tagdata)) goto read_error; GST_DEBUG ("Parsed tag: id 0x%x, type %u, count %u, offset %u (0x%x)", tagdata.tag, tagdata.tag_type, tagdata.count, tagdata.offset, tagdata.offset); map_index = exif_tag_map_find_reverse (tagdata.tag, tag_map, TRUE); if (map_index == -1) { GST_WARNING ("Unmapped exif tag: 0x%x", tagdata.tag); continue; } /* inner ifd tags handling */ if (tagdata.tag == EXIF_GPS_IFD_TAG) { i += parse_exif_ifd (exif_reader, tagdata.offset - exif_reader->base_offset, tag_map_gps); continue; } /* tags that need specialized deserialization */ if (tag_map[map_index].deserialize) { i += tag_map[map_index].deserialize (exif_reader, &reader, &tag_map[map_index], &tagdata); continue; } switch (tagdata.tag_type) { case EXIF_TYPE_ASCII: parse_exif_ascii_tag (exif_reader, tag_map[map_index].gst_tag, tagdata.count, tagdata.offset, tagdata.offset_as_data); break; case EXIF_TYPE_RATIONAL: parse_exif_rational_tag (exif_reader, tag_map[map_index].gst_tag, tagdata.count, tagdata.offset, 1); break; default: GST_WARNING ("Unhandled tag type: %u", tagdata.tag_type); break; } } return TRUE; read_error: { GST_WARNING ("Failed to parse the exif ifd"); return FALSE; } }
static void gst_exif_tag_rewrite_offsets (GstExifWriter * writer, guint32 base_offset) { guint32 offset; GST_LOG ("Rewriting tag entries offsets"); offset = gst_byte_writer_get_size (&writer->tagwriter); while (gst_byte_writer_get_pos (&writer->tagwriter) < gst_byte_writer_get_size (&writer->tagwriter)) { guint16 type = 0; guint32 cur_offset = 0; GstByteReader *reader; gint byte_size = 0; guint32 count = 0; guint16 tag_id = 0; reader = (GstByteReader *) & writer->tagwriter; /* read the type */ if (writer->byte_order == G_LITTLE_ENDIAN) { if (!gst_byte_reader_get_uint16_le (reader, &tag_id)) break; if (!gst_byte_reader_get_uint16_le (reader, &type)) break; if (!gst_byte_reader_get_uint32_le (reader, &count)) break; } else { if (!gst_byte_reader_get_uint16_be (reader, &tag_id)) break; if (!gst_byte_reader_get_uint16_be (reader, &type)) break; if (!gst_byte_reader_get_uint32_be (reader, &count)) break; } switch (type) { case EXIF_TYPE_BYTE: case EXIF_TYPE_ASCII: case EXIF_TYPE_UNDEFINED: byte_size = count; break; case EXIF_TYPE_SHORT: byte_size = count * 2; /* 2 bytes */ break; case EXIF_TYPE_LONG: case EXIF_TYPE_SLONG: byte_size = count * 4; /* 4 bytes */ break; case EXIF_TYPE_RATIONAL: case EXIF_TYPE_SRATIONAL: byte_size = count * 8; /* 8 bytes */ break; default: g_assert_not_reached (); break; } /* adjust the offset if needed */ if (byte_size > 4 || tag_id == EXIF_GPS_IFD_TAG) { if (writer->byte_order == G_LITTLE_ENDIAN) { if (gst_byte_reader_peek_uint32_le (reader, &cur_offset)) { gst_byte_writer_put_uint32_le (&writer->tagwriter, cur_offset + offset + base_offset); } } else { if (gst_byte_reader_peek_uint32_be (reader, &cur_offset)) { gst_byte_writer_put_uint32_be (&writer->tagwriter, cur_offset + offset + base_offset); } } GST_DEBUG ("Rewriting tag offset from %u to (%u + %u + %u) %u", cur_offset, cur_offset, offset, base_offset, cur_offset + offset + base_offset); } else { gst_byte_reader_skip (reader, 4); GST_DEBUG ("No need to rewrite tag offset"); } } }
static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* ivBuffer, GstBuffer* buffer, unsigned subSampleCount, GstBuffer* subSamplesBuffer) { GstMapInfo ivMap; if (!gst_buffer_map(ivBuffer, &ivMap, GST_MAP_READ)) { GST_ERROR_OBJECT(self, "Failed to map IV"); return false; } uint8_t ctr[CLEARKEY_SIZE]; if (ivMap.size == 8) { memset(ctr + 8, 0, 8); memcpy(ctr, ivMap.data, 8); } else { ASSERT(ivMap.size == CLEARKEY_SIZE); memcpy(ctr, ivMap.data, CLEARKEY_SIZE); } gst_buffer_unmap(ivBuffer, &ivMap); WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self)); gcry_error_t error = gcry_cipher_setctr(priv->handle, ctr, CLEARKEY_SIZE); if (error) { GST_ERROR_OBJECT(self, "gcry_cipher_setctr failed: %s", gpg_strerror(error)); return false; } GstMapInfo map; gboolean bufferMapped = gst_buffer_map(buffer, &map, static_cast<GstMapFlags>(GST_MAP_READWRITE)); if (!bufferMapped) { GST_ERROR_OBJECT(self, "Failed to map buffer"); return false; } GstMapInfo subSamplesMap; gboolean subsamplesBufferMapped = gst_buffer_map(subSamplesBuffer, &subSamplesMap, GST_MAP_READ); if (!subsamplesBufferMapped) { GST_ERROR_OBJECT(self, "Failed to map subsample buffer"); gst_buffer_unmap(buffer, &map); return false; } GstByteReader* reader = gst_byte_reader_new(subSamplesMap.data, subSamplesMap.size); unsigned position = 0; unsigned sampleIndex = 0; GST_DEBUG_OBJECT(self, "position: %d, size: %zu", 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)) { GST_DEBUG_OBJECT(self, "unsupported"); gst_byte_reader_free(reader); gst_buffer_unmap(buffer, &map); gst_buffer_unmap(subSamplesBuffer, &subSamplesMap); return false; } sampleIndex++; } else { nBytesClear = 0; nBytesEncrypted = map.size - position; } GST_TRACE_OBJECT(self, "%d bytes clear (todo=%zu)", nBytesClear, map.size - position); position += nBytesClear; if (nBytesEncrypted) { GST_TRACE_OBJECT(self, "%d bytes encrypted (todo=%zu)", nBytesEncrypted, map.size - position); error = gcry_cipher_decrypt(priv->handle, map.data + position, nBytesEncrypted, 0, 0); if (error) { GST_ERROR_OBJECT(self, "decryption failed: %s", gpg_strerror(error)); gst_byte_reader_free(reader); gst_buffer_unmap(buffer, &map); gst_buffer_unmap(subSamplesBuffer, &subSamplesMap); return false; } position += nBytesEncrypted; } } gst_byte_reader_free(reader); gst_buffer_unmap(buffer, &map); gst_buffer_unmap(subSamplesBuffer, &subSamplesMap); return true; }
/* Ideas from gstjpegparse.c */ GstTagList * gst_droidcamsrc_exif_tags_from_jpeg_data (void *data, size_t size) { GstByteReader reader; guint16 len = 0; const gchar *id; const guint8 *exif = NULL; GstBuffer *buff; GstTagList *tags = NULL; void *app1 = memmem (data, size, marker, 2); if (!app1) { GST_ERROR ("No tags found"); goto out; } size -= (app1 - data); gst_byte_reader_init (&reader, app1, size); if (!gst_byte_reader_skip (&reader, 2)) { GST_ERROR ("Not enough jpeg data for tags"); goto out; } if (!gst_byte_reader_get_uint16_be (&reader, &len)) { GST_ERROR ("Failed to get APP1 size"); goto out; } len -= 2; /* for the marker itself */ if (!gst_byte_reader_peek_string_utf8 (&reader, &id)) { goto out; } if (!strncmp (id, "Exif", 4)) { /* id + NUL + padding */ if (!gst_byte_reader_skip (&reader, 6)) { goto out; } len -= 6; if (!gst_byte_reader_get_data (&reader, len, &exif)) { goto out; } buff = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, (gpointer) exif, len, 0, len, NULL, NULL); tags = gst_tag_list_from_exif_buffer_with_tiff_header (buff); gst_buffer_unref (buff); return tags; } out: return NULL; }
static inline gboolean gst_jpeg_parse_sof (GstJpegParse * parse, GstByteReader * reader) { guint8 numcomps = 0; /* Number of components in image (1 for gray, 3 for YUV, etc.) */ guint8 precision; /* precision (in bits) for the samples */ guint8 compId[3]; /* unique value identifying each component */ guint8 qtId[3]; /* quantization table ID to use for this comp */ guint8 blockWidth[3]; /* Array[numComponents] giving the number of blocks (horiz) in this component */ guint8 blockHeight[3]; /* Same for the vertical part of this component */ guint8 i, value = 0; gint temp; /* flush length field */ if (!gst_byte_reader_skip (reader, 2)) return FALSE; /* Get sample precision */ if (!gst_byte_reader_get_uint8 (reader, &precision)) return FALSE; /* Get w and h */ if (!gst_byte_reader_get_uint16_be (reader, &parse->priv->height)) return FALSE; if (!gst_byte_reader_get_uint16_be (reader, &parse->priv->width)) return FALSE; /* Get number of components */ if (!gst_byte_reader_get_uint8 (reader, &numcomps)) return FALSE; if (numcomps > 3) return FALSE; /* Get decimation and quantization table id for each component */ for (i = 0; i < numcomps; i++) { /* Get component ID number */ if (!gst_byte_reader_get_uint8 (reader, &value)) return FALSE; compId[i] = value; /* Get decimation */ if (!gst_byte_reader_get_uint8 (reader, &value)) return FALSE; blockWidth[i] = (value & 0xf0) >> 4; blockHeight[i] = (value & 0x0f); /* Get quantization table id */ if (!gst_byte_reader_get_uint8 (reader, &value)) return FALSE; qtId[i] = value; } if (numcomps == 1) { /* gray image - no fourcc */ parse->priv->fourcc = 0; } else if (numcomps == 3) { temp = (blockWidth[0] * blockHeight[0]) / (blockWidth[1] * blockHeight[1]); if (temp == 4 && blockHeight[0] == 2) parse->priv->fourcc = GST_MAKE_FOURCC ('I', '4', '2', '0'); else if (temp == 4 && blockHeight[0] == 4) parse->priv->fourcc = GST_MAKE_FOURCC ('Y', '4', '1', 'B'); else if (temp == 2) parse->priv->fourcc = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'); else if (temp == 1) parse->priv->fourcc = GST_MAKE_FOURCC ('Y', 'V', '1', '2'); else parse->priv->fourcc = 0; } else { return FALSE; } GST_DEBUG_OBJECT (parse, "Header parsed"); return TRUE; }
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 gst_ac3_parse_handle_frame (GstBaseParse * parse, GstBaseParseFrame * frame, gint * skipsize) { GstAc3Parse *ac3parse = GST_AC3_PARSE (parse); GstBuffer *buf = frame->buffer; GstByteReader reader; gint off; gboolean lost_sync, draining, eac, more = FALSE; guint frmsiz, blocks, sid; guint rate, chans; gboolean update_rate = FALSE; gint framesize = 0; gint have_blocks = 0; GstMapInfo map; gboolean ret = FALSE; GstFlowReturn res = GST_FLOW_OK; gst_buffer_map (buf, &map, GST_MAP_READ); if (G_UNLIKELY (map.size < 8)) { *skipsize = 1; goto cleanup; } gst_byte_reader_init (&reader, map.data, map.size); off = gst_byte_reader_masked_scan_uint32 (&reader, 0xffff0000, 0x0b770000, 0, map.size); GST_LOG_OBJECT (parse, "possible sync at buffer offset %d", off); /* didn't find anything that looks like a sync word, skip */ if (off < 0) { *skipsize = map.size - 3; goto cleanup; } /* possible frame header, but not at offset 0? skip bytes before sync */ if (off > 0) { *skipsize = off; goto cleanup; } /* make sure the values in the frame header look sane */ if (!gst_ac3_parse_frame_header (ac3parse, buf, 0, &frmsiz, &rate, &chans, &blocks, &sid, &eac)) { *skipsize = off + 2; goto cleanup; } GST_LOG_OBJECT (parse, "size: %u, blocks: %u, rate: %u, chans: %u", frmsiz, blocks, rate, chans); framesize = frmsiz; if (G_UNLIKELY (g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_NONE)) gst_ac3_parse_set_alignment (ac3parse, eac); GST_LOG_OBJECT (parse, "got frame"); lost_sync = GST_BASE_PARSE_LOST_SYNC (parse); draining = GST_BASE_PARSE_DRAINING (parse); if (g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_IEC61937) { /* We need 6 audio blocks from each substream, so we keep going forwards * till we have it */ g_assert (blocks > 0); GST_LOG_OBJECT (ac3parse, "Need %d frames before pushing", 6 / blocks); if (sid != 0) { /* We need the first substream to be the one with id 0 */ GST_LOG_OBJECT (ac3parse, "Skipping till we find sid 0"); *skipsize = off + 2; goto cleanup; } framesize = 0; /* Loop till we have 6 blocks per substream */ for (have_blocks = 0; !more && have_blocks < 6; have_blocks += blocks) { /* Loop till we get one frame from each substream */ do { framesize += frmsiz; if (!gst_byte_reader_skip (&reader, frmsiz) || map.size < (framesize + 6)) { more = TRUE; break; } if (!gst_ac3_parse_frame_header (ac3parse, buf, framesize, &frmsiz, NULL, NULL, NULL, &sid, &eac)) { *skipsize = off + 2; goto cleanup; } } while (sid); } /* We're now at the next frame, so no need to skip if resyncing */ frmsiz = 0; } if (lost_sync && !draining) { guint16 word = 0; GST_DEBUG_OBJECT (ac3parse, "resyncing; checking next frame syncword"); if (more || !gst_byte_reader_skip (&reader, frmsiz) || !gst_byte_reader_get_uint16_be (&reader, &word)) { GST_DEBUG_OBJECT (ac3parse, "... but not sufficient data"); gst_base_parse_set_min_frame_size (parse, framesize + 8); *skipsize = 0; goto cleanup; } else { if (word != 0x0b77) { GST_DEBUG_OBJECT (ac3parse, "0x%x not OK", word); *skipsize = off + 2; goto cleanup; } else { /* ok, got sync now, let's assume constant frame size */ gst_base_parse_set_min_frame_size (parse, framesize); } } } /* expect to have found a frame here */ g_assert (framesize); ret = TRUE; /* arrange for metadata setup */ if (G_UNLIKELY (sid)) { /* dependent frame, no need to (ac)count for or consider further */ GST_LOG_OBJECT (parse, "sid: %d", sid); frame->flags |= GST_BASE_PARSE_FRAME_FLAG_NO_FRAME; /* TODO maybe also mark as DELTA_UNIT, * if that does not surprise baseparse elsewhere */ /* occupies same time space as previous base frame */ if (G_LIKELY (GST_BUFFER_TIMESTAMP (buf) >= GST_BUFFER_DURATION (buf))) GST_BUFFER_TIMESTAMP (buf) -= GST_BUFFER_DURATION (buf); /* only shortcut if we already arranged for caps */ if (G_LIKELY (ac3parse->sample_rate > 0)) goto cleanup; } if (G_UNLIKELY (ac3parse->sample_rate != rate || ac3parse->channels != chans || ac3parse->eac != eac)) { GstCaps *caps = gst_caps_new_simple (eac ? "audio/x-eac3" : "audio/x-ac3", "framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, chans, NULL); gst_caps_set_simple (caps, "alignment", G_TYPE_STRING, g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_IEC61937 ? "iec61937" : "frame", NULL); gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps); gst_caps_unref (caps); ac3parse->sample_rate = rate; ac3parse->channels = chans; ac3parse->eac = eac; update_rate = TRUE; } if (G_UNLIKELY (ac3parse->blocks != blocks)) { ac3parse->blocks = blocks; update_rate = TRUE; } if (G_UNLIKELY (update_rate)) gst_base_parse_set_frame_rate (parse, rate, 256 * blocks, 2, 2); cleanup: gst_buffer_unmap (buf, &map); if (ret && framesize <= map.size) { res = gst_base_parse_finish_frame (parse, frame, framesize); } return res; }
static gboolean gst_jif_mux_parse_image (GstJifMux * self, GstBuffer * buf) { GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buf); GstJifMuxMarker *m; guint8 marker = 0; guint16 size = 0; const guint8 *data = NULL; GST_LOG_OBJECT (self, "Received buffer of size: %u", GST_BUFFER_SIZE (buf)); if (!gst_byte_reader_peek_uint8 (&reader, &marker)) goto error; while (marker == 0xff) { if (!gst_byte_reader_skip (&reader, 1)) goto error; if (!gst_byte_reader_get_uint8 (&reader, &marker)) goto error; switch (marker) { case RST0: case RST1: case RST2: case RST3: case RST4: case RST5: case RST6: case RST7: case SOI: GST_DEBUG_OBJECT (self, "marker = %x", marker); m = gst_jif_mux_new_marker (marker, 0, NULL, FALSE); self->priv->markers = g_list_prepend (self->priv->markers, m); break; case EOI: GST_DEBUG_OBJECT (self, "marker = %x", marker); m = gst_jif_mux_new_marker (marker, 0, NULL, FALSE); self->priv->markers = g_list_prepend (self->priv->markers, m); goto done; break; default: if (!gst_byte_reader_get_uint16_be (&reader, &size)) goto error; if (!gst_byte_reader_get_data (&reader, size - 2, &data)) goto error; m = gst_jif_mux_new_marker (marker, size - 2, data, FALSE); self->priv->markers = g_list_prepend (self->priv->markers, m); GST_DEBUG_OBJECT (self, "marker = %2x, size = %u", marker, size); break; } if (marker == SOS) { gint eoi_pos = -1; gint i; /* search the last 5 bytes for the EOI marker */ g_assert (GST_BUFFER_SIZE (buf) >= 5); for (i = 5; i >= 2; i--) { if (GST_BUFFER_DATA (buf)[GST_BUFFER_SIZE (buf) - i] == 0xFF && GST_BUFFER_DATA (buf)[GST_BUFFER_SIZE (buf) - i + 1] == EOI) { eoi_pos = GST_BUFFER_SIZE (buf) - i; break; } } if (eoi_pos == -1) { GST_WARNING_OBJECT (self, "Couldn't find an EOI marker"); eoi_pos = GST_BUFFER_SIZE (buf); } /* remaining size except EOI is scan data */ self->priv->scan_size = eoi_pos - gst_byte_reader_get_pos (&reader); if (!gst_byte_reader_get_data (&reader, self->priv->scan_size, &self->priv->scan_data)) goto error; GST_DEBUG_OBJECT (self, "scan data, size = %u", self->priv->scan_size); } if (!gst_byte_reader_peek_uint8 (&reader, &marker)) goto error; } GST_INFO_OBJECT (self, "done parsing at 0x%x / 0x%x", gst_byte_reader_get_pos (&reader), GST_BUFFER_SIZE (buf)); done: self->priv->markers = g_list_reverse (self->priv->markers); return TRUE; error: GST_WARNING_OBJECT (self, "Error parsing image header (need more that %u bytes available)", gst_byte_reader_get_remaining (&reader)); return FALSE; }
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 gboolean gst_ac3_parse_check_valid_frame (GstBaseParse * parse, GstBaseParseFrame * frame, guint * framesize, gint * skipsize) { GstAc3Parse *ac3parse = GST_AC3_PARSE (parse); GstBuffer *buf = frame->buffer; GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buf); gint off; gboolean lost_sync, draining, eac, more = FALSE; guint frmsiz, blocks, sid; gint have_blocks = 0; if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 6)) return FALSE; off = gst_byte_reader_masked_scan_uint32 (&reader, 0xffff0000, 0x0b770000, 0, GST_BUFFER_SIZE (buf)); GST_LOG_OBJECT (parse, "possible sync at buffer offset %d", off); /* didn't find anything that looks like a sync word, skip */ if (off < 0) { *skipsize = GST_BUFFER_SIZE (buf) - 3; return FALSE; } /* possible frame header, but not at offset 0? skip bytes before sync */ if (off > 0) { *skipsize = off; return FALSE; } /* make sure the values in the frame header look sane */ if (!gst_ac3_parse_frame_header (ac3parse, buf, 0, &frmsiz, NULL, NULL, &blocks, &sid, &eac)) { *skipsize = off + 2; return FALSE; } *framesize = frmsiz; if (G_UNLIKELY (g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_NONE)) gst_ac3_parse_set_alignment (ac3parse, eac); GST_LOG_OBJECT (parse, "got frame"); lost_sync = GST_BASE_PARSE_LOST_SYNC (parse); draining = GST_BASE_PARSE_DRAINING (parse); if (g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_IEC61937) { /* We need 6 audio blocks from each substream, so we keep going forwards * till we have it */ g_assert (blocks > 0); GST_LOG_OBJECT (ac3parse, "Need %d frames before pushing", 6 / blocks); if (sid != 0) { /* We need the first substream to be the one with id 0 */ GST_LOG_OBJECT (ac3parse, "Skipping till we find sid 0"); *skipsize = off + 2; return FALSE; } *framesize = 0; /* Loop till we have 6 blocks per substream */ for (have_blocks = 0; !more && have_blocks < 6; have_blocks += blocks) { /* Loop till we get one frame from each substream */ do { *framesize += frmsiz; if (!gst_byte_reader_skip (&reader, frmsiz) || GST_BUFFER_SIZE (buf) < (*framesize + 6)) { more = TRUE; break; } if (!gst_ac3_parse_frame_header (ac3parse, buf, *framesize, &frmsiz, NULL, NULL, NULL, &sid, &eac)) { *skipsize = off + 2; return FALSE; } } while (sid); } /* We're now at the next frame, so no need to skip if resyncing */ frmsiz = 0; } if (lost_sync && !draining) { guint16 word = 0; GST_DEBUG_OBJECT (ac3parse, "resyncing; checking next frame syncword"); if (more || !gst_byte_reader_skip (&reader, frmsiz) || !gst_byte_reader_get_uint16_be (&reader, &word)) { GST_DEBUG_OBJECT (ac3parse, "... but not sufficient data"); gst_base_parse_set_min_frame_size (parse, *framesize + 6); *skipsize = 0; return FALSE; } else { if (word != 0x0b77) { GST_DEBUG_OBJECT (ac3parse, "0x%x not OK", word); *skipsize = off + 2; return FALSE; } else { /* ok, got sync now, let's assume constant frame size */ gst_base_parse_set_min_frame_size (parse, *framesize); } } } return TRUE; }