mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-02-12 10:15:54 +00:00
367 lines
13 KiB
C
367 lines
13 KiB
C
|
/*
|
||
|
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;
|
||
|
}
|
||
|
}
|