/*  libasf - An Advanced Systems Format media file parser
 *  Copyright (C) 2006-2007 Juho Vähä-Herttua
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef ASF_H
#define ASF_H


/* used int types for different platforms */
#if !defined(__MINGW_H) && defined(_WIN32)
typedef char int8_t;
typedef unsigned char uint8_t;
typedef short int16_t;
typedef unsigned short uint16_t;
typedef long int32_t;
typedef unsigned long uint32_t;

typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif

struct asf_guid_s {
	uint32_t v1;
	uint32_t v2;
	uint16_t v3;
	uint8_t  v4[8];
};
typedef struct asf_guid_s asf_guid_t;

struct asf_iostream_s {
	/* read function, returns -1 on error, 0 on EOF and read bytes
	 * otherwise */
	int32_t (*read)(void *opaque, void *buffer, int32_t size);

	/* write function, returns -1 on error, 0 on EOF and written
	 * bytes otherwise */
	int32_t (*write)(void *opaque, void *buffer, int32_t size);

	/* seek function, seeks to offset from beginning of the file,
	 * returns -1 on error, 0 on EOF */
	int64_t (*seek)(void *opaque, int64_t offset);

	/* opaque data pointer passed to each of the stream handling
	 * callbacks */
	void *opaque;
};
typedef struct asf_iostream_s asf_iostream_t;

struct asf_metadata_entry_s {
	char *key;	/* key of extended metadata entry */
	char *value;	/* value of extended metadata entry */
};
typedef struct asf_metadata_entry_s asf_metadata_entry_t;

/* all metadata entries are presented in UTF-8 character encoding */
struct asf_metadata_s {
	char *title;		/* title of the stream */
	char *artist;		/* artist of the stream */
	char *copyright;	/* copyright holder */
	char *description;	/* description of the stream */
	char *rating;		/* rating of the stream */
	uint16_t extended_count;	/* number of extended entries */
	asf_metadata_entry_t *extended; /* array of extended entries */
};
typedef struct asf_metadata_s asf_metadata_t;

struct asf_payload_s {
	uint8_t stream_number;	/* the stream number this payload belongs to */
	uint8_t key_frame;	/* a flag indicating if this payload contains a key frame  */

	uint32_t media_object_number;	/* number of media object this payload is part of */
	uint32_t media_object_offset;	/* byte offset from beginning of media object */

	uint32_t replicated_length;	/* length of some replicated data of a media object... */
	uint8_t *replicated_data;	/* the replicated data mentioned */

	uint32_t datalen;	/* length of the actual payload data */
	uint8_t *data;		/* the actual payload data to decode */

	uint32_t pts;		/* presentation time of this payload */
};
typedef struct asf_payload_s asf_payload_t;

struct asf_packet_s {
	uint8_t ec_length;	/* error correction data length */
	uint8_t *ec_data;	/* error correction data array */

	uint32_t length;		/* length of this packet, usually constant per stream */
	uint32_t padding_length;	/* length of the padding after the data in this packet */
	uint32_t send_time;		/* send time of this packet in milliseconds */
	uint16_t duration;		/* duration of this packet in milliseconds */

	uint16_t payload_count;		/* number of payloads contained in this packet */
	asf_payload_t *payloads;	/* an array of payloads in this packet */
	uint16_t payloads_size;		/* for internal library use, not to be modified by applications! */

	uint32_t payload_data_len;	/* length of the raw payload data of this packet */
	uint8_t *payload_data;		/* the raw payload data of this packet, usually not useful */

	uint8_t *data;		/* for internal library use, not to be modified by applications! */
	uint32_t data_size;	/* for internal library use, not to be modified by applications! */
};
typedef struct asf_packet_s asf_packet_t;

/* waveformatex fields specified in Microsoft documentation:
   http://msdn2.microsoft.com/en-us/library/ms713497.aspx */
struct asf_waveformatex_s {
	uint16_t wFormatTag;
	uint16_t nChannels;
	uint32_t nSamplesPerSec;
	uint32_t nAvgBytesPerSec;
	uint16_t nBlockAlign;
	uint16_t wBitsPerSample;
	uint16_t cbSize;
	uint8_t *data;
};
typedef struct asf_waveformatex_s asf_waveformatex_t;

#define ASF_BITMAPINFOHEADER_SIZE 40

/* bitmapinfoheader fields specified in Microsoft documentation:
   http://msdn2.microsoft.com/en-us/library/ms532290.aspx */
struct asf_bitmapinfoheader_s {
	uint32_t biSize;
	uint32_t biWidth;
	uint32_t biHeight;
	uint16_t biPlanes;
	uint16_t biBitCount;
	uint32_t biCompression;
	uint32_t biSizeImage;
	uint32_t biXPelsPerMeter;
	uint32_t biYPelsPerMeter;
	uint32_t biClrUsed;
	uint32_t biClrImportant;
	uint8_t *data;
};
typedef struct asf_bitmapinfoheader_s asf_bitmapinfoheader_t;

enum asf_stream_type_e {
	ASF_STREAM_TYPE_NONE     = 0x00,
	ASF_STREAM_TYPE_AUDIO    = 0x01,
	ASF_STREAM_TYPE_VIDEO    = 0x02,
	ASF_STREAM_TYPE_COMMAND  = 0x03,
	ASF_STREAM_TYPE_UNKNOWN  = 0xff
};
typedef enum asf_stream_type_e asf_stream_type_t;

