blob: 516659e272047aa99fc27759bcd49b9ee15c2e15 [file] [log] [blame]
/*
* Copyright (c) 2013-2015, Freescale Semiconductor, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "aiurcontent.h"
GST_DEBUG_CATEGORY_EXTERN (aiurdemux_debug);
#define GST_CAT_DEFAULT aiurdemux_debug
typedef struct{
const gchar *protocol;
guint32 flags;
}AiurdemuxProtocalEntry;
static AiurdemuxProtocalEntry aiurdemux_protocol_table[] = {
{"file",0},
{"http",FILE_FLAG_READ_IN_SEQUENCE},
{"rtsp",FILE_FLAG_READ_IN_SEQUENCE},
{"udp",FILE_FLAG_READ_IN_SEQUENCE},
{"rtp",FILE_FLAG_READ_IN_SEQUENCE},
{"mtp-track",FILE_FLAG_READ_IN_SEQUENCE},
{"mms",FILE_FLAG_NON_SEEKABLE|FILE_FLAG_READ_IN_SEQUENCE},
{"rtmp",FILE_FLAG_NON_SEEKABLE|FILE_FLAG_READ_IN_SEQUENCE},
{NULL,FILE_FLAG_NON_SEEKABLE|FILE_FLAG_READ_IN_SEQUENCE},
};
typedef struct
{
gint64 length;
gint64 offset;
gboolean seekable;
void *cache;
} AiurDemuxContentDesc;
struct _AiurContent
{
gchar *uri;
gint64 length;
gboolean seekable;
gboolean random_access;
guint32 flags;
gint64 duration;
gchar * index_file;
GstPad *sinkpad;
GstAiurStreamCache *stream_cache;
};
/* memory callbacks */
void *
aiurcontent_callback_malloc (uint32 size)
{
void *memory;
if (size == 0) {
size = 4;
GST_WARNING ("Try mallo 0 size buffer, maybe a core parser bug!");
}
memory = g_try_malloc (size);
return memory;
}
void *
aiurcontent_callback_calloc (uint32 numElements, uint32 size)
{
void *memory;
if (size == 0) {
size = 4;
GST_WARNING ("Try callo 0 size buffer, maybe a core parser bug!");
}
memory = g_try_malloc (numElements * size);
if (memory) {
memset (memory, 0, numElements * size);
}
return memory;
}
void *
aiurcontent_callback_realloc (void *ptr, uint32 size)
{
void *memory;
if (size == 0) {
size = 4;
GST_WARNING ("Try realloc 0 size buffer, maybe a core parser bug!");
}
memory = g_try_realloc (ptr, size);
return memory;
}
void
aiurcontent_callback_free (void *ptr)
{
if (ptr) {
g_free (ptr);
} else {
GST_WARNING ("Try free NULL buffer, maybe a core parser bug!");
}
}
/* pull mode stream callbacks */
FslFileHandle
aiurcontent_callback_open_pull (const uint8 * fileName, const uint8 * mode,
void *context)
{
AiurContent *pContent = (AiurContent *) context;
AiurDemuxContentDesc *content;
content = g_new0 (AiurDemuxContentDesc, 1);
if (content) {
content->length = pContent->length;
content->seekable = pContent->seekable;
content->offset = 0;
content->cache = NULL;
}
return content;
}
int32
aiurcontent_callback_close_pull (FslFileHandle handle, void *context)
{
if (handle) {
g_free (handle);
}
return 0;
}
uint32
aiurcontent_callback_read_pull (FslFileHandle handle, void *buffer, uint32 size,
void *context)
{
GstBuffer *gstbuffer = NULL;
AiurDemuxContentDesc *content = (AiurDemuxContentDesc *) handle;
AiurContent *pContent = (AiurContent *) context;
GstFlowReturn ret;
gint32 read_size = 0;
GstMapInfo map;
if ((content == NULL) || (size == 0))
return 0;
ret = gst_pad_pull_range (pContent->sinkpad, content->offset,
size, &gstbuffer);
if (ret == GST_FLOW_OK) {
gst_buffer_map (gstbuffer, &map, GST_MAP_READ);
read_size = map.size;
content->offset += read_size;
memcpy (buffer, map.data, read_size);
gst_buffer_unmap (gstbuffer, &map);
gst_buffer_unref (gstbuffer);
} else {
GST_WARNING ("gst_pad_pull_range failed ret = %d", ret);
}
return read_size;
}
int32
aiurcontent_callback_seek_pull (FslFileHandle handle, int64 offset,
int32 whence, void *context)
{
AiurDemuxContentDesc *content;
int64 newoffset;
int32 ret = 0;
if (handle == NULL)
return -1;
content = (AiurDemuxContentDesc *) handle;
newoffset = content->offset;
switch (whence) {
case SEEK_SET:
newoffset = offset;
break;
case SEEK_CUR:
newoffset += offset;
break;
case SEEK_END:
newoffset = content->length + offset;
break;
default:
return -1;
break;
}
if ((newoffset < 0) || ((content->length > 0)
&& (newoffset > content->length))) {
GST_ERROR ("Failed to seek. Target (%lld) exceeds the file range (%lld)",
newoffset, content->length);
ret = -1;
} else {
content->offset = newoffset;
}
return ret;
}
int64
aiurcontent_callback_tell_pull (FslFileHandle handle, void *context)
{
AiurDemuxContentDesc *content = (AiurDemuxContentDesc *) handle;
if (content == NULL)
return 0;
return content->offset;
}
int64
aiurcontent_callback_availiable_bytes_pull (FslFileHandle handle,
int64 bytesRequested, void *context)
{
return bytesRequested;
}
int64
aiurcontent_callback_size_pull (FslFileHandle handle, void *context)
{
AiurDemuxContentDesc *content = (AiurDemuxContentDesc *) handle;
if (content == NULL)
return 0;
return content->length;
}
uint32
aiurcontent_callback_getflag_pull (FslFileHandle handle, void *context)
{
AiurContent *pContent = (AiurContent *) context;
if(!pContent){
return 0;
}
return pContent->flags;
}
/* push mode stream callbacks */
FslFileHandle
aiurcontent_callback_open_push (const uint8 * fileName, const uint8 * mode,
void *context)
{
AiurContent *pContent = (AiurContent *) context;
AiurDemuxContentDesc *content;
content = g_new0 (AiurDemuxContentDesc, 1);
if (content) {
content->cache =
gst_mini_object_ref (GST_MINI_OBJECT_CAST (pContent->stream_cache));
content->length = pContent->length;
content->seekable = pContent->seekable;
content->offset = 0;
}
return content;
}
int32
aiurcontent_callback_close_push (FslFileHandle handle, void *context)
{
if (handle) {
AiurDemuxContentDesc *content = (AiurDemuxContentDesc *) handle;
if (content->cache) {
gst_mini_object_unref (GST_MINI_OBJECT_CAST (content->cache));
content->cache = NULL;
}
g_free (handle);
}
return 0;
}
uint32
aiurcontent_callback_read_push (FslFileHandle handle, void *buffer, uint32 size,
void *context)
{
uint32 ret = 0;
gint64 readsize = 0;
if (handle) {
AiurDemuxContentDesc *content = (AiurDemuxContentDesc *) handle;
if (size == 0)
return ret;
if (content->offset != gst_aiur_stream_cache_get_position (content->cache)) {
gst_aiur_stream_cache_seek (content->cache, content->offset);
}
readsize =
gst_aiur_stream_cache_read (content->cache, (guint64) size, buffer);
if (readsize >= 0) {
ret = readsize;
content->offset += readsize;
}
}
return ret;
}
int32
aiurcontent_callback_seek_push (FslFileHandle handle, int64 offset,
int32 whence, void *context)
{
GST_DEBUG ("seek to %lld", offset);
if (handle) {
AiurDemuxContentDesc *content = (AiurDemuxContentDesc *) handle;
int64 newoffset = content->offset;
switch (whence) {
case SEEK_SET:
newoffset = offset;
break;
case SEEK_CUR:
newoffset += offset;
break;
case SEEK_END:
newoffset = content->length + offset;
break;
default:
return -1;
break;
}
if ((newoffset < 0) || ((content->length > 0)
&& (newoffset > content->length))) {
GST_ERROR ("Failed to seek. Target (%lld) exceeds the file range (%lld)",
newoffset, content->length);
return -1;
} else {
content->offset = newoffset;
}
}
return 0;
}
int64
aiurcontent_callback_size_push (FslFileHandle handle, void *context)
{
if (handle) {
AiurDemuxContentDesc *content = (AiurDemuxContentDesc *) handle;
return content->length;
}
return -1;
}
int64
aiurcontent_callback_tell_push (FslFileHandle handle, void *context)
{
if (handle) {
AiurDemuxContentDesc *content = (AiurDemuxContentDesc *) handle;
return content->offset;
}
return -1;
}
int64
aiurcontent_callback_availiable_bytes_push (FslFileHandle handle,
int64 bytesRequested, void *context)
{
return bytesRequested;
}
uint32
aiurcontent_callback_getflag_push (FslFileHandle handle, void *context)
{
AiurContent *pContent = (AiurContent *) context;
if(!pContent){
return 0;
}
return pContent->flags;
}
/* buffer callbacks */
uint8 *
aiurcontent_callback_request_buffer (uint32 stream_idx, uint32 * size,
void **bufContext, void *parserContext)
{
uint8 *buffer = NULL;
GstBuffer *gstbuf = NULL;
GstMapInfo map;
AiurContent *pContent = (AiurContent *) parserContext;
//AiurDemuxStream *stream = aiurdemux_trackidx_to_stream (demux, stream_idx);
if((*size) >= 100000000){
GST_ERROR("request buffer failed!!!");
}
if (*size == 0) {
GST_WARNING
("Stream[%02d] request zero size buffer, maybe a core parser bug!",
stream_idx);
*size = AIURDEMUX_MIN_OUTPUT_BUFFER_SIZE;
}
if (TRUE) {
gstbuf = gst_buffer_new_and_alloc (*size);
*bufContext = gstbuf;
} else {
GST_ERROR ("Unknown stream number %d.", stream_idx);
}
if (gstbuf) {
gst_buffer_map(gstbuf, &map, GST_MAP_READ);
buffer = map.data;
gst_buffer_unmap (gstbuf, &map);
*bufContext = gstbuf;
}
return buffer;
}
void
aiurcontent_callback_release_buffer (uint32 stream_idx, uint8 * pBuffer,
void *bufContext, void *parserContext)
{
GstBuffer *gstbuf = (GstBuffer *) bufContext;
if (gstbuf) {
gst_buffer_unref (gstbuf);
}
}
static gchar* aiurcontent_generate_idx_file(AiurContent * pContent,char * prefix)
{
gchar *location = NULL, *buf = NULL;
gchar * protocal = NULL;
if(!pContent || !pContent->uri)
goto bail;
location = g_strdup (pContent->uri);
protocal = gst_uri_get_protocol (location);
if (strcmp(protocal, "file")) {
goto bail;
}
buf = gst_uri_get_location (location);
if (buf) {
g_free (location);
location = buf;
while (*buf != '\0') {
if (*buf == '/') {
*buf = '.';
}
buf++;
}
buf = g_strdup_printf ("%s/%s.%s", prefix, location, "aidx");
}
bail:
if(location)
g_free(location);
if(protocal)
g_free(protocal);
return buf;
}
static void aiurcontent_query_content_info (AiurContent *pContent)
{
GstQuery *q;
GstFormat fmt;
gchar *prefix;
GstPad *pad = pContent->sinkpad;
gst_object_ref (GST_OBJECT_CAST (pad));
q = gst_query_new_uri ();
if (gst_pad_peer_query (pad, q)) {
gchar *uri;
gst_query_parse_uri (q, &uri);
if (uri) {
pContent->uri = g_uri_unescape_string (uri, NULL);
g_free (uri);
}
}
gst_query_unref (q);
q = gst_query_new_seeking (GST_FORMAT_BYTES);
if (gst_pad_peer_query (pad, q)) {
gst_query_parse_seeking (q, &fmt, &(pContent->seekable), NULL,
NULL);
}
gst_query_unref (q);
pContent->length = -1;
q = gst_query_new_duration (GST_FORMAT_BYTES);
if (gst_pad_peer_query (pad, q)) {
gst_query_parse_duration (q, &fmt, &(pContent->length));
}
gst_query_unref (q);
gst_object_unref (GST_OBJECT_CAST (pad));
prefix = (gchar *)getenv ("HOME");
if (prefix == NULL)
prefix = "";
prefix = g_strdup_printf ("%s/.aiur", prefix);
pContent->index_file = aiurcontent_generate_idx_file(pContent,prefix);
if (pContent->index_file) {
umask (0);
if (mkdir (prefix, 0777))
GST_DEBUG("can not mkdir %s ", prefix);
}
g_free (prefix);
}
static void aiurcontent_set_flag (AiurContent *pContent)
{
int i = 0;
AiurdemuxProtocalEntry * protocol = NULL;
gchar *uri = pContent->uri;
gchar *uri_protocal = NULL;
if(uri)
uri_protocal = gst_uri_get_protocol (uri);
pContent->flags = 0;
if(uri_protocal){
for(i = 0; i < sizeof(aiurdemux_protocol_table)/sizeof(AiurdemuxProtocalEntry); i++){
protocol = &aiurdemux_protocol_table[i];
if (protocol->protocol != NULL && (strcmp (uri_protocal, protocol->protocol) == 0)){
pContent->flags = protocol->flags;
break;
}
}
}else{
pContent->flags = FILE_FLAG_NON_SEEKABLE|FILE_FLAG_READ_IN_SEQUENCE;
}
if(!pContent->seekable)
pContent->flags |= FILE_FLAG_NON_SEEKABLE;
if(pContent->flags & FILE_FLAG_READ_IN_SEQUENCE){
pContent->random_access= FALSE;
}else{
pContent->random_access = TRUE;
}
if(uri_protocal){
g_free(uri_protocal);
}
}
int aiurcontent_new(AiurContent **pContent)
{
if(pContent == NULL)
return 1;
*pContent = g_new0 (AiurContent, 1);
if(*pContent){
memset(*pContent,0, sizeof(AiurContent));
}
return 0;
}
void aiurcontent_release(AiurContent *pContent)
{
if(pContent == NULL)
return;
if(pContent->uri)
g_free (pContent->uri);
if(pContent->index_file)
g_free (pContent->index_file);
if(pContent)
g_free(pContent);
}
int aiurcontent_get_pullfile_callback(AiurContent * pContent,FslFileStream *file_cbks)
{
if(!pContent || file_cbks == NULL)
return -1;
file_cbks->Open = aiurcontent_callback_open_pull;
file_cbks->Read = aiurcontent_callback_read_pull;
file_cbks->Seek = aiurcontent_callback_seek_pull;
file_cbks->Tell = aiurcontent_callback_tell_pull;
file_cbks->Size = aiurcontent_callback_size_pull;
file_cbks->Close = aiurcontent_callback_close_pull;
file_cbks->CheckAvailableBytes = aiurcontent_callback_availiable_bytes_pull;
file_cbks->GetFlag = aiurcontent_callback_getflag_pull;
return 0;
}
int aiurcontent_get_pushfile_callback(AiurContent * pContent,FslFileStream *file_cbks)
{
if(!pContent || file_cbks == NULL)
return -1;
file_cbks->Open = aiurcontent_callback_open_push;
file_cbks->Read = aiurcontent_callback_read_push;
file_cbks->Seek = aiurcontent_callback_seek_push;
file_cbks->Tell = aiurcontent_callback_tell_push;
file_cbks->Size = aiurcontent_callback_size_push;
file_cbks->Close = aiurcontent_callback_close_push;
file_cbks->CheckAvailableBytes = aiurcontent_callback_availiable_bytes_push;
file_cbks->GetFlag = aiurcontent_callback_getflag_push;
return 0;
}
int aiurcontent_get_memory_callback(AiurContent * pContent,ParserMemoryOps *mem_cbks)
{
if(!pContent || mem_cbks == NULL)
return -1;
mem_cbks->Calloc = aiurcontent_callback_calloc;
mem_cbks->Malloc = aiurcontent_callback_malloc;
mem_cbks->Free = aiurcontent_callback_free;
mem_cbks->ReAlloc = aiurcontent_callback_realloc;
return 0;
}
int aiurcontent_get_buffer_callback(AiurContent * pContent,ParserOutputBufferOps *buffer_cbks)
{
if(!pContent || buffer_cbks == NULL)
return -1;
buffer_cbks->RequestBuffer = aiurcontent_callback_request_buffer;
buffer_cbks->ReleaseBuffer = aiurcontent_callback_release_buffer;
return 0;
}
int aiurcontent_init(AiurContent * pContent,GstPad *sinkpad,GstAiurStreamCache *stream_cache)
{
if(!pContent || sinkpad == NULL || stream_cache == NULL)
return -1;
pContent->sinkpad = sinkpad;
pContent->stream_cache = stream_cache;
aiurcontent_query_content_info (pContent);
aiurcontent_set_flag(pContent);
return 0;
}
gboolean aiurcontent_is_live(AiurContent * pContent)
{
gboolean isLive = FALSE;
if((pContent->random_access == FALSE) &&
(pContent->seekable == FALSE)){
isLive = TRUE;
}
return isLive;
}
gboolean aiurcontent_is_seelable(AiurContent * pContent)
{
if(!pContent)
return FALSE;
return pContent->seekable;
}
gboolean aiurcontent_is_random_access(AiurContent * pContent)
{
if(!pContent)
return FALSE;
return pContent->random_access;
}
gchar* aiurcontent_get_url(AiurContent * pContent)
{
if(!pContent)
return NULL;
if(pContent->uri)
return pContent->uri;
else
return NULL;
}
gchar* aiurcontent_get_index_file(AiurContent * pContent)
{
if(!pContent)
return NULL;
if(pContent->index_file)
return pContent->index_file;
else
return NULL;
}