forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
366 lines
13 KiB
366 lines
13 KiB
/* |
|
Copyright (c) 2012, Broadcom Europe Ltd |
|
All rights reserved. |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions are met: |
|
* Redistributions of source code must retain the above copyright |
|
notice, this list of conditions and the following disclaimer. |
|
* Redistributions in binary form must reproduce the above copyright |
|
notice, this list of conditions and the following disclaimer in the |
|
documentation and/or other materials provided with the distribution. |
|
* Neither the name of the copyright holder nor the |
|
names of its contributors may be used to endorse or promote products |
|
derived from this software without specific prior written permission. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
#include "containers/containers.h" |
|
#include "containers/core/containers_common.h" |
|
#include "containers/core/containers_utils.h" |
|
|
|
/****************************************************************************** |
|
Defines. |
|
******************************************************************************/ |
|
#define BITMAPINFOHEADER_SIZE_MAX 40 |
|
#define MAX_EXTENSION_SIZE 4 |
|
|
|
#define VC_CONTAINER_ES_FORMAT_MAGIC ((uint32_t)VC_FOURCC('m','a','g','f')) |
|
#define EXTRADATA_SIZE_DEFAULT 32 |
|
#define EXTRADATA_SIZE_MAX (10*1024) |
|
|
|
/*****************************************************************************/ |
|
typedef struct VC_CONTAINER_ES_FORMAT_PRIVATE_T |
|
{ |
|
VC_CONTAINER_ES_FORMAT_T format; |
|
VC_CONTAINER_ES_SPECIFIC_FORMAT_T type; |
|
|
|
uint32_t magic; |
|
|
|
unsigned int extradata_size; |
|
uint8_t *extradata; |
|
|
|
uint8_t buffer[EXTRADATA_SIZE_DEFAULT]; |
|
|
|
} VC_CONTAINER_ES_FORMAT_PRIVATE_T; |
|
|
|
/*****************************************************************************/ |
|
VC_CONTAINER_ES_FORMAT_T *vc_container_format_create(unsigned int extradata_size) |
|
{ |
|
VC_CONTAINER_ES_FORMAT_PRIVATE_T *private; |
|
VC_CONTAINER_STATUS_T status; |
|
|
|
private = malloc(sizeof(*private)); |
|
if(!private) return 0; |
|
memset(private, 0, sizeof(*private)); |
|
|
|
private->magic = VC_CONTAINER_ES_FORMAT_MAGIC; |
|
private->format.type = (void *)&private->type; |
|
private->extradata_size = EXTRADATA_SIZE_DEFAULT; |
|
|
|
status = vc_container_format_extradata_alloc(&private->format, extradata_size); |
|
if(status != VC_CONTAINER_SUCCESS) |
|
{ |
|
free(private); |
|
return NULL; |
|
} |
|
|
|
return &private->format; |
|
} |
|
|
|
/*****************************************************************************/ |
|
void vc_container_format_delete(VC_CONTAINER_ES_FORMAT_T *format) |
|
{ |
|
VC_CONTAINER_ES_FORMAT_PRIVATE_T *private = (VC_CONTAINER_ES_FORMAT_PRIVATE_T *)format; |
|
vc_container_assert(private->magic == VC_CONTAINER_ES_FORMAT_MAGIC); |
|
if(private->extradata) free(private->extradata); |
|
free(private); |
|
} |
|
|
|
/*****************************************************************************/ |
|
VC_CONTAINER_STATUS_T vc_container_format_extradata_alloc( |
|
VC_CONTAINER_ES_FORMAT_T *format, unsigned int size) |
|
{ |
|
VC_CONTAINER_ES_FORMAT_PRIVATE_T *private = (VC_CONTAINER_ES_FORMAT_PRIVATE_T *)format; |
|
vc_container_assert(private->magic == VC_CONTAINER_ES_FORMAT_MAGIC); |
|
|
|
/* Sanity check the size requested */ |
|
if(size > EXTRADATA_SIZE_MAX) |
|
return VC_CONTAINER_ERROR_CORRUPTED; |
|
|
|
/* Allocate memory if needed */ |
|
if(private->extradata_size < size) |
|
{ |
|
if(private->extradata) free(private->extradata); |
|
private->extradata = malloc(size); |
|
if(!private->extradata) |
|
return VC_CONTAINER_ERROR_OUT_OF_MEMORY; |
|
private->extradata_size = size; |
|
} |
|
|
|
/* Set the fields in the actual format structure */ |
|
if(private->extradata) private->format.extradata = private->extradata; |
|
else private->format.extradata = private->buffer; |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/*****************************************************************************/ |
|
VC_CONTAINER_STATUS_T vc_container_format_copy( VC_CONTAINER_ES_FORMAT_T *p_out, |
|
VC_CONTAINER_ES_FORMAT_T *p_in, |
|
unsigned int extra_buffer_size) |
|
{ |
|
void *type = p_out->type; |
|
uint8_t *extradata = p_out->extradata; |
|
|
|
/* Check we have a sufficient buffer to copy the extra data */ |
|
if(p_in->extradata_size > extra_buffer_size || |
|
(p_in->extradata_size && !p_out->extradata)) |
|
return VC_CONTAINER_ERROR_BUFFER_TOO_SMALL; |
|
|
|
*p_out->type = *p_in->type; |
|
*p_out = *p_in; |
|
p_out->type = type; |
|
p_out->extradata = extradata; |
|
if(p_in->extradata_size) |
|
memcpy(p_out->extradata, p_in->extradata, p_in->extradata_size); |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/*****************************************************************************/ |
|
int utf8_from_charset(const char *charset, char *out, unsigned int out_size, |
|
const void *in, unsigned int in_size) |
|
{ |
|
unsigned int i; |
|
const uint16_t *in16 = (const uint16_t *)in; |
|
const uint8_t *in8 = (const uint8_t *)in; |
|
|
|
if(out_size < 1) return 1; |
|
if(!strcmp(charset, "UTF16-LE")) goto utf16le; |
|
if(!strcmp(charset, "UTF8")) goto utf8; |
|
else return 1; |
|
|
|
utf16le: |
|
for(i = 0; i < in_size / 2 && in16[i] && i < out_size - 1; i++) |
|
{ |
|
out[i] = in16[i]; |
|
} |
|
out[i] = 0; |
|
return 0; |
|
|
|
utf8: |
|
for(i = 0; i < in_size && in8[i] && i < out_size - 1; i++) |
|
{ |
|
out[i] = in8[i]; |
|
} |
|
out[i] = 0; |
|
return 0; |
|
} |
|
|
|
/*****************************************************************************/ |
|
unsigned int vc_container_es_format_to_waveformatex(VC_CONTAINER_ES_FORMAT_T *format, |
|
uint8_t *buffer, unsigned int buffer_size) |
|
{ |
|
uint16_t waveformat = codec_to_waveformat(format->codec); |
|
|
|
if(format->es_type != VC_CONTAINER_ES_TYPE_AUDIO || |
|
waveformat == WAVE_FORMAT_UNKNOWN) return 0; |
|
|
|
if(!buffer) return format->extradata_size + 18; |
|
|
|
if(buffer_size < format->extradata_size + 18) return 0; |
|
|
|
/* Build a waveformatex header */ |
|
buffer[0] = waveformat; |
|
buffer[1] = waveformat >> 8; |
|
buffer[2] = format->type->audio.channels; |
|
buffer[3] = 0; |
|
buffer[4] = (format->type->audio.sample_rate >> 0) & 0xFF; |
|
buffer[5] = (format->type->audio.sample_rate >> 8) & 0xFF; |
|
buffer[6] = (format->type->audio.sample_rate >> 16) & 0xFF; |
|
buffer[7] = (format->type->audio.sample_rate >> 24) & 0xFF; |
|
buffer[8] = (format->bitrate >> 3) & 0xFF; |
|
buffer[9] = (format->bitrate >> 11) & 0xFF; |
|
buffer[10] = (format->bitrate >> 19) & 0xFF; |
|
buffer[11] = (format->bitrate >> 27) & 0xFF; |
|
buffer[12] = (format->type->audio.block_align >> 0) & 0xFF; |
|
buffer[13] = (format->type->audio.block_align >> 8) & 0xFF; |
|
buffer[14] = (format->type->audio.bits_per_sample >> 0) & 0xFF; |
|
buffer[15] = (format->type->audio.bits_per_sample >> 8) & 0xFF; |
|
buffer[16] = (format->extradata_size >> 0) & 0xFF; |
|
buffer[17] = (format->extradata_size >> 8) & 0xFF; |
|
memcpy(buffer+18, format->extradata, format->extradata_size); |
|
return format->extradata_size + 18; |
|
} |
|
|
|
/*****************************************************************************/ |
|
VC_CONTAINER_STATUS_T vc_container_waveformatex_to_es_format(uint8_t *p, |
|
unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, |
|
VC_CONTAINER_ES_FORMAT_T *format) |
|
{ |
|
VC_CONTAINER_FOURCC_T fourcc; |
|
uint32_t waveformat_id; |
|
|
|
if(!p || buffer_size < 16) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; |
|
waveformat_id = (p[1] << 8) | p[0]; |
|
fourcc = waveformat_to_codec(waveformat_id); |
|
|
|
/* Read the waveformatex header */ |
|
if(extra_offset) *extra_offset = 16; |
|
if(extra_size) *extra_size = 0; |
|
format->type->audio.channels = p[2]; |
|
format->type->audio.sample_rate = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; |
|
format->bitrate = ((p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]) * 8; |
|
format->type->audio.block_align = (p[13] << 8) | p[12]; |
|
format->type->audio.bits_per_sample = (p[15] << 8) | p[14]; |
|
|
|
if(waveformat_id == WAVE_FORMAT_PCM && format->type->audio.bits_per_sample == 8) |
|
fourcc = VC_CONTAINER_CODEC_PCM_UNSIGNED_LE; |
|
|
|
if(buffer_size >= 18) |
|
{ |
|
if(extra_size) |
|
{ |
|
*extra_size = (p[17] << 8) | p[16]; |
|
if(*extra_size + 18 > buffer_size) *extra_size = buffer_size - 18; |
|
} |
|
if(extra_offset) *extra_offset = 18; |
|
} |
|
|
|
/* Skip the MPEGLAYER3WAVEFORMAT structure */ |
|
if(waveformat_id == WAVE_FORMAT_MPEGLAYER3 && extra_size) |
|
{ |
|
if(extra_offset) *extra_offset += *extra_size; |
|
*extra_size = 0; |
|
} |
|
|
|
format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; |
|
format->codec = fourcc; |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/*****************************************************************************/ |
|
unsigned int vc_container_es_format_to_bitmapinfoheader(VC_CONTAINER_ES_FORMAT_T *format, |
|
uint8_t *buffer, unsigned int buffer_size) |
|
{ |
|
uint32_t fourcc = codec_to_vfw_fourcc(format->codec); |
|
uint32_t size = BITMAPINFOHEADER_SIZE_MAX + format->extradata_size; |
|
|
|
if(format->es_type != VC_CONTAINER_ES_TYPE_VIDEO || |
|
fourcc == VC_CONTAINER_CODEC_UNKNOWN) return 0; |
|
|
|
if(!buffer) return size; |
|
if(buffer_size < size) return 0; |
|
|
|
/* Build a bitmapinfoheader header */ |
|
memset(buffer, 0, BITMAPINFOHEADER_SIZE_MAX); |
|
buffer[0] = (size >> 0) & 0xFF; |
|
buffer[1] = (size >> 8) & 0xFF; |
|
buffer[2] = (size >> 16) & 0xFF; |
|
buffer[3] = (size >> 24) & 0xFF; |
|
buffer[4] = (format->type->video.width >> 0) & 0xFF; |
|
buffer[5] = (format->type->video.width >> 8) & 0xFF; |
|
buffer[6] = (format->type->video.width >> 16) & 0xFF; |
|
buffer[7] = (format->type->video.width >> 24) & 0xFF; |
|
buffer[8] = (format->type->video.height >> 0) & 0xFF; |
|
buffer[9] = (format->type->video.height >> 8) & 0xFF; |
|
buffer[10] = (format->type->video.height >> 16) & 0xFF; |
|
buffer[11] = (format->type->video.height >> 24) & 0xFF; |
|
memcpy(buffer + 16, &fourcc, 4); |
|
memcpy(buffer + BITMAPINFOHEADER_SIZE_MAX, format->extradata, format->extradata_size); |
|
return size; |
|
} |
|
|
|
/*****************************************************************************/ |
|
VC_CONTAINER_STATUS_T vc_container_bitmapinfoheader_to_es_format(uint8_t *p, |
|
unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, |
|
VC_CONTAINER_ES_FORMAT_T *format) |
|
{ |
|
VC_CONTAINER_FOURCC_T fourcc; |
|
|
|
if(!p || buffer_size < BITMAPINFOHEADER_SIZE_MAX) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; |
|
/* size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; */ |
|
format->type->video.width = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; |
|
format->type->video.height = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]; |
|
memcpy(&fourcc, p + 16, 4); |
|
|
|
format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; |
|
format->codec = vfw_fourcc_to_codec(fourcc); |
|
|
|
/* If no mapping is found from vfw, try a more generic one */ |
|
if (format->codec == fourcc && (fourcc = fourcc_to_codec(fourcc)) != VC_CONTAINER_CODEC_UNKNOWN) |
|
format->codec = fourcc; |
|
|
|
if(extra_offset) *extra_offset = BITMAPINFOHEADER_SIZE_MAX; |
|
if(extra_size) |
|
{ |
|
if (buffer_size > BITMAPINFOHEADER_SIZE_MAX) |
|
*extra_size = buffer_size - BITMAPINFOHEADER_SIZE_MAX; |
|
else |
|
*extra_size = 0; |
|
} |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/*****************************************************************************/ |
|
static struct { |
|
VC_CONTAINER_METADATA_KEY_T key; |
|
const char *name; |
|
} meta_key_conv[] = |
|
{ {VC_CONTAINER_METADATA_KEY_TITLE, "title"}, |
|
{VC_CONTAINER_METADATA_KEY_ARTIST, "artist"}, |
|
{VC_CONTAINER_METADATA_KEY_ALBUM, "album"}, |
|
{VC_CONTAINER_METADATA_KEY_DESCRIPTION, "description"}, |
|
{VC_CONTAINER_METADATA_KEY_YEAR, "year"}, |
|
{VC_CONTAINER_METADATA_KEY_GENRE, "genre"}, |
|
{VC_CONTAINER_METADATA_KEY_TRACK, "track"}, |
|
{VC_CONTAINER_METADATA_KEY_LYRICS, "lyrics"}, |
|
{VC_CONTAINER_METADATA_KEY_UNKNOWN, 0} }; |
|
|
|
/*****************************************************************************/ |
|
const char *vc_container_metadata_id_to_string(VC_CONTAINER_METADATA_KEY_T key) |
|
{ |
|
int i; |
|
for(i = 0; meta_key_conv[i].key != VC_CONTAINER_METADATA_KEY_UNKNOWN; i++ ) |
|
if(meta_key_conv[i].key == key) break; |
|
return meta_key_conv[i].name; |
|
} |
|
|
|
/*****************************************************************************/ |
|
int64_t vc_container_maths_gcd(int64_t a, int64_t b) |
|
{ |
|
while(b != 0) |
|
{ |
|
int64_t t = b; |
|
b = a % b; |
|
a = t; |
|
} |
|
return a; |
|
} |
|
|
|
/*****************************************************************************/ |
|
void vc_container_maths_rational_simplify(uint32_t *num, uint32_t *den) |
|
{ |
|
int64_t div = vc_container_maths_gcd((int64_t)*num, (int64_t)*den); |
|
if(div) |
|
{ |
|
*num /= div; |
|
*den /= div; |
|
} |
|
}
|
|
|