#define ASF_STREAM_FLAG_NONE       0x0000
#define ASF_STREAM_FLAG_AVAILABLE  0x0001
#define ASF_STREAM_FLAG_HIDDEN     0x0002
#define ASF_STREAM_FLAG_EXTENDED   0x0004

struct asf_stream_extended_s {
	uint64_t start_time;
	uint64_t end_time;
	uint32_t data_bitrate;
	uint32_t buffer_size;
	uint32_t initial_buf_fullness;
	uint32_t data_bitrate2;
	uint32_t buffer_size2;
	uint32_t initial_buf_fullness2;
	uint32_t max_obj_size;
	uint32_t flags;
	uint16_t stream_num;
	uint16_t lang_idx;
	uint64_t avg_time_per_frame;
	uint16_t stream_name_count;
	uint16_t num_payload_ext;
};
typedef struct asf_stream_extended_s asf_stream_extended_t;

struct asf_stream_s {
	asf_stream_type_t type;	/* type of this current stream */
	uint16_t flags;         /* possible flags related to this stream */

	/* pointer to type specific data (ie. waveformatex or bitmapinfoheader)
	 * only available if ASF_STREAM_FLAG_AVAILABLE flag is set, otherwise NULL */
	void *properties;

	/* pointer to extended properties of the stream if they specified
	 * only available if ASF_STREAM_FLAG_EXTENDED flag is set, otherwise NULL */
	asf_stream_extended_t *extended;
};
typedef struct asf_stream_s asf_stream_t;

typedef struct asf_file_s asf_file_t;

enum asf_error_e {
	ASF_ERROR_INTERNAL       = -1,  /* incorrect input to API calls */
	ASF_ERROR_OUTOFMEM       = -2,  /* some malloc inside program failed */
	ASF_ERROR_EOF            = -3,  /* unexpected end of file */
	ASF_ERROR_IO             = -4,  /* error reading or writing to file */
	ASF_ERROR_INVALID_LENGTH = -5,  /* length value conflict in input data */
	ASF_ERROR_INVALID_VALUE  = -6,  /* other value conflict in input data */
	ASF_ERROR_INVALID_OBJECT = -7,  /* ASF object missing or in wrong place */
	ASF_ERROR_OBJECT_SIZE    = -8,  /* invalid ASF object size (too small) */
	ASF_ERROR_SEEKABLE       = -9,  /* file not seekable */
	ASF_ERROR_SEEK           = -10  /* file is seekable but seeking failed */
};

struct asf_object_s {
	asf_guid_t   guid;
	uint64_t     size;
	uint8_t      *data;
};
typedef struct asf_object_s asf_object_t;

/* initialize the library using file on a local filesystem */
asf_file_t *asf_open_file(const char *filename);

/* initialize the library using callbacks defined on a stream structure,
   the stream structure can be freed after calling this function */
asf_file_t *asf_open_cb(asf_iostream_t *iostream);

/* close the library handle and free all allocated memory */
void asf_close(asf_file_t *file);


/* initialize the library and read all header information of the ASF file */
int asf_init(asf_file_t *file);


/* create a packet structure for reading data packets */
asf_packet_t *asf_packet_create();

/* free the packet structure allocated earlier, need to be called only once */
void asf_packet_destroy(asf_packet_t *packet);

/* get next packet from the stream to the specified packet structure */
int asf_get_packet(asf_file_t *file, asf_packet_t *packet);

/* seek to the closest (key frame) packet specified by milliseconds position */
int64_t asf_seek_to_msec(asf_file_t *file, int64_t msec);


/* get metadata information of the ASF file handle */
asf_metadata_t *asf_header_get_metadata(asf_file_t *file);

/* free metadata structure received from the library */
void asf_metadata_destroy(asf_metadata_t *metadata);

/* free all header information from the ASF file structure
 * WARNING: after calling this function all asf_header_*
 *          functions will return NULL or failure!!! */
void asf_header_destroy(asf_file_t *file);


/* calculate how many streams are available in current ASF file */
uint8_t asf_get_stream_count(asf_file_t *file);

/* get info of a stream, the resulting pointer and its contents should NOT be freed */
asf_stream_t *asf_get_stream(asf_file_t *file, uint8_t track);


/* return non-zero if the file is broadcasted, 0 otherwise */
int asf_is_broadcast(asf_file_t *file);

/* return non-zero if the file is seekable, 0 otherwise */
int asf_is_seekable(asf_file_t *file);

/* get size of the ASF file in bytes */
uint64_t asf_get_file_size(asf_file_t *file);

/* get creation date in 100-nanosecond units since Jan 1, 1601 GMT
   this value should be ignored for broadcasts */
uint64_t asf_get_creation_date(asf_file_t *file);

/* get number of data packets available in this file
   this value should be ignored for broadcasts */
uint64_t asf_get_data_packets(asf_file_t *file);

/* get play duration of the file in 100-nanosecond units,
   this value should be ignored for broadcasts */
uint64_t asf_get_duration(asf_file_t *file);

/* maximum bitrate as bits per second in the entire file */
uint32_t asf_get_max_bitrate(asf_file_t *file);

#endif
