diff options
Diffstat (limited to 'formats')
32 files changed, 14309 insertions, 0 deletions
diff --git a/formats/Makefile.am b/formats/Makefile.am new file mode 100755 index 0000000..2962060 --- /dev/null +++ b/formats/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS=ffmpeg + diff --git a/formats/ffmpeg/Makefile.am b/formats/ffmpeg/Makefile.am new file mode 100755 index 0000000..8457f15 --- /dev/null +++ b/formats/ffmpeg/Makefile.am @@ -0,0 +1,74 @@ +lib_LTLIBRARIES = libmmfile_formats.la + + +libmmfile_formats_la_DEPENDENCIES = $(top_builddir)/utils/libmmfile_utils.la + +noinst_HEADERS = include/mm_file_format_dummy.h \ + include/mm_file_format_aac.h \ + include/mm_file_format_amr.h \ + include/mm_file_format_imelody.h \ + include/mm_file_format_midi.h \ + include/mm_file_format_ffmpeg_mem.h \ + include/mm_file_format_ffmpeg.h \ + include/mm_file_format_mmf.h \ + include/mm_file_format_mp3.h \ + include/mm_file_format_wav.h \ + include/mm_file_format_private.h + +libmmfile_formats_la_SOURCES = mm_file_formats.c \ + mm_file_format_dummy.c \ + mm_file_format_ffmpeg.c \ + mm_file_format_ffmpeg_mem.c \ + mm_file_format_mp3.c \ + mm_file_format_aac.c \ + mm_file_format_mmf.c \ + mm_file_format_amr.c \ + mm_file_format_midi.c \ + mm_file_format_imelody.c \ + mm_file_format_wav.c \ + mm_file_format_frame.c + +libmmfile_formats_la_CFLAGS = -I$(srcdir)/include \ + $(MMCOMMON_CFLAGS) \ + -I$(srcdir)/../../include \ + -I$(srcdir)/../../utils/include \ + -D_LARGEFILE64_SOURCE \ + -D_FILE_OFFSET_BITS=64 \ + $(AVUTIL_CFLAGS) \ + $(AVCODEC_CFLAGS) \ + $(SWSCALE_CFLAGS) \ + $(AVFORMAT_CFLAGS) + +if USE_TESTMODE +libmmfile_formats_la_CFLAGS += -D__MMFILE_TEST_MODE__ +endif + +if USE_DUMP +libmmfile_formats_la_CFLAGS += -DMMFILE_FORMAT_DEBUG_DUMP +endif + +if USE_DRM +libmmfile_formats_la_CFLAGS += $(DRMCLIENT_CFLAGS) -DDRM_SUPPORT +endif + +if USE_IOMMAP +libmmfile_formats_la_CFLAGS += -D__MMFILE_MMAP_MODE__ +endif + + +libmmfile_formats_la_LIBADD = $(MMCOMMON_LIBS) \ + $(AVUTIL_LIBS) \ + $(AVCODEC_LIBS) \ + $(AVFORMAT_LIBS) \ + $(SWSCALE_LIBS) \ + $(top_builddir)/utils/libmmfile_utils.la + +if USE_DRM +libmmfile_formats_la_LIBADD += $(DRMCLIENT_LIBS) +endif + +libmmfile_formats_la_CFLAGS += $(MMLOG_CFLAGS) -DMMF_LOG_OWNER=0x040 -DMMF_DEBUG_PREFIX=\"MMF-FILE-FORMAT-FFMPEG\" +libmmfile_formats_la_LIBADD += $(MMLOG_LIBS) + +libmmfile_formats_la_CFLAGS += -D__MMFILE_FFMPEG_V100__ +libmmfile_formats_la_CFLAGS += -D__MMFILE_FFMPEG_V085__ diff --git a/formats/ffmpeg/include/mm_file_format_aac.h b/formats/ffmpeg/include/mm_file_format_aac.h new file mode 100755 index 0000000..25f3328 --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_aac.h @@ -0,0 +1,90 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __MM_FILE_FORMAT_AAC_H__ +#define __MM_FILE_FORMAT_AAC_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mm_file_formats.h" + +#define MMFILE_AAC_PARSER_SUCCESS 1 +#define MMFILE_AAC_PARSER_FAIL 0 + +typedef enum _mmfile_aac_profile_type { + AAC_PROFILE_MAIN, + AAC_PROFILE_LC, + AAC_PROFILE_SSR, + AAC_PROFILE_LTP, + AAC_PROFILE_UNKNOWN +}TAacProfileType; + +typedef void* MMFileAACHandle; + +typedef struct _mmfileaacstreaminfo { + unsigned int iseekable; + long long duration; + long long fileSize; + unsigned int bitRate; + unsigned int samplingRate; + unsigned int frameRate; + unsigned int numAudioChannels; + unsigned int numTracks; + TAacProfileType profileType; +} tMMFILE_AAC_STREAM_INFO; + + +typedef struct _mmfileaactaginfo { + char *title; + char *author; + char *artist; + char *album; + char *year; + char *copyright; + char *comment; + char *genre; + char *tracknum; + char *composer; + char *classification; + char *rating; + char *recordDate; + char *conductor; + char *artworkMime; + char *artwork; + unsigned int artworkSize; +} tMMFILE_AAC_TAG_INFO; + + +int mmfile_aacparser_open (MMFileAACHandle *handle, const char *src); +int mmfile_aacparser_get_stream_info (MMFileAACHandle handle, tMMFILE_AAC_STREAM_INFO *aacinfo); +int mmfile_aacparser_get_tag_info (MMFileAACHandle handle, tMMFILE_AAC_TAG_INFO *info); +int mmfile_aacparser_get_next_frame (MMFileAACHandle handle, tMMFILE_AAC_STREAM_INFO *aacinfo); +int mmfile_aacparser_close (MMFileAACHandle handle); + +#ifdef __cplusplus +} +#endif + +#endif /*__MM_FILE_FORMAT_AAC_H__*/ + diff --git a/formats/ffmpeg/include/mm_file_format_amr.h b/formats/ffmpeg/include/mm_file_format_amr.h new file mode 100755 index 0000000..4df6dcd --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_amr.h @@ -0,0 +1,56 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __MM_FILE_FORMAT_AMR_H__ +#define __MM_FILE_FORMAT_AMR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mm_file_formats.h" + +#define MMFILE_AMR_PARSER_SUCCESS 1 +#define MMFILE_AMR_PARSER_FAIL 0 + +typedef void* MMFileAMRHandle; + +typedef struct _mmfileamrstreaminfo { + long long duration; + long long fileSize; + unsigned int bitRate; + unsigned int samplingRate; + unsigned int frameRate; + unsigned int numAudioChannels; + unsigned int numTracks; +} tMMFILE_AMR_STREAM_INFO; + + +int mmfile_amrparser_open (MMFileAMRHandle *handle, const char *src); +int mmfile_amrparser_get_stream_info (MMFileAMRHandle handle, tMMFILE_AMR_STREAM_INFO *amrinfo); +int mmfile_amrparser_close (MMFileAMRHandle handle); + +#ifdef __cplusplus +} +#endif + +#endif /*__MM_FILE_FORMAT_AMR_H__*/ + diff --git a/formats/ffmpeg/include/mm_file_format_audio.h b/formats/ffmpeg/include/mm_file_format_audio.h new file mode 100755 index 0000000..0711395 --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_audio.h @@ -0,0 +1,215 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _MM_FILE_FORMAT_AUDIO_H_ +#define _MM_FILE_FORMAT_AUDIO_H_ + +#define MPEG_1_SIZE_LAYER_1 384 +#define MPEG_1_SIZE_LAYER_2_3 1152 + +#define MPEG_2_SIZE_LAYER_1 (MPEG_1_SIZE_LAYER_1 / 2) +#define MPEG_2_SIZE_LAYER_2_3 (MPEG_1_SIZE_LAYER_2_3 / 2) + +/* MP3 */ +#define MP3TAGINFO_SIZE 128 // file end 128 byte +#define FRAMES_FLAG 0x0001 +#define BYTES_FLAG 0x0002 +#define TOC_FLAG 0x0004 +#define VBR_SCALE_FLAG 0x0008 + +#define FRAMES_AND_BYTES (FRAMES_FLAG | BYTES_FLAG) +#define VALID_SYNC(x) (((unsigned char *)(x))[0] == 0xFF && (((unsigned char *)(x))[1] & 0xE0) == 0xE0) + +#define AV_MP3HDR_VERSION_OFS 1 +#define AV_MP3HDR_VERSION_M 0x18 +#define AV_MP3HDR_VERSION_SHIFT 3 + +#define AV_MP3HDR_LAYER_OFS 1 +#define AV_MP3HDR_LAYER_M 0x06 +#define AV_MP3HDR_LAYER_SHIFT 1 + +#define AV_MP3HDR_CRC_OFS 1 +#define AV_MP3HDR_CRC_M 0x01 +#define AV_MP3HDR_CRC_SHIFT 0 + +#define AV_MP3HDR_BITRATE_OFS 2 +#define AV_MP3HDR_BITRATE_M 0xF0 +#define AV_MP3HDR_BITRATE_SHIFT 4 + +#define AV_MP3HDR_SAMPLERATE_OFS 2 +#define AV_MP3HDR_SAMPLERATE_M 0x0C +#define AV_MP3HDR_SAMPLERATE_SHIFT 2 + +#define AV_MP3HDR_PADDING_OFS 2 +#define AV_MP3HDR_PADDING_M 0x02 +#define AV_MP3HDR_PADDING_SHIFT 1 + +#define AV_MP3HDR_PRIVATE_OFS 2 +#define AV_MP3HDR_PRIVATE_M 0x01 +#define AV_MP3HDR_PRIVATE_SHIFT 0 + +#define AV_MP3HDR_CHANNEL_OFS 3 +#define AV_MP3HDR_CHANNEL_M 0xC0 +#define AV_MP3HDR_CHANNEL_SHIFT 6 + +#define AV_MP3HDR_CHANNEL_EXT_OFS 3 +#define AV_MP3HDR_CHANNEL_EXT_M 0x30 +#define AV_MP3HDR_CHANNEL_EXT_SHIFT 4 + +#define AV_MP3HDR_COPYRIGHT_OFS 3 +#define AV_MP3HDR_COPYRIGHT_M 0x08 +#define AV_MP3HDR_COPYRIGHT_SHIFT 3 + +#define AV_MP3HDR_ORIGINAL_OFS 3 +#define AV_MP3HDR_ORIGINAL_M 0x06 +#define AV_MP3HDR_ORIGINAL_SHIFT 2 + +#define AV_MP3HDR_EMPHASIS_OFS 3 +#define AV_MP3HDR_EMPHASIS_M 0x03 +#define AV_MP3HDR_EMPHASIS_SHIFT 0 + +#define MASK_MPEG 0x18 // 00011000 +#define MASK_MPEG_25 0x00 // 00000000 +#define MASK_MPEG_2 0x10 // 00010000 +#define MASK_MPEG_1 0x18 // 00011000 + +#define MASK_LAYER 0x06 // 00000110 +#define MASK_LAYER_3 0x02 // 00000010 +#define MASK_LAYER_2 0x04 // 00000100 +#define MASK_LAYER_1 0x06 // 00000110 + +#define MASK_CHANNEL 0xC0 // 11000000 +#define MASK_CHANNEL_ST 0x00 // 00000000 +#define MASK_CHANNEL_JS 0x40 // 01000000 +#define MASK_CHANNEL_DC 0x80 // 10000000 +#define MASK_CHANNEL_MN 0xC0 // 11000000 + +#define MASK_SAMPLERATE 0x0C // 00001100 + +#define MASK_PADDING 0x02 // 00000010 + +#define _AV_MP3_HEADER_POSITION_MAX (50*1024) // mp3 header should be exist inside this size +#define AV_MP3_HEADER_READ_MAX 200000 // mp3 header should be exist inside this size +#define AV_WM_LOCALCODE_SIZE_MAX 2 + +/* + * Xing Header Information + */ +typedef struct{ + int hId; // from MPEG header, 0=MPEG2, 1=MPEG1 + int sampRate; // determined from MPEG header + int flags; // from Xing header data + int frames; // total bit stream frames from Xing header data + int bytes; // total bit stream bytes from Xing header data + int vbrScale; // encoded vbr scale from Xing header data + unsigned char *toc; // pointer to unsigned char toc_buffer[100] + // may be NULL if toc not desired +} AvXHeadData; + +typedef struct{ + int hId; // from MPEG header, 0=MPEG2, 1=MPEG1 + int vID; // ver. ID + int sampRate; // determined from MPEG header + float delay; // delay + int qualityIndicator; // qualityIndicator + int bytes; // total bit stream bytes from Xing header data + int frames; // total bit stream frames from Xing header data + int numOfTOC; // numOfTOC + int vbriScale; // encoded vbri scale from VBRI header data + int sizePerTable; // encoded sizePerTable from VBRI header data + int framesPerTable; //encoded framesPerTable from VBRI header data + unsigned char *toc; // pointer to unsigned char toc_buffer[100] + // may be NULL if toc not desired +} AvVBRIHeadData; + +typedef enum { + + AV_MPEG_VER_RESERVED, /* Reserved */ + AV_MPEG_VER_1, /* MPEG Version 1.0 */ + AV_MPEG_VER_2, /* MPEG Version 2.0 */ + AV_MPEG_VER_25, /* MPEG Version 2.5 */ + AV_MPEG_VER_4, /* MPEG Version 4 */ + AV_MPEG_VER_UNKNOWN /* Unable to determine version information */ +} AvMp3VerEnumType; + +typedef enum { + AV_MP3_LAYER_RESERVED = 0, /* Reserved */ + AV_MPEG2_LAYER_AAC = AV_MP3_LAYER_RESERVED, /* MPEG2 AAC compression */ + AV_MP3_LAYER_1, /* MPEG Layer 1 compression */ + AV_MP3_LAYER_2, /* MPEG Layer 2 compression */ + AV_MP3_LAYER_3, /* MPEG Layer 3 compression */ + AV_MP3_LAYER_UNKNOWN /* Unable to determine layer information */ +}AvMpegLayerEnumType; + +typedef enum { + AV_MP3_BITRATE_FREE = 0, /* Free bitrate (determined by software) */ + AV_MP3_BITRATE_8K = 8, /* Fixed bitrates */ + AV_MP3_BITRATE_16K = 16, /* */ + AV_MP3_BITRATE_24K = 24, /* */ + AV_MP3_BITRATE_32K = 32, /* */ + AV_MP3_BITRATE_40K = 40, /* */ + AV_MP3_BITRATE_48K = 48, /* */ + AV_MP3_BITRATE_56K = 56, /* */ + AV_MP3_BITRATE_64K = 64, /* */ + AV_MP3_BITRATE_80K = 80, /* */ + AV_MP3_BITRATE_96K = 96, /* */ + AV_MP3_BITRATE_112K = 112, /* */ + AV_MP3_BITRATE_128K = 128, /* */ + AV_MP3_BITRATE_144K = 144, /* */ + AV_MP3_BITRATE_160K = 160, /* */ + AV_MP3_BITRATE_176K = 176, /* */ + AV__MP3_BITRATE_192K = 192, /* */ + AV_MP3_BITRATE_224K = 224, /* */ + AV_MP3_BITRATE_256K = 256, /* */ + AV_MP3_BITRATE_288K = 288, /* */ + AV_MP3_BITRATE_320K = 320, /* */ + AV_MP3_BITRATE_352K = 352, /* */ + AV_MP3_BITRATE_384K = 384, /* */ + AV_MP3_BITRATE_416K = 416, /* */ + AV_MP3_BITRATE_448K = 448, /* */ + AV_MP3_BITRATE_VAR = 500, /* Variable bitrate (Changes each frame) */ + AV_MP3_BITRATE_UNK = 501 /* Unable to determine bitrate information */ +} AvMp3BitRateEnumType; + +typedef enum { + AV_SAMPLE_RATE_NONE, /* Zero sampling rate, turn clocks off */ + + AV_SAMPLE_RATE_8000 = 8000, /* 8k */ + AV_SAMPLE_RATE_11025 = 11025, /* 11.025k */ + AV_SAMPLE_RATE_12000 = 12000, /* 12k */ + + AV_SAMPLE_RATE_16000 = 16000, /* 16k */ + AV_SAMPLE_RATE_22050 = 22050, /* 22.050k */ + AV_SAMPLE_RATE_24000 = 24000, /* 24k */ + + AV_SAMPLE_RATE_32000 = 32000, /* 32k */ + AV_SAMPLE_RATE_44100 = 44100, /* 44.1k */ + AV_SAMPLE_RATE_48000 = 48000, /* 48k */ + + AV_SAMPLE_RATE_64000 = 64000, /* 64k */ + AV_SAMPLE_RATE_88200 = 88200, /* 88.2k */ + AV_SAMPLE_RATE_96000 = 96000, /* 96k */ + + AV_SAMPLE_RATE_MAX = 96001, /* MAX */ + AV_SAMPLE_RATE_UNKNOWN = AV_SAMPLE_RATE_MAX /* Unknown rate */ +} AvSampleRateType; + +#endif /* _MM_FILE_FORMAT_AUDIO_H_ */ diff --git a/formats/ffmpeg/include/mm_file_format_dummy.h b/formats/ffmpeg/include/mm_file_format_dummy.h new file mode 100755 index 0000000..43a5ad0 --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_dummy.h @@ -0,0 +1,37 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __MM_FILE_FORMAT_DUMMY_H__ +#define __MM_FILE_FORMAT_DUMMY_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif /*__MM_FILE_FORMAT_DUMMY_H__*/ + diff --git a/formats/ffmpeg/include/mm_file_format_ffmpeg.h b/formats/ffmpeg/include/mm_file_format_ffmpeg.h new file mode 100755 index 0000000..1820ca5 --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_ffmpeg.h @@ -0,0 +1,36 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __MM_FILE_FORMAT_FFMPEG_H__ +#define __MM_FILE_FORMAT_FFMPEG_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /*__MM_FILE_FORMAT_FFMPEG_H__*/ + diff --git a/formats/ffmpeg/include/mm_file_format_ffmpeg_drm.h b/formats/ffmpeg/include/mm_file_format_ffmpeg_drm.h new file mode 100755 index 0000000..092fd8c --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_ffmpeg_drm.h @@ -0,0 +1,37 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _MM_FILE_PLUGIN_FFMPEG_H_ +#define _MM_FILE_PLUGIN_FFMPEG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libavformat/avformat.h> + +extern URLProtocol MMFileDRMProtocol; + +#ifdef __cplusplus +} +#endif + +#endif /* _MM_FILE_PLUGIN_FFMPEG_H_ */ diff --git a/formats/ffmpeg/include/mm_file_format_ffmpeg_mem.h b/formats/ffmpeg/include/mm_file_format_ffmpeg_mem.h new file mode 100755 index 0000000..2cf62e2 --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_ffmpeg_mem.h @@ -0,0 +1,37 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _MM_FILE_PLUGIN_FFMPEG_MEM_H_ +#define _MM_FILE_PLUGIN_FFMPEG_MEM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libavformat/avformat.h> + +extern URLProtocol MMFileMEMProtocol; + +#ifdef __cplusplus +} +#endif + +#endif /* _MM_FILE_PLUGIN_FFMPEG_H_ */ diff --git a/formats/ffmpeg/include/mm_file_format_id3tag.h b/formats/ffmpeg/include/mm_file_format_id3tag.h new file mode 100755 index 0000000..8af7ee1 --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_id3tag.h @@ -0,0 +1,282 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __MM_FILE_PLUGIN_ID3TAG_H__ +#define __MM_FILE_PLUGIN_ID3TAG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include "mm_file_utils.h" + +#define IS_ID3V2_TAG(x) (((x))[0] == 0x49 && ((x))[1] == 0x44 && ((x))[2] == 0x33) +#define IS_INCLUDE_URL(x) (((x))[0] == 0x2D && ((x))[1] == 0x2D && ((x))[2] == 0x3E) +#define IS_INCLUDE_URL_UTF16(x) (((x))[0] == 0x2D && ((x))[1] == NULL && ((x))[2] == 0x2D && ((x))[3] == NULL && ((x))[4] == 0x3E && ((x))[5] == NULL) +#define IS_ENCODEDBY_UTF16(x) (((x))[0] == 0xFF && ((x))[1] == 0xFE) +#define IS_ENCODEDBY_UTF16_R(x) (((x))[0] == 0xFE && ((x))[1] == 0xFF) + +#define NEWLINE_OF_UTF16(x) (((x))[0] == 0xFF && ((x))[1] == 0xFE && ((x))[2] == 0x00 && ((x))[3] == 0x00) +#define NEWLINE_OF_UTF16_R(x) (((x))[0] == 0xFE && ((x))[1] == 0xFF && ((x))[2] == 0x00 && ((x))[3] == 0x00) + + +#define AV_WM_LOCALCODE_SIZE_MAX 2 +#define MP3_TAGv2_HEADER_LEN 10 +#define MP3_TAGv2_23_TXT_HEADER_LEN 10 +#define MP3_TAGv2_22_TXT_HEADER_LEN 6 +#define TAGV1_SEEK_GAP 10 + + +typedef enum { + AV_ID3V2_PICTURE_TYPE_MIN, + AV_ID3V2_PICTURE_TYPE_OTHER = AV_ID3V2_PICTURE_TYPE_MIN, + AV_ID3V2_PICTURE_TYPE_PNG_ONLY_FILEICON, + AV_ID3V2_PICTURE_TYPE_OTHER_FILEICON, + AV_ID3V2_PICTURE_TYPE_FRONT_COVER, + AV_ID3V2_PICTURE_TYPE_BACK_COVER, + AV_ID3V2_PICTURE_TYPE_LEAFLET_PAGE, + AV_ID3V2_PICTURE_TYPE_MEDIA_SIDEOFCD, + AV_ID3V2_PICTURE_TYPE_LEAD_ARTIST, + AV_ID3V2_PICTURE_TYPE_ARTIST_PERFORMER, + AV_ID3V2_PICTURE_TYPE_CONDUCTOR, + AV_ID3V2_PICTURE_TYPE_BAND_ORCHESTRA, + AV_ID3V2_PICTURE_TYPE_COMPOSER, + AV_ID3V2_PICTURE_TYPE_LYRICIST_TEXTWRITER, + AV_ID3V2_PICTURE_TYPE_RECORDING_LOCATION, + AV_ID3V2_PICTURE_TYPE_DURING_RECORDING, + AV_ID3V2_PICTURE_TYPE_DURING_PERFORMANCE, + AV_ID3V2_PICTURE_TYPE_MOVIE_VIDEO_SCREEN_CAPTURE, + AV_ID3V2_PICTURE_TYPE_BRIGHT_COLOURED_FISH, + AV_ID3V2_PICTURE_TYPE_ILLUSTRATION, + AV_ID3V2_PICTURE_TYPE_BAND_ARTIST_LOGOTYPE, + AV_ID3V2_PICTURE_TYPE_PUBLISHER_STUDIO_LOGOTYPE, + + AV_ID3V2_PICTURE_TYPE_MAX, + AV_ID3V2_PICTURE_TYPE_UNKNOWN = AV_ID3V2_PICTURE_TYPE_MAX /* Unknown picture type */ + +} AvID3v2PictureType; + + +#define MP3TAGINFO_SIZE 128 // file end 128 byte +#define MP3_ID3_TITLE_LENGTH 30 +#define MP3_ID3_ARTIST_LENGTH 30 +#define MP3_ID3_ALBUM_LENGTH 30 +#define MP3_ID3_YEAR_LENGTH 4 +#define MP3_ID3_DESCRIPTION_LENGTH 30 +#define MP3_ID3_GENRE_LENGTH 30 + +#define MP3_ID3_TRACKNUM_LENGTH 30 +#define MP3_ID3_ENCBY_LENGTH 30 +#define MP3_ID3_URL_LENGTH 100 +#define MP3_ID3_FRAMEID_LENGTH 30 +#define MP3_ID3_ORIGINARTIST_LENGTH 30 +#define MP3_ID3_COMPOSER_LENGTH 100 +#define MP3_ID3_IMAGE_DESCRIPTION_MAX_LENGTH 65 +#define MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH 31 +#define MP3_ID3_IMAGE_EXT_MAX_LENGTH 4 +#define TCC_FM_PATH_MOUNT_MMC "/Mount/Mmc" + + + +typedef enum { + AV_ID3V2_ISO_8859, + AV_ID3V2_UTF16, + AV_ID3V2_UTF16_BE, + AV_ID3V2_UTF8 + +} AvID3v2EncodingType; + + +typedef struct{ + char *pImageBuf; + char imageDescription[MP3_ID3_IMAGE_DESCRIPTION_MAX_LENGTH]; + char imageMIMEType[MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH]; + char imageExt[MP3_ID3_IMAGE_EXT_MAX_LENGTH]; + int pictureType; + int imageLen; + int imgDesLen; + int imgMimetypeLen; + bool bURLInfo; + +} AvTagVer2ImageInfo; + + typedef struct{ + int tagLen; + char tagVersion; + + bool bTagVer2Found; + + bool bTitleMarked; + bool bArtistMarked; + bool bAlbumMarked; + bool bYearMarked; + bool bDescriptionMarked; + bool bGenreMarked; + + bool bTrackNumMarked; + bool bEncByMarked; + bool bURLMarked; + bool bCopyRightMarked; + bool bOriginArtistMarked; + bool bComposerMarked; + bool bImageMarked; + bool bRecDateMarked; + bool bContentGroupMarked; + + bool bGenreUTF16; + +} AvTagVer2AdditionalData; + + +typedef struct +{ + int titleLen; + int artistLen; + int authorLen; + int copyrightLen; + int descriptionLen; + int commentLen; + int ratingLen; + int albumLen; + int yearLen; + int genreLen; + int tracknumLen; + int recdateLen; + +// for PC Studio Podcast + int contentGroupLen; + +// for ID3V2 Tag + int encbyLen; + int urlLen; + int originartistLen; + int composerLen; + +// To send resolution info to appl from OEM + int width; + int height; + + unsigned int bitRate; + unsigned int sampleRate; + unsigned int channels; +// unsigned long creationTime; + unsigned long duration; + +// for mp3 Info + char *pToc; // VBR�϶� SeekPosition�� ���ϱ� ���� TOC ���̺��� ������ ��� �ִ� char ï¿½è¿ , 100 ����Ʈ ���� + unsigned int mpegVersion; // 1 : mpeg 1, 2 : mpeg 2, 3 : mpeg2.5 + unsigned int layer; // 1 : layer1, 2 : layer2, 3 : layer3 + unsigned int channelIndex; // 0 : stereo, 1 : joint_stereo, 2 : dual_channel, 3 : mono + unsigned int objectType; + unsigned int headerType; + long fileLen; // mp3 ������ ��ü ���� + long headerPos; // mp3 ����� ���� ��Ÿ���� ��ġ + long datafileLen; // ID3Tag���� �����ϰ� ���� mp3 frame���� ���� , VBR�϶� XHEADDATA �� bytes �� �ش��Ѵ� + int frameSize; // mp3 frame �� ���� ũ�� + int frameNum; // mp3 ���Ͽ� �������� � ����ִ°�? + bool bVbr; // VBR mp3? + bool bPadding; // Padding? + bool bV1tagFound; + + char *pTitle; //Title/songname/ + char *pArtist; //Lead performer(s)/Soloist(s), + char *pAuthor; //Author + char *pCopyright; + char *pDescription; + char *pComment; //Same to Description. Apps use Description. So replace this to Description for ID3V2 Tag + char *pRating; + char *pAlbum; //Album/Movie/ + char *pYear; + char *pGenre; + char *pTrackNum; //Track number/Position in set + char *pRecDate; //Recording dates + +// for PC Studio Podcast + char *pContentGroup; + +// for ID3V2 Tag + char *pEncBy; //Encoded by + char *pURL; //User defined URL link frame for ID3V2 Tag + char *pOriginArtist; //Original artist(s)/performer(s) + char *pComposer; //Composer + AvTagVer2ImageInfo imageInfo; //Album art attached feature + AvTagVer2AdditionalData tagV2Info; //Needed data for ID3 tag parsing + +// for DRM 2.0 + char *pTransactionID; + +//for ID3V1 Tag + unsigned char genre; + +} AvFileContentInfo; + +typedef struct { + int videocodec; + int audiocodec; + int width; + int height; +} AvExtraInfo; + +inline static void mm_file_free_AvFileContentInfo (AvFileContentInfo *pInfo) +{ + if (pInfo) { + if (pInfo->pToc) mmfile_free (pInfo->pToc); + if (pInfo->pTitle) mmfile_free (pInfo->pTitle); + if (pInfo->pArtist) mmfile_free (pInfo->pArtist); + if (pInfo->pAuthor) mmfile_free (pInfo->pAuthor); + if (pInfo->pCopyright) mmfile_free (pInfo->pCopyright); + if (pInfo->pDescription) mmfile_free (pInfo->pDescription); + if (pInfo->pComment) mmfile_free (pInfo->pComment); + if (pInfo->pRating) mmfile_free (pInfo->pRating); + if (pInfo->pAlbum) mmfile_free (pInfo->pAlbum); + if (pInfo->pYear) mmfile_free (pInfo->pYear); + if (pInfo->pGenre) mmfile_free (pInfo->pGenre); + if (pInfo->pTrackNum) mmfile_free (pInfo->pTrackNum); + if (pInfo->pRecDate) mmfile_free (pInfo->pRecDate); + + if (pInfo->pContentGroup) mmfile_free (pInfo->pContentGroup); + + if (pInfo->pEncBy) mmfile_free (pInfo->pEncBy); + if (pInfo->pURL) mmfile_free (pInfo->pURL); + if (pInfo->pOriginArtist) mmfile_free (pInfo->pOriginArtist); + if (pInfo->pComposer) mmfile_free (pInfo->pComposer); + + if (pInfo->imageInfo.pImageBuf) mmfile_free (pInfo->imageInfo.pImageBuf); + + if (pInfo->pTransactionID) mmfile_free (pInfo->pTransactionID); + } +} + + +bool mm_file_id3tag_parse_v110 (AvFileContentInfo* pInfo, unsigned char *buffer); //20050401 Condol : for MP3 content Info. +bool mm_file_id3tag_parse_v222 (AvFileContentInfo* pInfo, unsigned char *buffer); +bool mm_file_id3tag_parse_v223 (AvFileContentInfo* pInfo, unsigned char *buffer); +bool mm_file_id3tag_parse_v224 (AvFileContentInfo* pInfo, unsigned char *buffer); +void mm_file_id3tag_restore_content_info (AvFileContentInfo* pInfo); + + +#ifdef __cplusplus +} +#endif + +#endif /*__MM_FILE_PLUGIN_ID3TAG_H__*/ + diff --git a/formats/ffmpeg/include/mm_file_format_imelody.h b/formats/ffmpeg/include/mm_file_format_imelody.h new file mode 100755 index 0000000..41cee55 --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_imelody.h @@ -0,0 +1,44 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __MM_FILE_FORMAT_IMELODY_H__ +#define __MM_FILE_FORMAT_IMELODY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct mmfileimelodytags +{ + char *title; + char *composer; + int beat; + char *copyright; + char *comment; +} tMMFileImelodyTagInfo; + + +#ifdef __cplusplus +} +#endif + +#endif /*__MM_FILE_FORMAT_IMELODY_H__*/ + diff --git a/formats/ffmpeg/include/mm_file_format_midi.h b/formats/ffmpeg/include/mm_file_format_midi.h new file mode 100755 index 0000000..87a3b99 --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_midi.h @@ -0,0 +1,49 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __MM_FILE_FORMAT_MIDI_H__ +#define __MM_FILE_FORMAT_MIDI_H__ + +#include "mm_file_formats.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int duration; + int track_num; + int is_xmf; + + char *title; + char *copyright; + char *comment; +} MIDI_INFO_SIMPLE; + +MIDI_INFO_SIMPLE* mmfile_format_get_midi_infomation (char* uri); +void mmfile_format_free_midi_infomation (MIDI_INFO_SIMPLE *info); + +#ifdef __cplusplus +} +#endif + +#endif /*__MM_FILE_FORMAT_MIDI_H__*/ + diff --git a/formats/ffmpeg/include/mm_file_format_mmf.h b/formats/ffmpeg/include/mm_file_format_mmf.h new file mode 100755 index 0000000..ffd7eae --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_mmf.h @@ -0,0 +1,36 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __MM_FILE_FORMAT_MMF_H__ +#define __MM_FILE_FORMAT_MMF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif /*__MM_FILE_FORMAT_MMF_H__*/ + diff --git a/formats/ffmpeg/include/mm_file_format_mp3.h b/formats/ffmpeg/include/mm_file_format_mp3.h new file mode 100755 index 0000000..320b9a4 --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_mp3.h @@ -0,0 +1,34 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __MM_FILE_FORMAT_MP3_H__ +#define __MM_FILE_FORMAT_MP3_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*__MM_FILE_FORMAT_MP3_H__*/ + diff --git a/formats/ffmpeg/include/mm_file_format_private.h b/formats/ffmpeg/include/mm_file_format_private.h new file mode 100755 index 0000000..366d5db --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_private.h @@ -0,0 +1,52 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _MMFILE_FORMAT_PRIVATE_H_ +#define _MMFILE_FORMAT_PRIVATE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <mm_types.h> +#include "mm_file_formats.h" + +/* open functions list: the order of list depends on mm-types.h */ +int mmfile_format_open_dummy (MMFileFormatContext *fileContext); +int mmfile_format_open_ffmpg (MMFileFormatContext *fileContext); +int mmfile_format_open_mp3 (MMFileFormatContext *fileContext); +//int mmfile_format_open_3gp (MMFileFormatContext *fileContext); +//int mmfile_format_open_avi (MMFileFormatContext *fileContext); +//int mmfile_format_open_asf (MMFileFormatContext *fileContext); +int mmfile_format_open_mmf (MMFileFormatContext *fileContext); +int mmfile_format_open_amr (MMFileFormatContext *fileContext); +int mmfile_format_open_aac (MMFileFormatContext *fileContext); +int mmfile_format_open_wav (MMFileFormatContext *fileContext); +int mmfile_format_open_mid (MMFileFormatContext *fileContext); +int mmfile_format_open_imy (MMFileFormatContext *fileContext); + + +#ifdef __cplusplus +} +#endif + +#endif /* _MMFILE_FORMAT_PRIVATE_H_ */ + diff --git a/formats/ffmpeg/include/mm_file_format_tag_id3.h b/formats/ffmpeg/include/mm_file_format_tag_id3.h new file mode 100755 index 0000000..e3b4d81 --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_tag_id3.h @@ -0,0 +1,56 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _MMFILE_ID3_TAG_H_ +#define _MMFILE_ID3_TAG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mm_file_utils.h" +#include "mm_file_format_tags.h" + +#define MMFILE_ID3TAG_FAIL 0 +#define MMFILE_ID3TAG_SUCCESS 1 + +typedef void* MMFileID3TagHandle; + +typedef enum mmfileId3TagInfoVersion +{ + MMFILE_ID3TAG_V1_0 = 0, + MMFILE_ID3TAG_V1_1, + MMFILE_ID3TAG_V2_0, + MMFILE_ID3TAG_V2_2, + MMFILE_ID3TAG_V2_3, + MMFILE_ID3TAG_V2_4, + MMFILE_ID3TAG_VMAX, +} eMMFileID3TagVersion; + +int MMFileID3V1TagFind (MMFileIOHandle *fp, unsigned char bAppended, unsigned int startOffset, unsigned int endOffset, tMMFileTags *out); +int MMFileID3V2TagFind (MMFileIOHandle *fp, unsigned char bAppended, unsigned int startOffset, unsigned int endOffset, tMMFileTags *out); + +#ifdef __cplusplus +} +#endif + +#endif /*_MMFILE_ID3_TAG_H_ */ + diff --git a/formats/ffmpeg/include/mm_file_format_tags.h b/formats/ffmpeg/include/mm_file_format_tags.h new file mode 100755 index 0000000..eb5100d --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_tags.h @@ -0,0 +1,65 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _MMFILE_TAGS_H_ +#define _MMFILE_TAGS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* MMFileTagsHandle; + +#define MMFILE_TAGS_SUCCESS 1 +#define MMFILE_TAGS_FAIL 0 + +typedef enum mmfiletagstype +{ + MMFILE_TAG_ID3V1 = 0, + MMFILE_TAG_ID3V2, + MMFILE_TAG_MUSICAL_MATCH, + MMFILE_TAG_LYRICS3, + MMFILE_TAG_APE, + MMFILE_TAG_MAX, +} eMMFileTagsType; + +typedef struct mmfileTags +{ + eMMFileTagsType typeOfTag; + unsigned char bAppendedTag; + unsigned int version; + unsigned int startOffset; + unsigned int tagSize; + unsigned int endOffset; +} tMMFileTags; + +int MMFileOpenTags (MMFileTagsHandle *tagsHandle, const char *uriName); +int MMFileGetFirstTag (MMFileTagsHandle tagsHandle, tMMFileTags *out); +int MMFileGetNextTag (MMFileTagsHandle tagsHandle, tMMFileTags *out); +int MMFileTagsClose (MMFileTagsHandle tagsHandle); + +#ifdef __cplusplus +} +#endif + + +#endif /* _MMFILE_TAGS_H_ */ + diff --git a/formats/ffmpeg/include/mm_file_format_wav.h b/formats/ffmpeg/include/mm_file_format_wav.h new file mode 100755 index 0000000..f7bd25c --- /dev/null +++ b/formats/ffmpeg/include/mm_file_format_wav.h @@ -0,0 +1,37 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __MM_FILE_FORMAT_WAV_H__ +#define __MM_FILE_FORMAT_WAV_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif /*__MM_FILE_FORMAT_WAV_H__*/ + diff --git a/formats/ffmpeg/mm_file_format_aac.c b/formats/ffmpeg/mm_file_format_aac.c new file mode 100755 index 0000000..762440f --- /dev/null +++ b/formats/ffmpeg/mm_file_format_aac.c @@ -0,0 +1,965 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <stdio.h> +#include <string.h> /*memcmp*/ +#include <stdlib.h> /*malloc*/ + +#include <mm_error.h> + +#include "mm_debug.h" +#include "mm_file_utils.h" +#include "mm_file_format_private.h" +#include "mm_file_format_aac.h" + + +// Internal Error Type +#define MMFILE_AAC_PARSER_FILE_END 2 + +// Media specific definations +#define MMFILE_AAC_ADIF_HEADER_MAX_SIZE 30 +#define MMFILE_AAC_ADTS_HEADER_MAX_SIZE 7 +#define AAC_ADTS_FRAME_LEN_OFFSET 30 +#define AAC_ADTS_SAMPLES_PER_FRAME 1024 + +#define IS_AAC_ADIF_HEADER(buff) (!(memcmp((buff), "ADIF", 4))) +#define IS_AAC_ADTS_HEADER(buff) (((buff)[0] == 0xff) && (((buff)[1] & 0xf0) == 0xf0)) + + +// Array to Number conversions +#define GET_INT_NUMBER(buff) (int)( (((int)(buff)[0]) << 24) | \ + (((int)(buff)[1]) << 16) | \ + (((int)(buff)[2]) << 8) | \ + (((int)(buff)[3]))) + +#define GET_SHORT_NUMBER(buff) (short)( ((short)(buff)[0] << 8) | \ + ((short)(buff)[1]) ) + + + +typedef enum _mmfile_aac_format_type { + AAC_FORMAT_ADIF, + AAC_FORMAT_ADTS, + AAC_FORMAT_UNKNOWN +}TAacFormatType; + +typedef enum _mmfile_aac_bitstream_type { + AAC_STREAM_CONSTANT, + AAC_STREAM_VARIABLE +}TAacStreamType; + +typedef enum _mmfile_aac_mpeg_type { + AAC_MPEG_4, + AAC_MPEG_2 +}TAacMpegType; + +typedef struct _mmfile_aac_handle { + MMFileIOHandle* hFile; + AvFileContentInfo id3Handle; + unsigned int streamOffset; + unsigned int tagOffset; + char isTagPresent; + unsigned int tagInfoSize; + unsigned char tagVersion; + TAacFormatType formatType; + TAacStreamType streamType; + TAacMpegType mpegType; + tMMFILE_AAC_STREAM_INFO streamInfo; + tMMFILE_AAC_TAG_INFO tagInfo; +}tMMFILE_AAC_HANDLE; + + +/*Index table for Sampling frequency */ +const int Sampling_freq_table[16] = { 96000, 88200, 64000, 48000, + 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, + 0, 0, 0, 0 }; + +/* internal APIs */ +void _aac_init_handle(tMMFILE_AAC_HANDLE* privateData); +int _search_id3tag(tMMFILE_AAC_HANDLE* pData); +int _parse_id3_tag(tMMFILE_AAC_HANDLE* pData); +int _get_range_bits_value (unsigned char* buff, int fieldOffset, int fieldSize); +int _parse_aac_adif_header (tMMFILE_AAC_HANDLE* pData); +int _get_next_adts_frame_length(tMMFILE_AAC_HANDLE* pData, int* frameLen); +int _parse_aac_adts_header(tMMFILE_AAC_HANDLE* pData); + + +void _aac_init_handle(tMMFILE_AAC_HANDLE* privateData) +{ + /* Default Initializations */ + privateData->streamOffset = 0; + privateData->isTagPresent = FALSE; + privateData->streamOffset = 0; + privateData->tagOffset = 0; + + privateData->streamInfo.fileSize = 0; + privateData->streamInfo.duration = 0; + privateData->streamInfo.bitRate = 0; + privateData->streamInfo.samplingRate = 0; + privateData->streamInfo.frameRate = 0; + privateData->streamInfo.numAudioChannels = 0; + privateData->streamInfo.numTracks = 1; + privateData->streamInfo.profileType = 0; + + privateData->tagInfo.title = NULL; + privateData->tagInfo.author = NULL; + privateData->tagInfo.artist = NULL; + privateData->tagInfo.album = NULL; + privateData->tagInfo.year = NULL; + privateData->tagInfo.copyright = NULL; + privateData->tagInfo.comment = NULL; + privateData->tagInfo.genre = NULL; + privateData->tagInfo.composer = NULL; + privateData->tagInfo.classification = NULL; + privateData->tagInfo.rating = NULL; + privateData->tagInfo.recordDate = NULL; + privateData->tagInfo.conductor = NULL; + privateData->tagInfo.artwork = NULL; + privateData->tagInfo.artworkSize = 0; + privateData->tagInfo.artworkMime = NULL; +} + + +int _search_id3tag(tMMFILE_AAC_HANDLE* pData) +{ + unsigned char tagHeader[MP3_TAGv2_HEADER_LEN] = {0,}; + int encSize = 0; + int readed = 0; + + mmfile_seek(pData->hFile, 0, MMFILE_SEEK_SET); + readed = mmfile_read (pData->hFile, tagHeader, MP3_TAGv2_HEADER_LEN); + if (MP3_TAGv2_HEADER_LEN != readed) { +#ifdef __MMFILE_TEST_MODE__ + debug_msg("Read Fail"); +#endif + return MMFILE_AAC_PARSER_FAIL; + } + + if (!IS_ID3V2_TAG(tagHeader)) { +#ifdef __MMFILE_TEST_MODE__ + debug_msg("No ID3 Tag"); +#endif + goto search_end; + } + + if (tagHeader[3] == 0xFF || tagHeader[4] == 0xFF || + tagHeader[6] >= 0x80 || tagHeader[7] >= 0x80 || + tagHeader[8] >= 0x80 || tagHeader[9] >= 0x80) { +#ifdef __MMFILE_TEST_MODE__ + debug_msg("Read Fail"); +#endif + return MMFILE_AAC_PARSER_FAIL; + } + + pData->tagVersion = tagHeader[3]; + + if(pData->tagVersion > 4) { +#ifdef __MMFILE_TEST_MODE__ + debug_msg("\nTag version not supported"); +#endif + return MMFILE_AAC_PARSER_FAIL; + } + + encSize = GET_INT_NUMBER(&tagHeader[6]); + pData->tagInfoSize = MP3_TAGv2_HEADER_LEN; + + pData->tagInfoSize += (((encSize & 0x0000007F) >> 0) | ((encSize & 0x00007F00) >> 1) | \ + ((encSize & 0x007F0000) >> 2) | ((encSize & 0x7F000000) >> 3)); + + if(pData->tagInfoSize > pData->streamInfo.fileSize) { +#ifdef __MMFILE_TEST_MODE__ + debug_msg("Invalid size"); +#endif + return MMFILE_AAC_PARSER_FAIL; + } + + pData->isTagPresent = TRUE; + pData->tagOffset = 0; + pData->streamOffset = pData->tagInfoSize; + + /* Filling the information in id3Handle for tag parsing */ + pData->id3Handle.fileLen = pData->streamInfo.fileSize; + pData->id3Handle.tagV2Info.tagLen = pData->tagInfoSize; + pData->id3Handle.tagV2Info.tagVersion = pData->tagVersion; + pData->id3Handle.tagV2Info.tagLen = pData->tagInfoSize; + +search_end: + return MMFILE_AAC_PARSER_SUCCESS; +} + + +int _parse_id3_tag(tMMFILE_AAC_HANDLE* pData) +{ + unsigned char* tagBuff = NULL; + AvFileContentInfo* hTag = &pData->id3Handle; + int ret = FALSE; + int readed = 0; + + mmfile_seek(pData->hFile, pData->tagOffset, MMFILE_SEEK_SET); + tagBuff = (unsigned char*) mmfile_malloc(hTag->fileLen); + if(tagBuff == NULL) { + ret = MMFILE_AAC_PARSER_FAIL; + debug_error ("failed to memory allocation. %d\n", hTag->fileLen); + goto failure; + } + + readed = mmfile_read(pData->hFile, tagBuff, hTag->fileLen); + if (readed != hTag->fileLen) { + debug_error ("failed to read. %d, %lld\n", readed, hTag->fileLen); + goto failure; + } + + switch(hTag->tagV2Info.tagVersion) { + case 1: + ret = mm_file_id3tag_parse_v110(hTag, tagBuff); + break; + case 2: + ret = mm_file_id3tag_parse_v222(hTag, tagBuff); + break; + case 3: + ret = mm_file_id3tag_parse_v223(hTag, tagBuff); + break; + case 4: + ret = mm_file_id3tag_parse_v224(hTag, tagBuff); + break; + default: + debug_error ("Invalid Tag version [%d]\n", hTag->tagV2Info.tagVersion); + break; + } + + if(ret == FALSE) { + ret = MMFILE_AAC_PARSER_FAIL; + debug_warning ("failed to parse\n"); + goto failure; + } + + mm_file_id3tag_restore_content_info(hTag); + + pData->tagInfo.title = hTag->pTitle; + pData->tagInfo.author = hTag->pAuthor; + pData->tagInfo.artist = hTag->pArtist; + pData->tagInfo.album = hTag->pAlbum; + pData->tagInfo.year = hTag->pYear; + pData->tagInfo.copyright = hTag->pCopyright; + pData->tagInfo.comment = hTag->pDescription; + pData->tagInfo.genre = hTag->pGenre; + pData->tagInfo.tracknum = hTag->pTrackNum; + pData->tagInfo.composer = hTag->pComposer; + pData->tagInfo.classification = hTag->pContentGroup; + pData->tagInfo.rating = hTag->pRating; + pData->tagInfo.recordDate = hTag->pRecDate; + pData->tagInfo.conductor = hTag->pConductor; + pData->tagInfo.artworkMime = hTag->imageInfo.imageMIMEType; + pData->tagInfo.artworkSize = hTag->imageInfo.imageLen; + pData->tagInfo.artwork = hTag->imageInfo.pImageBuf; + + ret = MMFILE_AAC_PARSER_SUCCESS; + + +failure: + if(tagBuff) { + mmfile_free(tagBuff); + tagBuff = NULL; + } + + return ret; + +} + + +int _get_range_bits_value (unsigned char* buff, int fieldOffset, int fieldSize) +{ + int pos = 0; + unsigned int srcByteStartOff = 0; + unsigned int srcByteEndOff = 0; + unsigned int srcBitStartOff = 0; + unsigned int srcBitEndOff = 0; + unsigned char dest[4] = {0,}; + unsigned int res = 0; + unsigned int i,j, temp; + unsigned char extraByteFlag = 0; + unsigned int occupiedBytes = 0; + unsigned char mask = 0, maskBit = 0x01; + + + srcByteStartOff = (fieldOffset / 8); + srcBitStartOff = (fieldOffset % 8); + + srcByteEndOff = ((fieldOffset + fieldSize - 1) / 8); + srcBitEndOff = ((fieldOffset + fieldSize - 1) % 8); + + occupiedBytes = srcByteEndOff - srcByteStartOff + 1; + + for(i = srcByteStartOff, j = 0; i <= srcByteEndOff && j <= 3; i++,j++) { + dest[j] = buff[i]; + } + + for(pos = 7; pos>= (char)srcBitStartOff; pos--) { + mask = mask | maskBit; + maskBit <<= 1; + } + + dest[0] = dest[0] & mask; + + if(i <= srcByteEndOff) { + extraByteFlag = 1; + } + + res = GET_INT_NUMBER(dest); + + if(!extraByteFlag) { + temp = (4 - occupiedBytes) * 8 + (7 - srcBitEndOff); + res >>= temp; + } + + if(extraByteFlag) { + res <<= srcBitStartOff; + temp = buff[srcByteEndOff] >> (7 - srcBitEndOff); + res = res | (unsigned int)temp; + } + + return res; +} + + +int _parse_aac_adif_header (tMMFILE_AAC_HANDLE* pData) +{ + unsigned char adifHeader[MMFILE_AAC_ADIF_HEADER_MAX_SIZE] = {0,}; + int currentBitOffset = 0; + unsigned int fieldValue = 0; + int copyRightStatus = 0; + int readed = 0; + + mmfile_seek(pData->hFile, pData->streamOffset, MMFILE_SEEK_SET); + readed = mmfile_read(pData->hFile, adifHeader, MMFILE_AAC_ADIF_HEADER_MAX_SIZE); + if (readed < 0) { + return MMFILE_AAC_PARSER_FAIL; + } + + if(memcmp(adifHeader, "ADIF", 4) != 0) { + return MMFILE_AAC_PARSER_FAIL; + } + currentBitOffset += 32; + + copyRightStatus = _get_range_bits_value(adifHeader, currentBitOffset, 1); + currentBitOffset += 1; + + if(copyRightStatus) { + //skipping Copyright info + currentBitOffset += 72; + } + + //Original/copy + fieldValue = _get_range_bits_value(adifHeader, currentBitOffset, 1); + currentBitOffset += 1; + + //skipping Home status + currentBitOffset += 1; + + //Bit stream type + fieldValue = _get_range_bits_value(adifHeader, currentBitOffset, 1); + currentBitOffset += 1; + if(!fieldValue) { + pData->streamType = AAC_STREAM_CONSTANT; + } + else { + pData->streamType = AAC_STREAM_VARIABLE; + } + + //Bit-rate + pData->streamInfo.bitRate = _get_range_bits_value(adifHeader, currentBitOffset, 23); + currentBitOffset += 23; + + //Num of program config elements + fieldValue = _get_range_bits_value(adifHeader, currentBitOffset, 4); + currentBitOffset += 4; + + //skipping adif buffer fullness + currentBitOffset += 20; + + //skipping element instance tag + currentBitOffset += 4; + + //Profile + pData->streamInfo.profileType = _get_range_bits_value(adifHeader, currentBitOffset, 2); + currentBitOffset += 2; + + //sampling freq index + fieldValue = _get_range_bits_value(adifHeader, currentBitOffset, 4); + currentBitOffset += 4; + pData->streamInfo.samplingRate = Sampling_freq_table[fieldValue]; + + //num_front_channel_elements + pData->streamInfo.numAudioChannels = _get_range_bits_value(adifHeader, currentBitOffset, 4); + currentBitOffset += 4; + + //num_side_channel_elements + pData->streamInfo.numAudioChannels += _get_range_bits_value(adifHeader, currentBitOffset, 4); + currentBitOffset += 4; + + //num_back_channel_elements + pData->streamInfo.numAudioChannels += _get_range_bits_value(adifHeader, currentBitOffset, 4); + currentBitOffset += 4; + + //num_lfe_channel_elements + pData->streamInfo.numAudioChannels += _get_range_bits_value(adifHeader, currentBitOffset, 2); + currentBitOffset += 2; + + return MMFILE_AAC_PARSER_SUCCESS; + +} + + +int _parse_aac_adts_header(tMMFILE_AAC_HANDLE* pData) +{ + unsigned char adtsHeader[MMFILE_AAC_ADTS_HEADER_MAX_SIZE] = {0,}; + int currentBitOffset = 0; + unsigned int fieldValue = 0; + int readed = 0; + + mmfile_seek(pData->hFile, pData->streamOffset, MMFILE_SEEK_SET); + readed = mmfile_read(pData->hFile, adtsHeader, MMFILE_AAC_ADTS_HEADER_MAX_SIZE); + if (readed < 0) { + return MMFILE_AAC_PARSER_FAIL; + } + + if(!IS_AAC_ADTS_HEADER(adtsHeader)) { + return MMFILE_AAC_PARSER_FAIL; + } + currentBitOffset += 12; + + //adtsId + fieldValue = _get_range_bits_value(adtsHeader, currentBitOffset, 1); + currentBitOffset += 1; + pData->mpegType = (fieldValue != 0); + + //LayerType + fieldValue = _get_range_bits_value(adtsHeader, currentBitOffset, 2); + currentBitOffset += 2; + + //skipping Protection Absent + currentBitOffset += 1; + + //ProfileType + fieldValue = _get_range_bits_value(adtsHeader, currentBitOffset, 2); + currentBitOffset += 2; + pData->streamInfo.profileType = fieldValue; + + //SamplingrateIndex + fieldValue = _get_range_bits_value(adtsHeader, currentBitOffset, 4); + currentBitOffset += 4; + pData->streamInfo.samplingRate = Sampling_freq_table[fieldValue]; + + //skipping PrivateBit + currentBitOffset += 1; + + //ChannelConfig + pData->streamInfo.numAudioChannels = _get_range_bits_value(adtsHeader, currentBitOffset, 3); + currentBitOffset += 3; + + //Original/copy status + fieldValue = _get_range_bits_value(adtsHeader, currentBitOffset, 1); + currentBitOffset += 1; + + //skipping Home status + fieldValue = _get_range_bits_value(adtsHeader, currentBitOffset, 1); + currentBitOffset += 1; + + //copy right Id status bit + currentBitOffset += 1; + + return MMFILE_AAC_PARSER_SUCCESS; +} + + +int _get_next_adts_frame_length(tMMFILE_AAC_HANDLE* pData, int* frameLen) +{ + unsigned char adtsHeader[MMFILE_AAC_ADTS_HEADER_MAX_SIZE] = {0,}; + int ret = MMFILE_AAC_PARSER_SUCCESS; + long long filePosBefore = mmfile_tell(pData->hFile); + int readed = 0; + + readed = mmfile_read(pData->hFile, adtsHeader, MMFILE_AAC_ADTS_HEADER_MAX_SIZE); + if (readed < 0) + return MMFILE_AAC_PARSER_FAIL; + +#ifdef __MMFILE_TEST_MODE__ + debug_msg("\nFILE POS: %lld\n", filePosBefore); + debug_msg("\nADTS HEADER: [%2x] [%2x] [%2x] [%2x] [%2x] [%2x]\n", + adtsHeader[0], adtsHeader[1], adtsHeader[2], adtsHeader[3], adtsHeader[4], adtsHeader[5]); +#endif + + if(mmfile_tell(pData->hFile) >= pData->streamInfo.fileSize) { + *frameLen = 0; + ret = MMFILE_AAC_PARSER_FILE_END; + goto function_end; + } + + if(!IS_AAC_ADTS_HEADER(adtsHeader)) { + *frameLen = 0; + ret = MMFILE_AAC_PARSER_FAIL; + goto function_end; + } + + *frameLen = _get_range_bits_value(adtsHeader, AAC_ADTS_FRAME_LEN_OFFSET, 13); + + if(*frameLen == 0 || *frameLen > (pData->streamInfo.fileSize - filePosBefore)) { + *frameLen = 0; + ret = MMFILE_AAC_PARSER_FAIL; + goto function_end; + } + +function_end: + + mmfile_seek(pData->hFile, filePosBefore + *frameLen, MMFILE_SEEK_SET); + + return ret; +} + + +int mmfile_aacparser_open (MMFileAACHandle *handle, const char *filenamec) +{ + tMMFILE_AAC_HANDLE *privateData = NULL; + int ret = 0; + unsigned char header[4] = {0,}; + int firstFrameLen = 0; + int readed = 0; + + if (NULL == filenamec) { + debug_error ("file source is NULL\n"); + return MMFILE_AAC_PARSER_FAIL; + } + + privateData = mmfile_malloc (sizeof(tMMFILE_AAC_HANDLE)); + if (NULL == privateData) { + debug_error ("file source is NULL\n"); + return MMFILE_AAC_PARSER_FAIL; + } + + ret = mmfile_open (&privateData->hFile, filenamec, MMFILE_RDONLY); + if(ret == MMFILE_UTIL_FAIL) { + debug_error ("error: mmfile_open\n"); + goto exception; + } + + /* Initialize the members of handle */ + _aac_init_handle(privateData); + + mmfile_seek (privateData->hFile, 0, MMFILE_SEEK_END); + privateData->streamInfo.fileSize= mmfile_tell(privateData->hFile); + + mmfile_seek (privateData->hFile, 0, MMFILE_SEEK_SET); + + /* Search the existance of ID3 tag */ + ret = _search_id3tag(privateData); + if(ret == MMFILE_AAC_PARSER_FAIL) { + debug_error("Error in searching the ID3 tag\n"); + goto exception; + } + + mmfile_seek (privateData->hFile, privateData->streamOffset, MMFILE_SEEK_SET); + readed = mmfile_read (privateData->hFile, header, 4); + if (readed != 4) + goto exception; + + if(IS_AAC_ADIF_HEADER(header)) { + privateData->formatType = AAC_FORMAT_ADIF; + +#ifdef __MMFILE_TEST_MODE__ + debug_msg("AAC Format: ADIF\n"); +#endif + + } + else if(IS_AAC_ADTS_HEADER(header)) { + privateData->formatType = AAC_FORMAT_ADTS; + +#ifdef __MMFILE_TEST_MODE__ + debug_msg("AAC Format: ADTS\n"); +#endif + + /* Verify whether the first frame size is proper */ + mmfile_seek (privateData->hFile, privateData->streamOffset, MMFILE_SEEK_SET); + ret = _get_next_adts_frame_length(privateData, &firstFrameLen); + if(ret == MMFILE_AAC_PARSER_FAIL) { + debug_error("Invalid Frame length in ADTS header\n"); + goto exception; + } + } + else { + privateData->formatType = AAC_FORMAT_UNKNOWN; + debug_error("AAC Format: UNKNOWN\n"); + goto exception; + } + + *handle = privateData; + + return MMFILE_AAC_PARSER_SUCCESS; + +exception: + if (privateData) { + mmfile_close (privateData->hFile); + mmfile_free (privateData); + *handle = NULL; + } + return MMFILE_AAC_PARSER_FAIL; + +} + + +int mmfile_aacparser_get_stream_info (MMFileAACHandle handle, tMMFILE_AAC_STREAM_INFO *aacinfo) +{ + tMMFILE_AAC_HANDLE *privateData = NULL; + int frameLen = 0; + long long totalFrames = 0, totalFrameLength = 0; + unsigned long long streamDataSize = 0; + int ret = MMFILE_AAC_PARSER_SUCCESS; + + if (NULL == handle || NULL == aacinfo) { + debug_error ("handle is NULL\n"); + return MMFILE_AAC_PARSER_FAIL; + } + + privateData = (tMMFILE_AAC_HANDLE *) handle; + + if(privateData->formatType == AAC_FORMAT_ADIF) { + ret = _parse_aac_adif_header(privateData); + aacinfo->iseekable = 0; + } + else { + ret = _parse_aac_adts_header(privateData); + aacinfo->iseekable = 1; + } + + if(ret == MMFILE_AAC_PARSER_FAIL) { + debug_error("Error in parsing the stream header\n"); + return ret; + } + + mmfile_seek(privateData->hFile, privateData->streamOffset, MMFILE_SEEK_SET); + + if(privateData->formatType == AAC_FORMAT_ADTS) { + + while(TRUE) { + ret = _get_next_adts_frame_length(privateData, &frameLen); + if(ret != MMFILE_AAC_PARSER_SUCCESS) { + break; + } + totalFrameLength += frameLen - MMFILE_AAC_ADTS_HEADER_MAX_SIZE; + totalFrames++; + } + + if(ret == MMFILE_AAC_PARSER_FAIL) { + debug_error("Found corrupted frames!!! Ignoring\n"); + } + +#ifdef __MMFILE_TEST_MODE__ + debug_msg("No of ADTS frames: %d\n", totalFrames); +#endif + privateData->streamInfo.frameRate = privateData->streamInfo.samplingRate / AAC_ADTS_SAMPLES_PER_FRAME; + + if(privateData->streamInfo.frameRate) + privateData->streamInfo.duration = (totalFrames * 1000) / privateData->streamInfo.frameRate; + else privateData->streamInfo.duration = 0; + + if(privateData->streamInfo.duration) + privateData->streamInfo.bitRate = (totalFrameLength * 8 * 1000) / (privateData->streamInfo.duration); + else privateData->streamInfo.bitRate = 0; + + } + else { + streamDataSize = (unsigned long long)privateData->streamInfo.fileSize - privateData->tagInfoSize; + privateData->streamInfo.duration = streamDataSize * 8 * 1000 / privateData->streamInfo.bitRate; + } + + // Return the stream info structure + memcpy(aacinfo, &(privateData->streamInfo), sizeof(tMMFILE_AAC_STREAM_INFO)); + + return MMFILE_AAC_PARSER_SUCCESS; +} + + +int mmfile_aacparser_get_tag_info (MMFileAACHandle handle, tMMFILE_AAC_TAG_INFO *tagInfo) +{ + tMMFILE_AAC_HANDLE *privateData = NULL; + int ret = 0; + + if (NULL == handle || NULL == tagInfo) { + debug_error ("handle is NULL\n"); + return MMFILE_AAC_PARSER_FAIL; + } + + privateData = (tMMFILE_AAC_HANDLE *) handle; + if(privateData->id3Handle.tagV2Info.tagVersion == 0) + { + debug_warning ("There is no Tag info\n"); + return MMFILE_AAC_PARSER_SUCCESS; + } + + ret = _parse_id3_tag(privateData); + if(ret == MMFILE_AAC_PARSER_FAIL) { + debug_warning ("Error in parsing the Tag info\n"); + return ret; + } + + // Return the tag info structure + memcpy(tagInfo, &(privateData->tagInfo), sizeof(tMMFILE_AAC_TAG_INFO)); + + return MMFILE_AAC_PARSER_SUCCESS; +} + + +int mmfile_aacparser_close (MMFileAACHandle handle) +{ + tMMFILE_AAC_HANDLE *privateData = NULL; + + if (NULL == handle) { + debug_error ("handle is NULL\n"); + return MMFILE_AAC_PARSER_FAIL; + } + + privateData = (tMMFILE_AAC_HANDLE *) handle; + mm_file_free_AvFileContentInfo(&privateData->id3Handle); + + mmfile_close(privateData->hFile); + + return MMFILE_AAC_PARSER_SUCCESS; +} + + +/* mm plugin interface */ +int mmfile_format_read_stream_aac (MMFileFormatContext *formatContext); +int mmfile_format_read_frame_aac (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame); +int mmfile_format_read_tag_aac (MMFileFormatContext *formatContext); +int mmfile_format_close_aac (MMFileFormatContext *formatContext); + + +EXPORT_API +int mmfile_format_open_aac (MMFileFormatContext *formatContext) +{ + MMFileAACHandle handle = NULL; + int res = MMFILE_FORMAT_FAIL; + + if (NULL == formatContext || NULL == formatContext->uriFileName) { + debug_error ("error: mmfile_format_open_aac\n"); + return MMFILE_FORMAT_FAIL; + } + + if (formatContext->pre_checked == 0) { + res = MMFileFormatIsValidAAC (formatContext->uriFileName); + if (res == 0) { + debug_error("It is not AAC file\n"); + return MMFILE_FORMAT_FAIL; + } + } + + formatContext->ReadStream = mmfile_format_read_stream_aac; + formatContext->ReadFrame = mmfile_format_read_frame_aac; + formatContext->ReadTag = mmfile_format_read_tag_aac; + formatContext->Close = mmfile_format_close_aac; + + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = 1; + + res = mmfile_aacparser_open (&handle, formatContext->uriFileName); + if (MMFILE_AAC_PARSER_FAIL == res) { + debug_error ("mmfile_aacparser_open\n"); + return MMFILE_FORMAT_FAIL; + } + + formatContext->privateFormatData = handle; + + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_read_stream_aac (MMFileFormatContext *formatContext) +{ + MMFileAACHandle handle = NULL; + tMMFILE_AAC_STREAM_INFO aacinfo = {0,}; + MMFileFormatStream *audioStream = NULL; + + int ret = MMFILE_FORMAT_FAIL; + + if (NULL == formatContext ) { + debug_error ("error: invalid params\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + handle = formatContext->privateFormatData; + + ret = mmfile_aacparser_get_stream_info (handle, &aacinfo); + if (MMFILE_FORMAT_SUCCESS != ret) { + debug_error ("error: mmfile_aacparser_get_stream_info\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + formatContext->isseekable = aacinfo.iseekable; + formatContext->duration = aacinfo.duration; + formatContext->videoStreamId = -1; + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = aacinfo.numTracks; + formatContext->nbStreams = 1; + + audioStream = mmfile_malloc (sizeof(MMFileFormatStream)); + if (NULL == audioStream) { + debug_error ("error: calloc_audiostream\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + audioStream->streamType = MMFILE_AUDIO_STREAM; + audioStream->codecId = MM_AUDIO_CODEC_AAC; + audioStream->bitRate = aacinfo.bitRate; + audioStream->framePerSec = aacinfo.frameRate; + audioStream->width = 0; + audioStream->height = 0; + audioStream->nbChannel = aacinfo.numAudioChannels; + audioStream->samplePerSec = aacinfo.samplingRate; + formatContext->streams[MMFILE_AUDIO_STREAM] = audioStream; + +#ifdef __MMFILE_TEST_MODE__ + mmfile_format_print_contents (formatContext); +#endif + + return MMFILE_FORMAT_SUCCESS; + +exception: + return ret; +} + +EXPORT_API +int mmfile_format_read_tag_aac (MMFileFormatContext *formatContext) +{ + MMFileAACHandle handle = NULL; + tMMFILE_AAC_TAG_INFO aacinfo = {0,}; + int ret= MMFILE_FORMAT_FAIL; + + if (NULL == formatContext) { + debug_error ("error: invalid params\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + handle = formatContext->privateFormatData; + + ret = mmfile_aacparser_get_tag_info (handle, &aacinfo); + if (MMFILE_FORMAT_SUCCESS != ret) { + debug_warning ("error: mmfile_aacparser_get_tag_info\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + if(aacinfo.title) + formatContext->title = mmfile_strdup(aacinfo.title); + if(aacinfo.author) + formatContext->author = mmfile_strdup(aacinfo.author); + if(aacinfo.artist) + formatContext->artist = mmfile_strdup(aacinfo.artist); + if(aacinfo.album) + formatContext->album = mmfile_strdup(aacinfo.album); + if(aacinfo.year) + formatContext->year = mmfile_strdup(aacinfo.year); + if(aacinfo.copyright) + formatContext->copyright = mmfile_strdup(aacinfo.copyright); + if(aacinfo.comment) + formatContext->comment = mmfile_strdup(aacinfo.comment); + if(aacinfo.genre) + formatContext->genre = mmfile_strdup(aacinfo.genre); + if(aacinfo.tracknum) + formatContext->tagTrackNum= mmfile_strdup(aacinfo.tracknum); + if(aacinfo.composer) + formatContext->composer = mmfile_strdup(aacinfo.composer); + if(aacinfo.classification) + formatContext->classification = mmfile_strdup(aacinfo.classification); + if(aacinfo.rating) + formatContext->rating = mmfile_strdup(aacinfo.rating); /*not exist rating tag in id3*/ + if(aacinfo.conductor) + formatContext->conductor = mmfile_strdup(aacinfo.conductor); + if(aacinfo.artworkMime) + formatContext->artworkMime = mmfile_strdup(aacinfo.artworkMime); + if(aacinfo.artwork) { + formatContext->artworkSize = aacinfo.artworkSize; + formatContext->artwork = mmfile_malloc(aacinfo.artworkSize); + if(formatContext->artwork == NULL) { + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + memcpy(formatContext->artwork, aacinfo.artwork, aacinfo.artworkSize); + } + +#ifdef __MMFILE_TEST_MODE__ + mmfile_format_print_contents (formatContext); +#endif + + return MMFILE_FORMAT_SUCCESS; + +exception: + return ret; +} + + +EXPORT_API +int mmfile_format_read_frame_aac (MMFileFormatContext *formatContext, + unsigned int timestamp, MMFileFormatFrame *frame) +{ + debug_error ("error: mmfile_format_read_frame_aac, no handling\n"); + + return MMFILE_FORMAT_FAIL; +} + + +EXPORT_API +int mmfile_format_close_aac (MMFileFormatContext *formatContext) +{ + MMFileAACHandle handle = NULL; + int ret = MMFILE_FORMAT_FAIL; + + if (NULL == formatContext ) { + debug_error ("error: invalid params\n"); + return MMFILE_FORMAT_FAIL; + } + + handle = formatContext->privateFormatData; + + if(NULL != handle) { + ret = mmfile_aacparser_close(handle); + if(ret == MMFILE_AAC_PARSER_FAIL) { + debug_error("error: mmfile_format_close_aac\n"); + } + } + + if(formatContext->streams[MMFILE_AUDIO_STREAM]) { + mmfile_free(formatContext->streams[MMFILE_AUDIO_STREAM]); + formatContext->streams[MMFILE_AUDIO_STREAM] = NULL; + } + + formatContext->ReadStream = NULL; + formatContext->ReadFrame = NULL; + formatContext->ReadTag = NULL; + formatContext->Close = NULL; + + return MMFILE_FORMAT_SUCCESS; +} + + diff --git a/formats/ffmpeg/mm_file_format_amr.c b/formats/ffmpeg/mm_file_format_amr.c new file mode 100755 index 0000000..d15cf62 --- /dev/null +++ b/formats/ffmpeg/mm_file_format_amr.c @@ -0,0 +1,501 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <stdio.h> +#include <string.h> /*memcmp*/ +#include <stdlib.h> /*malloc*/ + + +#include <mm_error.h> +#include "mm_debug.h" + +#include "mm_file_utils.h" +#include "mm_file_format_private.h" +#include "mm_file_format_amr.h" + + +// Media specific definations +#define NUM_AMR_NB_MODES 8 +#define NUM_AMR_WB_MODES 9 + +#define MMFILE_AMR_SINGLE_CH_HEADER_SIZE 6 +#define MMFILE_AMR_SINGLE_CH_HEADER "#!AMR\n" +#define MMFILE_AMR_WB_SINGLE_CH_HEADER_SIZE 9 +#define MMFILE_AMR_WB_SINGLE_CH_HEADER "#!AMR-WB\n" +#define MMFILE_AMR_MULTI_CH_HEADER_SIZE 12 +#define MMFILE_AMR_MULTI_CH_HEADER "#!AMR_MC1.0\n" +#define MMFILE_AMR_WB_MULTI_CH_HEADER_SIZE 15 +#define MMFILE_AMR_WB_MULTI_CH_HEADER "#!AMR-WB_MC1.0\n" + +#define MMFILE_AMR_MAX_HEADER_SIZE MMFILE_AMR_WB_MULTI_CH_HEADER_SIZE +#define MMFILE_AMR_MIN_HEADER_SIZE MMFILE_AMR_SINGLE_CH_HEADER_SIZE + +#define MMFILE_AMR_FRAME_DUR 20 +#define AMR_NB_SAMPLES_PER_SEC 8000 +#define AMR_WB_SAMPLES_PER_SEC 16000 + +#define AMR_MAX_READ_BUF_SZ 4096 + +#define AMR_GET_MODE(firstByte) (((firstByte) >> 3) & 0x0F) + + +typedef enum _mmfile_amr_format_types { + AMR_FORMAT_NB, + AMR_FORMAT_WB, + AMR_FORMAT_UNKNOWN +}eAmrFormatType; + +typedef enum _mmfile_amr_channel_type { + AMR_CHANNEL_TYPE_SINGLE, + AMR_CHANNEL_TYPE_MULTIPLE, + AMR_CHANNEL_TYPE_UNKNOWN +}eAmrChannelType; + +typedef struct _mmfile_amr_handle { + MMFileIOHandle* hFile; + long long duration; + long long fileSize; + unsigned int streamOffset; + unsigned int bitRate; + unsigned int samplingRate; + unsigned int frameRate; + unsigned int numAudioChannels; + long long numFrames; + unsigned int numTracks; + int amrMode; + eAmrFormatType amrFormat; + eAmrChannelType amrChannelType; +}tMMFILE_AMR_HANDLE; + + +typedef struct _mmfile_amr_mode_config { + unsigned int bitRate; + unsigned int frameSize; +}tAmrModeConfig; + +/*RTP format only supported*/ +/*mode vs bitRate-frameSize lookup table; [0]->AMR-NB [1]->AMR-WB */ +const tAmrModeConfig AmrModeConfigTable[2][16] = +{ + { + {4750, 13}, {5150, 14}, {5900, 16}, {6700, 18}, + {7400, 20}, {7950, 21}, {10200,27}, {12200,32}, + {0, 6}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + }, + { + {6600, 18}, {8850, 24}, {12650,33}, {14250,37}, + {15850,41}, {18250,47}, {19850,51}, {23050,59}, + {23850,61}, {0, 6}, {0, 6}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + } +}; + +/* internal APIs */ + +void _amr_init_handle(tMMFILE_AMR_HANDLE* pData) +{ + pData->hFile = NULL; + pData->duration = 0; + pData->fileSize = 0L; + pData->streamOffset = 0; + pData->bitRate = 0; + pData->samplingRate = 0; + pData->frameRate = 0; + pData->numAudioChannels = 1; + pData->numTracks = 1; + pData->numFrames = 0; + pData->amrChannelType = AMR_CHANNEL_TYPE_SINGLE; +} + +int _parse_amr_header(tMMFILE_AMR_HANDLE* pData) +{ + + unsigned char header[MMFILE_AMR_MAX_HEADER_SIZE]; + int ret = MMFILE_AMR_PARSER_SUCCESS; + + ret = mmfile_read(pData->hFile, header, MMFILE_AMR_MAX_HEADER_SIZE); + if(ret != MMFILE_AMR_MAX_HEADER_SIZE) { + return MMFILE_AMR_PARSER_FAIL; + } + +#ifdef __MMFILE_TEST_MODE__ + debug_msg("\nAMR HEADER: [%2x] [%2x] [%2x] [%2x] [%2x]\n \ + [%2x] [%2x] [%2x] [%2x] [%2x]\n \ + [%2x] [%2x] [%2x] [%2x] [%2x]\n", + header[0], header[1], header[2], header[3],header[4], + header[5], header[6], header[7], header[8],header[9], + header[10],header[11],header[12],header[13],header[14]); +#endif + + if(!(memcmp(header, MMFILE_AMR_SINGLE_CH_HEADER, MMFILE_AMR_SINGLE_CH_HEADER_SIZE))) { + pData->amrFormat = AMR_FORMAT_NB; + pData->amrChannelType = AMR_CHANNEL_TYPE_SINGLE; + pData->streamOffset = MMFILE_AMR_SINGLE_CH_HEADER_SIZE; + } + + else if(!(memcmp(header, MMFILE_AMR_WB_SINGLE_CH_HEADER, MMFILE_AMR_WB_SINGLE_CH_HEADER_SIZE))) { + pData->amrFormat = AMR_FORMAT_WB; + pData->amrChannelType = AMR_CHANNEL_TYPE_SINGLE; + pData->streamOffset = MMFILE_AMR_WB_SINGLE_CH_HEADER_SIZE; + } + + else if(!(memcmp(header, MMFILE_AMR_MULTI_CH_HEADER, MMFILE_AMR_MULTI_CH_HEADER_SIZE))){ + pData->amrFormat = AMR_FORMAT_NB; + pData->amrChannelType = AMR_CHANNEL_TYPE_MULTIPLE; + pData->streamOffset = MMFILE_AMR_MULTI_CH_HEADER_SIZE; + } + + else if(!(memcmp(header, MMFILE_AMR_WB_MULTI_CH_HEADER, MMFILE_AMR_WB_MULTI_CH_HEADER_SIZE))) { + pData->amrFormat = AMR_FORMAT_WB; + pData->amrChannelType = AMR_CHANNEL_TYPE_MULTIPLE; + pData->streamOffset = MMFILE_AMR_WB_MULTI_CH_HEADER_SIZE; + } + + else { + pData->amrFormat = AMR_FORMAT_UNKNOWN; + pData->amrChannelType = AMR_CHANNEL_TYPE_UNKNOWN; + ret = MMFILE_AMR_PARSER_FAIL; + } + + return ret; +} + + +int _parse_amr_stream(tMMFILE_AMR_HANDLE* pData) +{ + int frameLen = 0; + unsigned char amrMode = 0; + int ret = MMFILE_AMR_PARSER_SUCCESS; + unsigned char *p; + unsigned char *buf; + int readed; + int pos; + long long sum_bitrate = 0; + long long frames_bitrate = 0; + + buf = mmfile_malloc (AMR_MAX_READ_BUF_SZ); + if (!buf) { + debug_error ("failed to memory allocaion.\n"); + return MMFILE_AMR_PARSER_FAIL; + } + + for (readed = 0;;) { + readed = mmfile_read (pData->hFile, buf, AMR_MAX_READ_BUF_SZ); + if (readed <= 0) break; + + for (p = buf, pos = 0;;) { + amrMode = AMR_GET_MODE ((*(char *)p)); + frameLen = AmrModeConfigTable[pData->amrFormat][amrMode].frameSize; + sum_bitrate += AmrModeConfigTable[pData->amrFormat][amrMode].bitRate; + pData->numFrames++; + frames_bitrate += (AmrModeConfigTable[pData->amrFormat][amrMode].bitRate == 0 ? 0 : 1); + + p += frameLen; + pos += frameLen; + if (pos == readed) { + break; + } else if (pos > readed) { + mmfile_seek (pData->hFile, (pos - readed), MMFILE_SEEK_CUR); + break; + } + } + } + + mmfile_free (buf); + + pData->duration = pData->numFrames * MMFILE_AMR_FRAME_DUR; + pData->frameRate = 1000 / MMFILE_AMR_FRAME_DUR; + + if(frames_bitrate) { + pData->bitRate = sum_bitrate / frames_bitrate; + } + + return ret; +} + + +int mmfile_amrparser_open (MMFileAMRHandle *handle, const char *filenamec) +{ + tMMFILE_AMR_HANDLE *privateData = NULL; + int ret = 0; + + if (NULL == filenamec || NULL == handle) { + debug_error ("file source is NULL\n"); + return MMFILE_AMR_PARSER_FAIL; + } + + privateData = mmfile_malloc (sizeof(tMMFILE_AMR_HANDLE)); + if (NULL == privateData) { + debug_error ("file source is NULL\n"); + return MMFILE_AMR_PARSER_FAIL; + } + + /* Initialize the members of handle */ + _amr_init_handle(privateData); + + ret = mmfile_open (&privateData->hFile, filenamec, MMFILE_RDONLY); + if(ret == MMFILE_UTIL_FAIL) { + debug_error ("error: mmfile_open\n"); + goto exception; + } + + mmfile_seek (privateData->hFile, 0, MMFILE_SEEK_END); + privateData->fileSize= mmfile_tell(privateData->hFile); + mmfile_seek (privateData->hFile, 0, MMFILE_SEEK_SET); + + if(privateData->fileSize < MMFILE_AMR_MIN_HEADER_SIZE) { + debug_error("Too small file to parse!!\n"); + goto exception; + } + + ret = _parse_amr_header(privateData); + if(ret == MMFILE_AMR_PARSER_FAIL) { + debug_error("Invalid AMR header\n"); + goto exception; + } + + if(privateData->amrChannelType != AMR_CHANNEL_TYPE_SINGLE) { + debug_error("Unsupported channel mode\n"); /*Need to study AMR_Format.txt, Pg:36*/ + goto exception; + } + +#ifdef __MMFILE_TEST_MODE__ + debug_msg("AMR Format Type: %s\n", \ + privateData->amrFormat == AMR_FORMAT_NB? "AMR-NB":"AMR-WB"); +#endif + + *handle = privateData; + + return MMFILE_AMR_PARSER_SUCCESS; + +exception: + if (privateData) { + mmfile_close (privateData->hFile); + mmfile_free (privateData); + *handle = NULL; + } + return MMFILE_AMR_PARSER_FAIL; + +} + + +int mmfile_amrparser_get_stream_info (MMFileAMRHandle handle, tMMFILE_AMR_STREAM_INFO *amrinfo) +{ + tMMFILE_AMR_HANDLE *privateData = NULL; + int ret; + + if (NULL == handle || NULL == amrinfo) { + debug_error ("handle is NULL\n"); + return MMFILE_AMR_PARSER_FAIL; + } + + privateData = (tMMFILE_AMR_HANDLE *) handle; + + mmfile_seek(privateData->hFile, privateData->streamOffset, MMFILE_SEEK_SET); + + ret = _parse_amr_stream(privateData); + if(ret == MMFILE_AMR_PARSER_FAIL) { + debug_error("Error in parsing the stream\n"); + return ret; + } + + amrinfo->duration = privateData->duration; + amrinfo->fileSize = privateData->fileSize; + amrinfo->bitRate = privateData->bitRate; + amrinfo->frameRate = privateData->frameRate; + amrinfo->numAudioChannels = 1; + amrinfo->numTracks = 1; + + if(privateData->amrFormat == AMR_FORMAT_NB) { + amrinfo->samplingRate = AMR_NB_SAMPLES_PER_SEC; + } + else { + amrinfo->samplingRate = AMR_WB_SAMPLES_PER_SEC; + } + + return MMFILE_AMR_PARSER_SUCCESS; +} + + +int mmfile_amrparser_close (MMFileAMRHandle handle) +{ + tMMFILE_AMR_HANDLE *privateData = NULL; + + if (NULL == handle) { + debug_error ("handle is NULL\n"); + return MMFILE_AMR_PARSER_FAIL; + } + + privateData = (tMMFILE_AMR_HANDLE *) handle; + + mmfile_close(privateData->hFile); + + return MMFILE_AMR_PARSER_SUCCESS; +} + + + +/* mm plugin interface */ +int mmfile_format_read_stream_amr (MMFileFormatContext *formatContext); +int mmfile_format_read_frame_amr (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame); +int mmfile_format_read_tag_amr (MMFileFormatContext *formatContext); +int mmfile_format_close_amr (MMFileFormatContext *formatContext); + + +EXPORT_API +int mmfile_format_open_amr (MMFileFormatContext *formatContext) +{ + MMFileAMRHandle handle = NULL; + int res = MMFILE_FORMAT_FAIL; + + if (NULL == formatContext || NULL == formatContext->uriFileName) { + debug_error ("error: mmfile_format_open_amr\n"); + return MMFILE_FORMAT_FAIL; + } + + formatContext->ReadStream = mmfile_format_read_stream_amr; + formatContext->ReadFrame = mmfile_format_read_frame_amr; + formatContext->ReadTag = mmfile_format_read_tag_amr; + formatContext->Close = mmfile_format_close_amr; + + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = 1; + + res = mmfile_amrparser_open (&handle, formatContext->uriFileName); + if (MMFILE_AMR_PARSER_FAIL == res) { + debug_error ("mmfile_amrparser_open\n"); + return MMFILE_FORMAT_FAIL; + } + + formatContext->privateFormatData = handle; + + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_read_stream_amr (MMFileFormatContext *formatContext) +{ + MMFileAMRHandle handle = NULL; + tMMFILE_AMR_STREAM_INFO amrinfo = {0,}; + MMFileFormatStream *audioStream = NULL; + + int ret = MMFILE_FORMAT_FAIL; + + if (NULL == formatContext ) { + debug_error ("error: invalid params\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + handle = formatContext->privateFormatData; + + ret = mmfile_amrparser_get_stream_info (handle, &amrinfo); + if (MMFILE_FORMAT_SUCCESS != ret) { + debug_error ("error: mmfile_amrparser_get_stream_info\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + formatContext->duration = amrinfo.duration; + formatContext->videoStreamId = -1; + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = amrinfo.numTracks; + formatContext->nbStreams = 1; + + audioStream = mmfile_malloc (sizeof(MMFileFormatStream)); + if (NULL == audioStream) { + debug_error ("error: calloc_audiostream\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + audioStream->streamType = MMFILE_AUDIO_STREAM; + audioStream->codecId = MM_AUDIO_CODEC_AMR; + audioStream->bitRate = amrinfo.bitRate; + audioStream->framePerSec = amrinfo.frameRate; + audioStream->width = 0; + audioStream->height = 0; + audioStream->nbChannel = amrinfo.numAudioChannels; + audioStream->samplePerSec = amrinfo.samplingRate; + formatContext->streams[MMFILE_AUDIO_STREAM] = audioStream; + +#ifdef __MMFILE_TEST_MODE__ + mmfile_format_print_contents (formatContext); +#endif + + return MMFILE_FORMAT_SUCCESS; + +exception: + return ret; +} + +EXPORT_API +int mmfile_format_read_tag_amr (MMFileFormatContext *formatContext) +{ + return MMFILE_FORMAT_SUCCESS; +} + + +EXPORT_API +int mmfile_format_read_frame_amr (MMFileFormatContext *formatContext, + unsigned int timestamp, MMFileFormatFrame *frame) +{ + debug_error ("error: mmfile_format_read_frame_amr, no handling\n"); + + return MMFILE_FORMAT_FAIL; +} + + +EXPORT_API +int mmfile_format_close_amr (MMFileFormatContext *formatContext) +{ + MMFileAMRHandle handle = NULL; + int ret = MMFILE_FORMAT_FAIL; + + if (NULL == formatContext ) { + debug_error ("error: invalid params\n"); + return MMFILE_FORMAT_FAIL; + } + + handle = formatContext->privateFormatData; + + if(NULL != handle) { + ret = mmfile_amrparser_close(handle); + if(ret == MMFILE_AMR_PARSER_FAIL) { + debug_error("error: mmfile_format_close_amr\n"); + } + } + + if(formatContext->streams[MMFILE_AUDIO_STREAM]) { + mmfile_free(formatContext->streams[MMFILE_AUDIO_STREAM]); + formatContext->streams[MMFILE_AUDIO_STREAM] = NULL; + } + + formatContext->ReadStream = NULL; + formatContext->ReadFrame = NULL; + formatContext->ReadTag = NULL; + formatContext->Close = NULL; + + return MMFILE_FORMAT_SUCCESS; +} + + diff --git a/formats/ffmpeg/mm_file_format_dummy.c b/formats/ffmpeg/mm_file_format_dummy.c new file mode 100755 index 0000000..b8f7a19 --- /dev/null +++ b/formats/ffmpeg/mm_file_format_dummy.c @@ -0,0 +1,91 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string.h> /*memcmp*/ +#include <stdlib.h> /*malloc*/ + +#include <mm_error.h> + +#include "mm_debug.h" + +#include "mm_file_format_private.h" +#include "mm_file_format_dummy.h" + + +/* internal functions */ + + +/* plugin manadatory API */ +int mmfile_format_read_stream_dummy (MMFileFormatContext *formatContext); +int mmfile_format_read_frame_dummy (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame); +int mmfile_format_read_tag_dummy (MMFileFormatContext *formatContext); +int mmfile_format_close_dummy (MMFileFormatContext *formatContext); + + +EXPORT_API +int mmfile_format_open_dummy (MMFileFormatContext *formatContext) +{ + debug_warning ("called mmfile_format_open_dummy\n"); + + formatContext->ReadStream = mmfile_format_read_stream_dummy; + formatContext->ReadFrame = mmfile_format_read_frame_dummy; + formatContext->ReadTag = mmfile_format_read_tag_dummy; + formatContext->Close = mmfile_format_close_dummy; + + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_read_stream_dummy (MMFileFormatContext * formatContext) +{ + debug_warning ("called mmfile_format_read_stream_dummy\n"); + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_read_frame_dummy (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame) +{ + debug_warning ("called mmfile_format_read_frame_dummy\n"); + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_read_tag_dummy (MMFileFormatContext *formatContext) +{ + debug_warning ("called mmfile_format_read_tag_dummy\n"); + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_close_dummy (MMFileFormatContext *formatContext) +{ + debug_warning ("called mmfile_format_close_dummy\n"); + if (formatContext) + { + formatContext->ReadStream = NULL; + formatContext->ReadFrame = NULL; + formatContext->ReadTag = NULL; + formatContext->Close = NULL; + } + + return MMFILE_FORMAT_SUCCESS; +} + diff --git a/formats/ffmpeg/mm_file_format_ffmpeg.c b/formats/ffmpeg/mm_file_format_ffmpeg.c new file mode 100755 index 0000000..7d0b12f --- /dev/null +++ b/formats/ffmpeg/mm_file_format_ffmpeg.c @@ -0,0 +1,1296 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string.h> +#include <stdlib.h> + +#include <libavformat/avformat.h> +#include <libavcodec/avcodec.h> +#ifdef __MMFILE_FFMPEG_V085__ +#include <libswscale/swscale.h> +#endif +#include <mm_error.h> +#include <mm_types.h> +#include "mm_debug.h" +#include "mm_file_formats.h" +#include "mm_file_utils.h" +#include "mm_file_format_ffmpeg.h" + +#include "mm_file_format_ffmpeg_mem.h" +#include <sys/time.h> + + + +#define _SHORT_MEDIA_LIMIT 2000 /* under X seconds duration*/ + +extern int img_convert (AVPicture *dst, int dst_pix_fmt, const AVPicture *src, int src_pix_fmt,int src_width, int src_height); + +/* internal functions */ +static int _is_good_pgm (unsigned char *buf, int wrap, int xsize, int ysize); +#ifdef MMFILE_FORMAT_DEBUG_DUMP +static void _save_pgm (unsigned char *buf, int wrap, int xsize, int ysize, char *filename); +#endif +#ifdef __MMFILE_TEST_MODE__ +static void _dump_av_packet (AVPacket *pkt); +#endif + +static int _get_video_fps (int frame_cnt, int duration, AVRational r_frame_rate, int is_roundup); +static int _get_first_good_video_frame (AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx, int videoStream, AVFrame **pFrame); + +static int ConvertVideoCodecEnum (int AVVideoCodecID); +static int ConvertAudioCodecEnum (int AVAudioCodecID); + +/* plugin manadatory API */ +int mmfile_format_read_stream_ffmpg (MMFileFormatContext * formatContext); +int mmfile_format_read_frame_ffmpg (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame); +int mmfile_format_read_tag_ffmpg (MMFileFormatContext *formatContext); +int mmfile_format_close_ffmpg (MMFileFormatContext *formatContext); +static int getMimeType(int formatId, char *mimeType); + + + +EXPORT_API +int mmfile_format_open_ffmpg (MMFileFormatContext *formatContext) +{ + AVFormatContext *pFormatCtx = NULL; + AVInputFormat *grab_iformat = NULL; + int ret = 0; + int i; + char ffmpegFormatName[MMFILE_FILE_FMT_MAX_LEN] = {0,}; + char mimeType[MMFILE_MIMETYPE_MAX_LEN] = {0,}; + + formatContext->ReadStream = mmfile_format_read_stream_ffmpg; + formatContext->ReadFrame = mmfile_format_read_frame_ffmpg; + formatContext->ReadTag = mmfile_format_read_tag_ffmpg; + formatContext->Close = mmfile_format_close_ffmpg; + +#ifdef __MMFILE_TEST_MODE__ + debug_msg ("ffmpeg version: %d\n", avformat_version ()); + /** + * FFMPEG DEBUG LEVEL + * AV_LOG_QUIET -1 + * AV_LOG_FATAL 0 + * AV_LOG_ERROR 0 + * AV_LOG_WARNING 1 + * AV_LOG_INFO 1 + * AV_LOG_VERBOSE 1 + * AV_LOG_DEBUG 2 + */ + av_log_set_level (AV_LOG_DEBUG); +#else + av_log_set_level (AV_LOG_QUIET); +#endif + + av_register_all(); + + if (formatContext->filesrc->type == MM_FILE_SRC_TYPE_MEMORY) { + +#ifdef __MMFILE_FFMPEG_V085__ + ffurl_register_protocol(&MMFileMEMProtocol, sizeof (URLProtocol)); +#else + register_protocol (&MMFileMEMProtocol); +#endif + if(getMimeType(formatContext->filesrc->memory.format,mimeType)< 0) { + debug_error ("error: Error in MIME Type finding\n"); + return MMFILE_FORMAT_FAIL; + } + + memset (ffmpegFormatName, 0x00, MMFILE_FILE_FMT_MAX_LEN); + + ret = mmfile_util_get_ffmpeg_format (mimeType,ffmpegFormatName); + + if (MMFILE_UTIL_SUCCESS != ret) { + debug_error ("error: mmfile_util_get_ffmpeg_format\n"); + return MMFILE_FORMAT_FAIL; + } + + grab_iformat = av_find_input_format (ffmpegFormatName); + + if (NULL == grab_iformat) { + debug_error ("error: cannot find format\n"); + goto exception; + } + +#ifdef __MMFILE_FFMPEG_V085__ + ret = avformat_open_input (&pFormatCtx, formatContext->uriFileName, grab_iformat, NULL); +#else + ret = av_open_input_file (&pFormatCtx, formatContext->uriFileName, grab_iformat, 0, NULL); +#endif + if (ret < 0) { + debug_error("error: cannot open %s %d\n", formatContext->uriFileName, ret); + goto exception; + } + formatContext->privateFormatData = pFormatCtx; + } + + if (formatContext->filesrc->type == MM_FILE_SRC_TYPE_FILE) { + + if (formatContext->isdrm == MM_FILE_DRM_OMA) { + debug_error ("error: drm content\n"); + goto exception; + } else { +HANDLING_DRM_DIVX: +#ifdef __MMFILE_FFMPEG_V085__ + ret = avformat_open_input(&pFormatCtx, formatContext->filesrc->file.path, NULL, NULL); +#else + ret = av_open_input_file(&pFormatCtx, formatContext->filesrc->file.path, NULL, 0, NULL); +#endif + if (ret < 0) { + debug_error("error: cannot open %s %d\n", formatContext->filesrc->file.path, ret); + goto exception; + } + formatContext->privateFormatData = pFormatCtx; + } + } + + if (!pFormatCtx || !(pFormatCtx->nb_streams > 0)) { + debug_warning ("failed to find av stream. maybe corrupted data.\n"); + goto exception; + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("number of stream: %d\n", pFormatCtx->nb_streams); + #endif + + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = 0; + + for(i = 0; i < pFormatCtx->nb_streams; i++) { +#ifdef __MMFILE_FFMPEG_V085__ + if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("FFMPEG video codec id: 0x%08X\n", pFormatCtx->streams[i]->codec->codec_id); + #endif + formatContext->videoTotalTrackNum += 1; + } + if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("FFMPEG audio codec id: 0x%08X\n", pFormatCtx->streams[i]->codec->codec_id); + #endif + formatContext->audioTotalTrackNum += 1; + } +#else + if (pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("FFMPEG video codec id: 0x%08X\n", pFormatCtx->streams[i]->codec->codec_id); + #endif + formatContext->videoTotalTrackNum += 1; + } + if (pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("FFMPEG audio codec id: 0x%08X\n", pFormatCtx->streams[i]->codec->codec_id); + #endif + formatContext->audioTotalTrackNum += 1; + } +#endif + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("format: %s (%s)\n", pFormatCtx->iformat->name, pFormatCtx->iformat->long_name); + #ifdef __MMFILE_FFMPEG_V085__ + av_dump_format (pFormatCtx, 0, formatContext->filesrc->file.path, 0); + #else + dump_format (pFormatCtx, 0, formatContext->filesrc->file.path, 0); + #endif + #endif + + return MMFILE_FORMAT_SUCCESS; + +exception: /* fail to get content information */ + + mmfile_format_close_ffmpg (formatContext); + formatContext->privateFormatData = NULL; + + return MMFILE_FORMAT_FAIL; +} + + +EXPORT_API +int mmfile_format_read_stream_ffmpg (MMFileFormatContext * formatContext) +{ + AVFormatContext *pFormatCtx = NULL; + AVCodecContext *pAudioCodecCtx = NULL; + AVCodecContext *pVideoCodecCtx = NULL; + + MMFileFormatStream *videoStream = NULL; + MMFileFormatStream *audioStream = NULL; + int ret = 0; + + if (NULL == formatContext || NULL == formatContext->privateFormatData) { + debug_error ("invalid param\n"); + return MMFILE_FORMAT_FAIL; + } + + pFormatCtx = formatContext->privateFormatData; + + /** + *@important if data is corrupted, occur segment fault by av_find_stream_info(). + * - fixed 2009-06-25. + */ +#ifdef __MMFILE_FFMPEG_V100__ + ret = avformat_find_stream_info (pFormatCtx, NULL); +#else + ret = av_find_stream_info (pFormatCtx); +#endif + if ( ret < 0 ) { + debug_warning ("failed to find stream info. errcode = %d\n", ret); + goto exception; + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("FFMPEG: dur %lld, start %lld\n", pFormatCtx->duration, pFormatCtx->start_time); + #endif + + /** + *@note asf has long duration bug. and Some content's start time is wrong(negative number). + */ + if(pFormatCtx->start_time < 0) { + debug_warning ("Wrong Start time = %lld\n", pFormatCtx->start_time); + formatContext->duration = (long long)(pFormatCtx->duration) * 1000 / AV_TIME_BASE; + } + else { + formatContext->duration = (long long)(pFormatCtx->duration + pFormatCtx->start_time) * 1000 / AV_TIME_BASE; + } + + formatContext->videoStreamId = -1; + formatContext->audioStreamId = -1; + formatContext->nbStreams = 0; + + int i = 0; + for ( i = 0; i < pFormatCtx->nb_streams; i++ ) { +#ifdef __MMFILE_FFMPEG_V085__ + if ( pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { +#else + if ( pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) { +#endif + if (formatContext->videoStreamId == -1) { + videoStream = mmfile_malloc (sizeof(MMFileFormatStream)); + if (NULL == videoStream) { + debug_error ("mmfile_malloc error\n"); + goto exception; + } + + videoStream->streamType = MMFILE_VIDEO_STREAM; + formatContext->streams[MMFILE_VIDEO_STREAM] = videoStream; + formatContext->nbStreams += 1; + formatContext->videoStreamId = i; + + pVideoCodecCtx = pFormatCtx->streams[i]->codec; + if (pVideoCodecCtx) { + videoStream->codecId = ConvertVideoCodecEnum (pVideoCodecCtx->codec_id); + + /** + * Get FPS + * 1. try to get average fps of video stream. + * 2. if (1) failed, try to get fps of media container. + */ + videoStream->framePerSec = _get_video_fps (pFormatCtx->streams[i]->nb_frames, + pFormatCtx->streams[i]->duration, + pFormatCtx->streams[i]->time_base, + 1); + + if (videoStream->framePerSec == 0) + videoStream->framePerSec = av_q2d (pFormatCtx->streams[i]->r_frame_rate); + + videoStream->width = pVideoCodecCtx->width; + videoStream->height = pVideoCodecCtx->height; + videoStream->bitRate = pVideoCodecCtx->bit_rate; + } + } + } +#ifdef __MMFILE_FFMPEG_V085__ + else if ( pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) { +#else + else if ( pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO ) { +#endif + if (formatContext->audioStreamId == -1) { + audioStream = mmfile_malloc (sizeof(MMFileFormatStream)); + if (NULL == audioStream) { + debug_error ("mmfile_malloc error\n"); + goto exception; + } + + audioStream->streamType = MMFILE_AUDIO_STREAM; + formatContext->streams[MMFILE_AUDIO_STREAM] = audioStream; + formatContext->nbStreams += 1; + formatContext->audioStreamId = i; + + pAudioCodecCtx = pFormatCtx->streams[i]->codec; + if (pAudioCodecCtx) { + audioStream->codecId = ConvertAudioCodecEnum (pAudioCodecCtx->codec_id); + audioStream->bitRate = pAudioCodecCtx->bit_rate; + audioStream->nbChannel = pAudioCodecCtx->channels; + audioStream->samplePerSec = pAudioCodecCtx->sample_rate; + } + } + } + } + + if ( formatContext->nbStreams == 0 ) { + debug_error("error: there is no audio and video track\n"); + goto exception; + } + + #ifdef __MMFILE_TEST_MODE__ + mmfile_format_print_contents (formatContext); + #endif + + return MMFILE_FORMAT_SUCCESS; + +exception: + if (videoStream) { + mmfile_free (videoStream); + formatContext->streams[MMFILE_VIDEO_STREAM] = NULL; + } + + if (audioStream) { + mmfile_free (audioStream); + formatContext->streams[MMFILE_AUDIO_STREAM] = NULL; + } + + if (pFormatCtx) { +#ifdef __MMFILE_FFMPEG_V100__ + avformat_close_input (&pFormatCtx); +#else + av_close_input_file (pFormatCtx); +#endif + formatContext->privateFormatData = NULL; + } + + formatContext->audioTotalTrackNum = 0; + formatContext->videoTotalTrackNum = 0; + formatContext->nbStreams = 0; + + return MMFILE_FORMAT_FAIL; +} + + +EXPORT_API +int mmfile_format_read_tag_ffmpg (MMFileFormatContext *formatContext) +{ + AVFormatContext *pFormatCtx = NULL; + + if (NULL == formatContext || NULL == formatContext->privateFormatData) { + debug_error ("invalid param\n"); + return MMFILE_FORMAT_FAIL; + } + + pFormatCtx = formatContext->privateFormatData; + + if (formatContext->formatType == MM_FILE_FORMAT_3GP ||formatContext->formatType == MM_FILE_FORMAT_MP4) { + MMFileUtilGetMetaDataFromMP4 (formatContext); + } + +#ifdef __MMFILE_FFMPEG_V085__ +/*metadata extracted by ffmpeg*/ + int idx = 0; + + if(pFormatCtx != NULL) { + for(idx = 0; idx < 3; idx++) { + AVDictionary *metainfo = NULL; + + if(idx == 0) { //Check metadata of Content + if(pFormatCtx->metadata != NULL) { + metainfo = pFormatCtx->metadata; + } else { + continue; + } + } else if(idx == 1) { //Check metadata of Video stream + if (formatContext->videoStreamId != -1) { + AVStream *st = pFormatCtx->streams[formatContext->videoStreamId]; + if(st != NULL) + metainfo = st->metadata; + } else { + continue; + } + } else if(idx == 2) { //Check metadata of Audio stream + if (formatContext->audioStreamId != -1) { + AVStream *st = pFormatCtx->streams[formatContext->audioStreamId]; + if(st != NULL) + metainfo = st->metadata; + } else { + continue; + } + } else { + metainfo = NULL; + } + + if(metainfo != NULL) { + AVDictionaryEntry *tag = NULL; + while((tag=av_dict_get(metainfo, "", tag, AV_DICT_IGNORE_SUFFIX))) { + if(tag->key != NULL) { + if(!strcasecmp(tag->key, "title")) { + if (formatContext->title) free (formatContext->title); + formatContext->title = mmfile_strdup (tag->value); + } else if(!strcasecmp(tag->key, "artist")) { + if (formatContext->artist) free (formatContext->artist); + formatContext->artist = mmfile_strdup (tag->value); + } else if(!strcasecmp(tag->key, "composer")) { + if (formatContext->composer) free (formatContext->composer); + formatContext->composer = mmfile_strdup (tag->value); + } else if(!strcasecmp(tag->key, "album")) { + if (formatContext->album) free (formatContext->album); + formatContext->album = mmfile_strdup (tag->value); + } else if(!strcasecmp(tag->key, "copyright")) { + if (formatContext->copyright) free (formatContext->copyright); + formatContext->copyright = mmfile_strdup (tag->value); + } else if(!strcasecmp(tag->key, "comment")) { + if (formatContext->comment) free (formatContext->comment); + formatContext->comment = mmfile_strdup (tag->value); + } else if(!strcasecmp(tag->key, "genre")) { + if (formatContext->genre) free (formatContext->genre); + formatContext->genre = mmfile_strdup (tag->value); + } else if(!strcasecmp(tag->key, "date")) { + if (formatContext->year) free (formatContext->year); + formatContext->year = mmfile_strdup (tag->value); + } else if((!strcasecmp(tag->key, "track")) || (!strcasecmp(tag->key, "tracknumber"))) { + if (formatContext->tagTrackNum) free (formatContext->tagTrackNum); + formatContext->tagTrackNum = mmfile_strdup (tag->value); + } else if(!strcasecmp(tag->key, "lyrics")) { + if (formatContext->unsyncLyrics) free (formatContext->unsyncLyrics); + formatContext->unsyncLyrics= mmfile_strdup (tag->value); + } else if(!strcasecmp(tag->key, "rotate")) { //can be "90", "180", "270" + if (formatContext->rotate) free (formatContext->rotate); + formatContext->rotate= mmfile_strdup (tag->value); + } else { + debug_log("Not support metadata. [%s:%s]", tag->key, tag->value); + } + } + } + } + } + } +#else + if (pFormatCtx->title[0]) { + if (formatContext->title) + free (formatContext->title); + formatContext->title = mmfile_strdup (pFormatCtx->title); + } + if (pFormatCtx->author[0]){ + if (formatContext->author) + free (formatContext->author); + formatContext->author = mmfile_strdup (pFormatCtx->author); + } + if (pFormatCtx->copyright[0]) { + if (formatContext->copyright) + free (formatContext->copyright); + formatContext->copyright = mmfile_strdup (pFormatCtx->copyright); + } + if (pFormatCtx->comment[0]) { + if (formatContext->comment) + free (formatContext->comment); + formatContext->comment = mmfile_strdup (pFormatCtx->comment); + } + if (pFormatCtx->album[0]) { + if (formatContext->album) + free (formatContext->album); + formatContext->album = mmfile_strdup (pFormatCtx->album); + } + if (pFormatCtx->genre[0]) { + if (formatContext->genre) + free (formatContext->genre); + formatContext->genre = mmfile_strdup (pFormatCtx->genre); + } + + if (pFormatCtx->year) { + char year[10] = {0,}; + snprintf (year, 10, "%d", pFormatCtx->year); + year[9] = '\0'; + if (formatContext->year) + free (formatContext->year); + formatContext->year = mmfile_strdup (year); + } + + if (pFormatCtx->track) { + char tracknum[10] = {0,}; + snprintf (tracknum, 10, "%d", pFormatCtx->track); + tracknum[9] = '\0'; + if (formatContext->tagTrackNum) + free (formatContext->tagTrackNum); + formatContext->tagTrackNum = mmfile_strdup (tracknum); + } +#endif + #ifdef __MMFILE_TEST_MODE__ + mmfile_format_print_tags (formatContext); + #endif + + return MMFILE_FORMAT_SUCCESS; +} + + +EXPORT_API +int mmfile_format_read_frame_ffmpg (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame) +{ + AVFormatContext *pFormatCtx = NULL; + AVCodecContext *pVideoCodecCtx = NULL; + AVCodec *pVideoCodec = NULL; + AVFrame *pFrame = NULL; + AVFrame *pFrameRGB = NULL; + + int width; + int height; + int numBytes = 0; + int ret = 0; + + if (NULL == formatContext || + NULL == frame || + NULL == formatContext->privateFormatData || + formatContext->videoTotalTrackNum <= 0) { + + debug_error ("invalid param\n"); + return MMFILE_FORMAT_FAIL; + } + + pFormatCtx = formatContext->privateFormatData; + + if (formatContext->videoStreamId != -1) { + pVideoCodecCtx = pFormatCtx->streams[formatContext->videoStreamId]->codec; + if (NULL == pVideoCodecCtx) { + debug_error ("invalid param\n"); + return MMFILE_FORMAT_FAIL; + } + + pVideoCodec = avcodec_find_decoder (pVideoCodecCtx->codec_id); + if ( NULL == pVideoCodec ) { + debug_error ("error: avcodec_find_decoder failed\n"); + return MMFILE_FORMAT_FAIL; + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("flag: 0x%08X\n", pVideoCodec->capabilities); + // debug_msg (" DRAW_HORIZ_BAND : %d\n", pVideoCodec->capabilities & CODEC_CAP_DRAW_HORIZ_BAND ? 1 : 0); + // debug_msg (" DR1 : %d\n", pVideoCodec->capabilities & CODEC_CAP_DR1 ? 1 : 0); + // debug_msg (" PARSE_ONLY : %d\n", pVideoCodec->capabilities & CODEC_CAP_PARSE_ONLY ? 1 : 0); + // debug_msg (" TRUNCATED : %d\n", pVideoCodec->capabilities & CODEC_CAP_TRUNCATED ? 1 : 0); + // debug_msg (" HWACCEL : %d\n", pVideoCodec->capabilities & CODEC_CAP_HWACCEL ? 1 : 0); + // debug_msg (" DELAY : %d\n", pVideoCodec->capabilities & CODEC_CAP_DELAY ? 1 : 0); + // debug_msg (" SMALL_LAST_FRAME: %d\n", pVideoCodec->capabilities & CODEC_CAP_SMALL_LAST_FRAME ? 1 : 0); + // debug_msg (" HWACCEL_VDPAU : %d\n", pVideoCodec->capabilities & CODEC_CAP_HWACCEL_VDPAU ? 1 : 0); + #endif + + if (pVideoCodec->capabilities & CODEC_CAP_TRUNCATED) { + pVideoCodecCtx->flags |= CODEC_FLAG_TRUNCATED; + } + + /*set workaround bug flag*/ + pVideoCodecCtx->workaround_bugs = FF_BUG_AUTODETECT; +#ifdef __MMFILE_FFMPEG_V100__ + ret = avcodec_open2 (pVideoCodecCtx, pVideoCodec, NULL); +#else + ret = avcodec_open (pVideoCodecCtx, pVideoCodec); +#endif + if (ret < 0) { + debug_error ("error: avcodec_open fail.\n"); + return MMFILE_FORMAT_FAIL; + } + + pFrameRGB = avcodec_alloc_frame (); + + if (!pFrameRGB) { + debug_error ("error: pFrame or pFrameRGB is NULL\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + /* search & decode */ + // seek_ts = formatContext->duration > _SHORT_MEDIA_LIMIT ? seek_ts : 0; /*if short media, seek first key frame*/ + ret = _get_first_good_video_frame (pFormatCtx, pVideoCodecCtx, formatContext->videoStreamId, &pFrame); + if ( ret != MMFILE_FORMAT_SUCCESS ) { + debug_error ("error: get key frame\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("Video default resolution = [%dx%d]\n", pVideoCodecCtx->coded_width, pVideoCodecCtx->coded_height); + debug_msg ("Video coded resolution = [%dx%d]\n", pVideoCodecCtx->width, pVideoCodecCtx->height); + #endif + + /*sometimes, ffmpeg's width/height is wrong*/ + #if 0 /*coded_width/height sometimes wrong. so use width/height*/ + width = pVideoCodecCtx->coded_width == 0 ? pVideoCodecCtx->width : pVideoCodecCtx->coded_width; + height = pVideoCodecCtx->coded_height == 0 ? pVideoCodecCtx->height : pVideoCodecCtx->coded_height; + #endif + if((pVideoCodecCtx->width == 0) || (pVideoCodecCtx->height == 0)) { + width = pVideoCodecCtx->coded_width; + height = pVideoCodecCtx->coded_height; + } else { + width = pVideoCodecCtx->width; + height = pVideoCodecCtx->height; + } + + numBytes = avpicture_get_size(PIX_FMT_RGB24, width, height); + if (numBytes < 0) { + debug_error ("error: avpicture_get_size. [%d x %d]\n", width, height); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + frame->frameData = mmfile_malloc (numBytes); + if (NULL == frame->frameData) { + debug_error ("error: avpicture_get_size. [%d]\n", numBytes); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + ret = avpicture_fill ((AVPicture *)pFrameRGB, frame->frameData, PIX_FMT_RGB24, width, height); + if (ret < 0) { + debug_error ("error: avpicture_fill fail. errcode = 0x%08X\n", ret); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + +#ifdef __MMFILE_FFMPEG_V085__ + struct SwsContext *img_convert_ctx = NULL; + + img_convert_ctx = sws_getContext (width, height, pVideoCodecCtx->pix_fmt, + width, height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL); + + if (NULL == img_convert_ctx) { + debug_error ("failed to get img convet ctx\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + ret = sws_scale (img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, + 0, height, pFrameRGB->data, pFrameRGB->linesize); + if ( ret < 0 ) { + debug_error ("failed to convet image\n"); + ret = MMFILE_FORMAT_FAIL; + sws_freeContext(img_convert_ctx); + img_convert_ctx = NULL; + goto exception; + } + + sws_freeContext(img_convert_ctx); + img_convert_ctx = NULL; +#else + ret = img_convert ((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, pVideoCodecCtx->pix_fmt, width, height); + if ( ret < 0 ) { + debug_error ("failed to convet image\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } +#endif + frame->frameSize = numBytes; + frame->frameWidth = width; + frame->frameHeight = height; + frame->configLenth = 0; + frame->bCompressed = 0; /* false */ + + if (pFrame) av_free (pFrame); + if (pFrameRGB) av_free (pFrameRGB); + + avcodec_close(pVideoCodecCtx); + + return MMFILE_FORMAT_SUCCESS; + } + + +exception: + if (pVideoCodecCtx) avcodec_close (pVideoCodecCtx); + if (frame->frameData) { mmfile_free (frame->frameData); frame->frameData = NULL; } + if (pFrame) av_free (pFrame); + if (pFrameRGB) av_free (pFrameRGB); + return ret; +} + + +EXPORT_API +int mmfile_format_close_ffmpg(MMFileFormatContext *formatContext) +{ + if (formatContext) { + AVFormatContext *pFormatCtx = formatContext->privateFormatData; + + if (pFormatCtx) { +#ifdef __MMFILE_FFMPEG_V100__ + avformat_close_input(&pFormatCtx); +#else + av_close_input_file (pFormatCtx); +#endif + formatContext->privateFormatData = NULL; + } + } + + return MMFILE_FORMAT_SUCCESS; +} + +/** + * return average of difference + */ +static unsigned int _diff_memory (const void *s1, const void *s2, unsigned int n) +{ + char *s = (char *)s1; + char *d = (char *)s2; + int i; + int ret; + int tmp; + + for (i = 0, ret = 0; i < n; i++) { + if (*s++ != *d++) { + tmp = (*s - *d); + ret += (tmp < 0 ? -tmp : tmp); + } + } + ret /= n; + return ret; +} + +int64_t gettime(void) +{ + struct timeval tv; + gettimeofday(&tv,NULL); + return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; +} + + +#define IS_GOOD_OLD_METHOD +#ifdef IS_GOOD_OLD_METHOD +/** + * compare with center line. + */ +static int _is_good_pgm (unsigned char *buf, int wrap, int xsize, int ysize) +{ +#define _MM_CHUNK_NUM 8 /*FIXME*/ +#define _MM_CHUNK_LIMIT (_MM_CHUNK_NUM / 2) +#define _MM_CHUNK_DIFF_LIMIT (_MM_CHUNK_LIMIT * 2 + 1) /*FIXME*/ + + int i; + int step; + int point; + unsigned char *cnt; /*center line of image*/ + int is_different; + unsigned int sum_diff; + int cnt_offset; + + /*set center line*/ + step = ysize / _MM_CHUNK_NUM; + cnt_offset = (ysize / 2); + cnt = buf + cnt_offset * wrap; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("checking frame. %p, %d, %d, %d\n", buf, wrap, xsize, ysize); + #endif + + /*if too small, always ok return.*/ + if (ysize < _MM_CHUNK_NUM) + return 1; + + for (point = 0, sum_diff = 0, i = step; i < ysize; i += step) { + if (i != cnt_offset) { + + /*binary compare*/ + is_different = _diff_memory (cnt, buf + i * wrap, xsize); + point += (is_different == 0 ? 0 : 1); + sum_diff += is_different; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("check %04d line. %s [%d]\n", i, (is_different == 0 ? "same" : "different"), is_different); + #endif + + if (point >= _MM_CHUNK_LIMIT) { + if (sum_diff > _MM_CHUNK_DIFF_LIMIT) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("Good :-)\n"); + #endif + return 1; + } else { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("Bad :-(\n"); + #endif + return 0; + } + } + } + + } + return 0; +} +#else // IS_GOOD_OLD_METHOD +/* ToDo : for enhancement */ +#endif // IS_GOOD_OLD_METHOD + + + +static int +_get_video_fps (int frame_cnt, int duration, AVRational r_frame_rate, int is_roundup) +{ + double fps, round; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("frame count: %d, dur: %d, num: %d, den: %d\n", frame_cnt, duration, r_frame_rate.num, r_frame_rate.den) + #endif + + if (duration <= 0 || r_frame_rate.num <= 0 || r_frame_rate.den <= 0) + return 0; + + round = (is_roundup != 0 ? 0.50f : 0.00f); + + fps = (double)frame_cnt / ((double)(duration * r_frame_rate.num) / r_frame_rate.den) + round; + + return (int)fps; +} + +#ifdef MMFILE_FORMAT_DEBUG_DUMP +static void _save_pgm (unsigned char *buf,int wrap, int xsize,int ysize,char *filename) +{ + FILE *f; + int i; + + f = fopen(filename,"w"); + if (f) { + fprintf (f,"P5\n%d %d\n%d\n",xsize,ysize,255); + for (i = 0; i < ysize; i++) + fwrite (buf + i * wrap, 1, xsize, f); + fclose (f); + } +} +#endif + +#ifdef __MMFILE_TEST_MODE__ +static void _dump_av_packet (AVPacket *pkt) +{ + debug_msg ("--------- AV Packet -----------\n"); + debug_msg (" pts: %lld\n", pkt->pts); + debug_msg (" dts: %lld\n", pkt->dts); + debug_msg (" data: %p\n", pkt->data); + debug_msg (" size: %d\n", pkt->size); + debug_msg (" stream_index: %d\n", pkt->stream_index); +#ifdef __MMFILE_FFMPEG_V085__ + debug_msg (" flags: 0x%08X, %s\n", pkt->flags, (pkt->flags & AV_PKT_FLAG_KEY) ? "Keyframe" : "_"); +#else + debug_msg (" flags: 0x%08X, %s\n", pkt->flags, (pkt->flags & PKT_FLAG_KEY) ? "Keyframe" : "_"); +#endif + debug_msg (" duration: %d\n", pkt->duration); + debug_msg (" destruct: %p\n", pkt->destruct); + debug_msg (" priv: %p\n", pkt->priv); + debug_msg (" pos: %lld\n", pkt->pos); + debug_msg (" convergence_duration: %lld\n", pkt->convergence_duration); + debug_msg ("-------------------------------\n"); +} +#endif + +static int _get_first_good_video_frame (AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx, int videoStream, AVFrame **pFrame) +{ + // AVStream *st = NULL; + AVPacket pkt; + + AVFrame *frame = NULL; + AVFrame *tmp_frame = NULL; + AVFrame *first_frame = NULL; + + // long long timestamp; + int stream_id = videoStream; + int ret; + int found = 0; + int i,v, len, got_picture; + int retry = 0; + int key_detected; +#ifdef MMFILE_FORMAT_DEBUG_DUMP + char pgm_name[256] = {0,}; +#endif + +#define _RETRY_SEARCH_LIMIT 150 +#define _KEY_SEARCH_LIMIT (_RETRY_SEARCH_LIMIT*2) /*2 = 1 read. some frame need to read one more*/ +#define _FRAME_SEARCH_LIMIT 1000 + + first_frame = avcodec_alloc_frame (); + tmp_frame = avcodec_alloc_frame (); + + if (!first_frame || !tmp_frame) { + debug_error ("failed to alloc frame.\n"); + if (first_frame) av_free (first_frame); + if (tmp_frame) av_free (tmp_frame); + return MMFILE_FORMAT_FAIL; + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("frame: 1. %p, 2. %p\n", first_frame, tmp_frame); + #endif + +#ifdef __MMFILE_FFMPEG_V085__ + pCodecCtx->skip_frame = AVDISCARD_BIDIR; +#else + pCodecCtx->hurry_up = 1; +#endif + + for(i = 0, v = 0, key_detected = 0, frame = first_frame; i < _KEY_SEARCH_LIMIT && v < _FRAME_SEARCH_LIMIT;) { + av_init_packet (&pkt); + got_picture = 0; + + ret = av_read_frame (pFormatCtx, &pkt); + if (ret < 0) { + debug_error ("read failed. (maybe EOF or broken)\n"); + break; + } else { + if (pkt.stream_index == stream_id) { + v++; +#ifdef __MMFILE_FFMPEG_V085__ + if ((pkt.flags & AV_PKT_FLAG_KEY ) || (key_detected == 1)) +#else + if ((pkt.flags & PKT_FLAG_KEY ) || (key_detected == 1)) +#endif + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("video frame: %d, %d, %d\n", retry, i, v); + _dump_av_packet (&pkt); + #endif + + i++; + key_detected = 0; +#ifdef __MMFILE_FFMPEG_V085__ + len = avcodec_decode_video2 (pCodecCtx, frame, &got_picture, &pkt); +#else + len = avcodec_decode_video (pCodecCtx, frame, &got_picture, pkt.data, pkt.size); +#endif + if (len < 0) { + debug_warning ("Error while decoding frame %dth\n", i); + } else if (got_picture) { + if (frame->key_frame) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("key frame!\n"); + #endif + #ifdef MMFILE_FORMAT_DEBUG_DUMP + sprintf (pgm_name, "./key_%d_%d_%d.pgm", retry, i, v); + _save_pgm (frame->data[0], frame->linesize[0], pCodecCtx->width, pCodecCtx->height, pgm_name); + #endif + + found++; + + #ifdef __MMFILE_TEST_MODE__ + int64_t ti; + ti = gettime(); + #endif + ret = _is_good_pgm (frame->data[0], frame->linesize[0], pCodecCtx->width, pCodecCtx->height); + #ifdef __MMFILE_TEST_MODE__ + ti = gettime() - ti; + debug_msg ("Elapsed time = %lld\n", ti); + #endif + if (ret != 0) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("is good frame.\n"); + #endif + break; + } else { + /*reset video frame count & retry searching*/ + debug_warning ("not good fame. retry scanning.\n"); + i = 0; + v = 0; + retry++; + } + + /*set buffer frame*/ + frame = tmp_frame; + + /*limit of retry.*/ + if (retry > _RETRY_SEARCH_LIMIT) break; + + } else { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("skip (not key frame).\n"); + #endif + #ifdef MMFILE_FORMAT_DEBUG_DUMP + sprintf (pgm_name, "./not_key_%d_%d_%d.pgm", retry, i, v); + _save_pgm (frame->data[0], frame->linesize[0], pCodecCtx->width, pCodecCtx->height, pgm_name); + #endif + } + } else { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("decode not completed.\n"); + #endif + key_detected = 1; + } + } + } + } + av_free_packet (&pkt); + } + + /*free pkt after loop breaking*/ + if (pkt.data) av_free_packet (&pkt); + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("found: %d, retry: %d\n", found, retry); + #endif + + /*set decode frame to output*/ + if (found > 0) { + ret = MMFILE_FORMAT_SUCCESS; + if (retry == 0 || found == retry) { + *pFrame = first_frame; + if (tmp_frame) av_free (tmp_frame); + } else { + *pFrame = tmp_frame; + if (first_frame) av_free (first_frame); + } + } else { + ret = MMFILE_FORMAT_FAIL; + if (first_frame) av_free (first_frame); + if (tmp_frame) av_free (tmp_frame); + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("out frame: %p\n", *pFrame); + #endif + +#ifdef __MMFILE_FFMPEG_V085__ + pCodecCtx->skip_frame = AVDISCARD_NONE; +#else + pCodecCtx->hurry_up = 0; +#endif + + return ret; +} + +static int ConvertVideoCodecEnum (int AVVideoCodecID) +{ + int ret_codecid = 0; + + switch (AVVideoCodecID) + { + case AV_CODEC_ID_NONE: + ret_codecid = MM_VIDEO_CODEC_NONE; + break; + case AV_CODEC_ID_MPEG1VIDEO: + ret_codecid = MM_VIDEO_CODEC_MPEG1; + break; + case AV_CODEC_ID_MPEG2VIDEO: ///< preferred ID for MPEG-1/2 video decoding + ret_codecid = MM_VIDEO_CODEC_MPEG2; + break; + case AV_CODEC_ID_MPEG2VIDEO_XVMC: + ret_codecid = MM_VIDEO_CODEC_MPEG2; + break; + case AV_CODEC_ID_H261: + ret_codecid = MM_VIDEO_CODEC_H261; + break; + case AV_CODEC_ID_H263: + ret_codecid = MM_VIDEO_CODEC_H263; + break; + case AV_CODEC_ID_MPEG4: + ret_codecid = MM_VIDEO_CODEC_MPEG4; + break; + case AV_CODEC_ID_MSMPEG4V1: + ret_codecid = MM_VIDEO_CODEC_MPEG4; + break; + case AV_CODEC_ID_MSMPEG4V2: + ret_codecid = MM_VIDEO_CODEC_MPEG4; + break; + case AV_CODEC_ID_MSMPEG4V3: + ret_codecid = MM_VIDEO_CODEC_MPEG4; + break; + case AV_CODEC_ID_WMV1: + ret_codecid = MM_VIDEO_CODEC_WMV; + break; + case AV_CODEC_ID_WMV2: + ret_codecid = MM_VIDEO_CODEC_WMV; + break; + case AV_CODEC_ID_H263P: + ret_codecid = MM_VIDEO_CODEC_H263; + break; + case AV_CODEC_ID_H263I: + ret_codecid = MM_VIDEO_CODEC_H263; + break; + case AV_CODEC_ID_FLV1: + ret_codecid = MM_VIDEO_CODEC_FLV; + break; + case AV_CODEC_ID_H264: + ret_codecid = MM_VIDEO_CODEC_H264; + break; + case AV_CODEC_ID_INDEO2: + case AV_CODEC_ID_INDEO3: + case AV_CODEC_ID_INDEO4: + case AV_CODEC_ID_INDEO5: + ret_codecid = MM_VIDEO_CODEC_INDEO; + break; + case AV_CODEC_ID_THEORA: + ret_codecid = MM_VIDEO_CODEC_THEORA; + break; + case AV_CODEC_ID_CINEPAK: + ret_codecid = MM_VIDEO_CODEC_CINEPAK; + break; +#ifndef __MMFILE_FFMPEG_V085__ + case CODEC_ID_XVID: + ret_codecid = MM_VIDEO_CODEC_XVID; + break; +#endif + case AV_CODEC_ID_VC1: + ret_codecid = MM_VIDEO_CODEC_VC1; + break; + case AV_CODEC_ID_WMV3: + ret_codecid = MM_VIDEO_CODEC_WMV; + break; + case AV_CODEC_ID_AVS: + ret_codecid = MM_VIDEO_CODEC_AVS; + break; + case AV_CODEC_ID_RL2: + ret_codecid = MM_VIDEO_CODEC_REAL; + break; + default: + ret_codecid = MM_VIDEO_CODEC_NONE; + break; + } + + return ret_codecid; +} + + +static int ConvertAudioCodecEnum (int AVAudioCodecID) +{ + int ret_codecid = 0; + + switch (AVAudioCodecID) + { + case AV_CODEC_ID_AMR_NB: + case AV_CODEC_ID_AMR_WB: + ret_codecid = MM_AUDIO_CODEC_AMR; + break; + /* RealAudio codecs*/ + case AV_CODEC_ID_RA_144: + case AV_CODEC_ID_RA_288: + ret_codecid = MM_AUDIO_CODEC_REAL; + break; + case AV_CODEC_ID_MP2: + ret_codecid = MM_AUDIO_CODEC_MP2; + break; + case AV_CODEC_ID_MP3: + case AV_CODEC_ID_MP3ADU: + case AV_CODEC_ID_MP3ON4: + ret_codecid = MM_AUDIO_CODEC_MP3; + break; + case AV_CODEC_ID_AAC: + ret_codecid = MM_AUDIO_CODEC_AAC; + break; + case AV_CODEC_ID_AC3: + ret_codecid = MM_AUDIO_CODEC_AC3; + break; + case AV_CODEC_ID_VORBIS: + ret_codecid = MM_AUDIO_CODEC_VORBIS; + break; + case AV_CODEC_ID_WMAV1: + case AV_CODEC_ID_WMAV2: + case AV_CODEC_ID_WMAVOICE: + case AV_CODEC_ID_WMAPRO: + case AV_CODEC_ID_WMALOSSLESS: + ret_codecid = MM_AUDIO_CODEC_WMA; + break; + case AV_CODEC_ID_FLAC: + ret_codecid = MM_AUDIO_CODEC_FLAC; + break; + case AV_CODEC_ID_ALAC: + ret_codecid = MM_AUDIO_CODEC_ALAC; + break; + case AV_CODEC_ID_WAVPACK: + ret_codecid = MM_AUDIO_CODEC_WAVE; + break; + case AV_CODEC_ID_ATRAC3: + case AV_CODEC_ID_ATRAC3P: + case AV_CODEC_ID_EAC3: + ret_codecid = MM_AUDIO_CODEC_AC3; + break; + default: + ret_codecid = MM_AUDIO_CODEC_NONE; + break; + } + + return ret_codecid; +} + + + +static int getMimeType(int formatId, char *mimeType) +{ + int ret = 0; /*default: success*/ + + switch(formatId) { + case MM_FILE_FORMAT_3GP: + case MM_FILE_FORMAT_MP4: + sprintf(mimeType,"video/3gpp"); + break; + case MM_FILE_FORMAT_ASF: + case MM_FILE_FORMAT_WMA: + case MM_FILE_FORMAT_WMV: + sprintf(mimeType,"video/x-ms-asf"); + break; + case MM_FILE_FORMAT_AVI: + sprintf(mimeType,"video/avi"); + break; + case MM_FILE_FORMAT_OGG: + sprintf(mimeType,"video/ogg"); + break; + case MM_FILE_FORMAT_REAL: + sprintf(mimeType,"video/vnd.rn-realvideo"); + break; + case MM_FILE_FORMAT_AMR: + sprintf(mimeType,"audio/AMR"); + break; + case MM_FILE_FORMAT_AAC: + sprintf(mimeType,"audio/aac"); + break; + case MM_FILE_FORMAT_MP3: + sprintf(mimeType,"audio/mp3"); + break; + case MM_FILE_FORMAT_AIFF: + case MM_FILE_FORMAT_WAV: + sprintf(mimeType,"audio/wave"); + break; + case MM_FILE_FORMAT_MID: + sprintf(mimeType,"audio/midi"); + break; + case MM_FILE_FORMAT_MMF: + sprintf(mimeType,"audio/mmf"); + break; + case MM_FILE_FORMAT_DIVX: + sprintf(mimeType,"video/divx"); + break; + case MM_FILE_FORMAT_IMELODY: + sprintf(mimeType,"audio/iMelody"); + break; + case MM_FILE_FORMAT_JPG: + sprintf(mimeType,"image/jpeg"); + break; + case MM_FILE_FORMAT_AU: + sprintf(mimeType,"audio/basic"); + break; + case MM_FILE_FORMAT_VOB: + sprintf(mimeType,"video/mpeg"); + break; + case MM_FILE_FORMAT_FLV: + sprintf(mimeType,"video/x-flv"); + break; + case MM_FILE_FORMAT_QT: + sprintf(mimeType,"video/quicktime"); + break; + case MM_FILE_FORMAT_MATROSKA: + sprintf(mimeType,"video/x-matroska"); + break; + case MM_FILE_FORMAT_FLAC: + sprintf(mimeType,"audio/x-flac"); + break; + default: + ret = -1; + } + + debug_msg ("id: %d, mimetype: %s\n", formatId, mimeType); + + return ret; +} + + diff --git a/formats/ffmpeg/mm_file_format_ffmpeg_mem.c b/formats/ffmpeg/mm_file_format_ffmpeg_mem.c new file mode 100755 index 0000000..b117132 --- /dev/null +++ b/formats/ffmpeg/mm_file_format_ffmpeg_mem.c @@ -0,0 +1,248 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <libavformat/avformat.h> +#include <libavformat/avio.h> +#include "mm_debug.h" +#include "mm_file_utils.h" + + +typedef struct { + unsigned char *ptr; + long long size; + long long offset; + int state; +} MMFmemIOHandle; + +static int mmf_mem_open (URLContext *handle, const char *filename, int flags) +{ + MMFmemIOHandle *memHandle = NULL; + char **splitedString = NULL; + + + if (!handle || !filename || !handle->prot) + { + debug_error ("invalid param\n"); + return MMFILE_UTIL_FAIL; + } + + filename += strlen(handle->prot->name) + 3; /* ://%d:%d means (memory addr:mem size)*/ + + splitedString = mmfile_strsplit (filename, ":"); + if (splitedString == NULL) + { + debug_error ("invalid param\n"); + return MMFILE_UTIL_FAIL; + } + + if (!splitedString[0] || !splitedString[1]) + + { + debug_error ("invalid param\n"); + goto exception; + } + + memHandle = mmfile_malloc (sizeof(MMFmemIOHandle)); + if (!memHandle) + { + debug_error ("error: mmfile_malloc memHandle\n"); + goto exception; + } + + memHandle->ptr = (unsigned char *) atoll(splitedString[0]); //memory allocation address changed. memHandle->ptr = (unsigned char *) atoi(splitedString[0]); + memHandle->size = atoi(splitedString[1]); + memHandle->offset = 0; + memHandle->state = 0; + + handle->priv_data = (void*) memHandle; + + // Imp to reset them otherwise file seek will fail + handle->is_streamed = 0; /*FALSE*/ + handle->max_packet_size = 0; + + if (splitedString) + { + mmfile_strfreev (splitedString); + } + + return MMFILE_UTIL_SUCCESS; + +exception: + + if (splitedString) + { + mmfile_strfreev (splitedString); + } + +#if 0 //dead code + if (memHandle) + { + mmfile_free (memHandle); + handle->priv_data = NULL; + } +#endif + + return MMFILE_UTIL_FAIL; +} + +static int mmf_mem_read (URLContext *h, unsigned char *buf, int size) +{ + MMFmemIOHandle *memHandle = NULL; + const unsigned char *c = NULL; + int len = 0; + + + if (!h || !h->priv_data || !buf) + { + debug_error ("invalid para\n"); + return MMFILE_UTIL_FAIL; + } + + memHandle = h->priv_data; + + if (!memHandle->ptr) + { + debug_error ("invalid para\n"); + return MMFILE_UTIL_FAIL; + } + + if (memHandle->offset >= memHandle->size) { + // for some file formats last file read + debug_error ("File Read is beyond the file Size\n"); + return MMFILE_UTIL_FAIL; + + } + + c = memHandle->ptr + memHandle->offset; + + if (memHandle->state != EOF) + { + len = size; + if (len + memHandle->offset > memHandle->size) + { + len = memHandle->size - memHandle->offset; + } + } + + memcpy (buf, c, len); + + memHandle->offset += len; + + if ( memHandle->offset == memHandle->size) + { + memHandle->state = EOF; + } + + return len; +} + +static int mmf_mem_write (URLContext *h, unsigned char *buf, int size) +{ + if (!h || !h->priv_data || !buf) + { + debug_error ("invalid para\n"); + return MMFILE_UTIL_FAIL; + } + + debug_error ("NOTE PERMITTED\n"); + return MMFILE_UTIL_FAIL; +} + + +static long long mmf_mem_seek (URLContext *h, long long pos, int whence) +{ + MMFmemIOHandle *memHandle = NULL; + long long tmp_offset = 0; + + + if (!h || !h->priv_data) + { + debug_error ("invalid para\n"); + return MMFILE_UTIL_FAIL; + } + + memHandle = h->priv_data; + + switch (whence) + { + case SEEK_SET: + tmp_offset = 0 + pos; + break; + case SEEK_CUR: + tmp_offset = memHandle->offset + pos; + break; + case SEEK_END: + tmp_offset = memHandle->size + pos; + break; + case AVSEEK_SIZE: /*FFMPEG specific*/ + return memHandle->size; + default: + return MMFILE_UTIL_FAIL; + } + + /*check validation*/ + if (tmp_offset < 0 && tmp_offset > memHandle->size) + { + debug_error ("invalid file offset\n"); + return MMFILE_UTIL_FAIL; + } + + /*set */ + memHandle->state = (tmp_offset >= memHandle->size) ? EOF : !EOF; + memHandle->offset = (unsigned int) tmp_offset; + + return tmp_offset; +} + +static int mmf_mem_close (URLContext *h) +{ + MMFmemIOHandle *memHandle = NULL; + + if (!h || !h->priv_data) + { + debug_error ("invalid para\n"); + return MMFILE_UTIL_FAIL; + } + + memHandle = h->priv_data; + + if (memHandle) + { + mmfile_free (memHandle); + h->priv_data = NULL; + } + + return MMFILE_UTIL_SUCCESS; +} + + +URLProtocol MMFileMEMProtocol = { + .name = "mem", + .url_open = mmf_mem_open, + .url_read = mmf_mem_read, + .url_write = mmf_mem_write, + .url_seek = mmf_mem_seek, + .url_close = mmf_mem_close, +}; diff --git a/formats/ffmpeg/mm_file_format_frame.c b/formats/ffmpeg/mm_file_format_frame.c new file mode 100755 index 0000000..7d7a8f0 --- /dev/null +++ b/formats/ffmpeg/mm_file_format_frame.c @@ -0,0 +1,357 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include <stdbool.h> +#include <libavformat/avformat.h> +#include <libavcodec/avcodec.h> +#include <libswscale/swscale.h> + +#include "mm_debug.h" +#include "mm_file_formats.h" +#include "mm_file_utils.h" +#include "mm_file_format_frame.h" + +#define MILLION 1000000 +#ifdef MMFILE_FORMAT_DEBUG_DUMP +static void _save_pgm (unsigned char *buf,int wrap, int xsize,int ysize,char *filename); + +static void _save_pgm (unsigned char *buf,int wrap, int xsize,int ysize,char *filename) +{ + FILE *f; + int i; + + f = fopen(filename,"w"); + if (f) { + fprintf (f,"P5\n%d %d\n%d\n",xsize,ysize,255); + for (i = 0; i < ysize; i++) + fwrite (buf + i * wrap, 1, xsize, f); + fclose (f); + } +} +#endif + +int mmfile_format_get_frame(const char* path, double timestamp, bool is_accurate, unsigned char **frame, int *size, int *width, int *height) +{ + int i = 0; + int ret = MMFILE_FORMAT_SUCCESS; + int videoStream = -1; + int frameFinished = 0; + double pos = timestamp; + bool find = false ; + bool first_seek = true; + int64_t pts = 0; + AVFormatContext *pFormatCtx = NULL; + AVCodecContext *pVideoCodecCtx = NULL; + AVCodec *pVideoCodec = NULL; + AVFrame *pFrame = NULL, *pFrameRGB = NULL; + AVPacket packet; + int len = 0; + int key_detected = 0; + + if (!size || !width || !height) { + return MMFILE_FORMAT_FAIL; + } + + av_register_all(); + + /* Open video file */ + if(avformat_open_input(&pFormatCtx, path, NULL, NULL) != 0) { + debug_error("error : avformat_open_input failed"); + return MMFILE_FORMAT_FAIL; /* Couldn't open file */ + } + + if (!pFormatCtx) { + debug_warning ("failed to find av stream. maybe corrupted data.\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + /* Retrieve stream information */ +#ifdef __MMFILE_FFMPEG_V100__ + if(avformat_find_stream_info(pFormatCtx, NULL) < 0) { +#else + if(av_find_stream_info(pFormatCtx) < 0) { +#endif + debug_error("error : av_find_stream_info failed"); + ret = MMFILE_FORMAT_FAIL; + goto exception; /* Couldn't find stream information */ + } + + /* Find the first video stream */ + for(i = 0; i < pFormatCtx->nb_streams; i++) { + if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + videoStream = i; + break; + } + } + + if(videoStream == -1) { + debug_error("error : videoStream == -1"); + ret = MMFILE_FORMAT_FAIL; + goto exception; /* Didn't find a video stream */ + } + + /* Get a pointer to the codec context for the video stream */ + pVideoCodecCtx=pFormatCtx->streams[videoStream]->codec; + if (pVideoCodecCtx == NULL) { + debug_error ("invalid param\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + /* Find the decoder for the video stream */ + pVideoCodec = avcodec_find_decoder(pVideoCodecCtx->codec_id); + if(pVideoCodec == NULL) { + debug_error("error : Unsupported codec"); + ret = MMFILE_FORMAT_FAIL; + goto exception; /* Codec not found */ + } + + /* Open codec */ +#ifdef __MMFILE_FFMPEG_V100__ + if(avcodec_open2(pVideoCodecCtx, pVideoCodec, NULL) < 0) { +#else + if(avcodec_open(pVideoCodecCtx, pVideoCodec) < 0) { +#endif + debug_error("error : avcodec_open failed"); + ret = MMFILE_FORMAT_FAIL; + goto exception;; /*Could not open codec */ + } + + /* Storing Data */ + /* Allocate video frame */ + pFrame = avcodec_alloc_frame(); + if(pFrame == NULL) { + debug_error ("error: pFrame is NULL\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + /* Allocate an AVFrame structure */ + pFrameRGB = avcodec_alloc_frame(); + if(pFrameRGB == NULL) { + debug_error ("error: pFrameRGB is NULL\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + /* Seeking */ + AVStream *pStream = pFormatCtx->streams[videoStream]; + double duration = (double) pFormatCtx->duration / AV_TIME_BASE; +#if 0 + if (duration <= 0) { + double tmpDuration = 0.0; + + if (pStream->codec->bit_rate > 0 && pFormatCtx->file_size > 0) { + if (pStream->codec->bit_rate >= 8) + tmpDuration = 0.9 * pFormatCtx->file_size / (pStream->codec->bit_rate / 8); + + if (tmpDuration > 0) + duration = tmpDuration; + } + } +#endif + duration = duration * MILLION; + if ((duration <= 0) ||(duration <= pos)) { + debug_error("duration error duration[%f] pos[%f]", duration, pos); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + if (is_accurate) + av_seek_frame(pFormatCtx, -1, pos, AVSEEK_FLAG_ANY); + else + av_seek_frame(pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); + + /* Reading Data */ + int64_t tmpPts = 0; +#ifdef __MMFILE_TEST_MODE__ + int idx = 0; +#endif + + av_init_packet(&packet); + + while(av_read_frame(pFormatCtx, &packet) >= 0) { + frameFinished = 0; + + // Is this a packet from the video stream? + if(packet.stream_index == videoStream) { +#ifdef __MMFILE_TEST_MODE__ + debug_msg("find Video Stream+++++++[%2d]", idx++); +#endif + /* Decode video frame*/ + len = avcodec_decode_video2(pVideoCodecCtx, pFrame, &frameFinished, &packet); + if (len < 0) { + debug_warning ("Error while decoding frame"); + } else if ((packet.flags & AV_PKT_FLAG_KEY) || (key_detected == 1)) { + + key_detected = 0; + + if (first_seek || !is_accurate) { + /* This is first seeking or not accurate mode. + Sometimes flag is AV_PKT_FLAG_KEY but frameFinished is NULL. + first_seek is used when accurate mode and when time stamp's frame is not key frame. + Go back to previousto Key frame and decode frame until time stamp's frame*/ + + if (frameFinished) { + if(pFrame->key_frame) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg("find Video Stream+++++++Find key frame"); + #endif + + find = true; + } else { + #ifdef __MMFILE_TEST_MODE__ + debug_msg("find Video Stream+++++++skip (not key frame)"); + #endif + } + } else { + #ifdef __MMFILE_TEST_MODE__ + debug_msg("find Video Stream+++++++Find key but no frame"); + #endif + key_detected = 1; + } + } + } else { + if (first_seek) { + pts = (packet.pts == AV_NOPTS_VALUE) ? (packet.dts * av_q2d(pStream->time_base)) : packet.pts; + first_seek = false; + + av_seek_frame(pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); + } else { + tmpPts = (packet.pts == AV_NOPTS_VALUE) ? (packet.dts * av_q2d(pStream->time_base)) : packet.pts; + if (pts == tmpPts) + find = true; + } + } + + if(find && frameFinished) { + #ifdef MMFILE_FORMAT_DEBUG_DUMP + char pgm_name[256] = {0,}; + sprintf (pgm_name, "./key_%d.ppm", (int)pos/1000); + _save_pgm (pFrame->data[0], pFrame->linesize[0], pVideoCodecCtx->width, pVideoCodecCtx->height, pgm_name); + #endif + break; + } + } + + /* Free the packet that was allocated by av_read_frame*/ + av_free_packet(&packet); + av_init_packet(&packet); + } + + /*free pkt after loop breaking*/ + av_free_packet (&packet); + + /* Did we get a video frame?*/ + if(frameFinished && find) { + +#ifdef __MMFILE_TEST_MODE__ + debug_msg("Find Frame"); +#endif + /* return frame infromations*/ + if((pVideoCodecCtx->width == 0) || (pVideoCodecCtx->height == 0)) { + *width = pVideoCodecCtx->coded_width; + *height = pVideoCodecCtx->coded_height; + } else { + *width = pVideoCodecCtx->width; + *height = pVideoCodecCtx->height; + } + + *size = avpicture_get_size(PIX_FMT_RGB24, *width, *height); + *frame = mmfile_malloc (*size); + if (NULL == *frame) { + debug_error ("error: avpicture_get_size. [%d]\n", size); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + +#ifdef __MMFILE_TEST_MODE__ + debug_msg("size : %d", *size); + debug_msg("width : %d", *width); + debug_msg("height : %d", *height); + debug_msg("frame : %x", *frame); +#endif + ret = avpicture_fill ((AVPicture *)pFrameRGB, *frame, PIX_FMT_RGB24, *width, *height); + if (ret < 0) { + debug_error ("error: avpicture_fill fail. errcode = 0x%08X\n", ret); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + +#ifdef __MMFILE_FFMPEG_V085__ + struct SwsContext *img_convert_ctx = NULL; + + img_convert_ctx = sws_getContext (*width, *height, pVideoCodecCtx->pix_fmt, + *width, *height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL); + + if (NULL == img_convert_ctx) { + debug_error ("failed to get img convet ctx\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + ret = sws_scale (img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, + 0, *height, pFrameRGB->data, pFrameRGB->linesize); + if ( ret < 0 ) { + debug_error ("failed to convet image\n"); + sws_freeContext(img_convert_ctx); + img_convert_ctx = NULL; + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + sws_freeContext(img_convert_ctx); + img_convert_ctx = NULL; +#else + ret = img_convert ((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, pVideoCodecCtx->pix_fmt, *width, *height); + if ( ret < 0 ) { + debug_error ("failed to convet image\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } +#endif + + } + else + { + debug_error("Not Found Proper Frame[%d][%d]", frameFinished, find); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + if (pFrame) av_free (pFrame); + if (pFrameRGB) av_free (pFrameRGB); + if (pVideoCodecCtx) avcodec_close(pVideoCodecCtx); + /* Close video file */ + if (pFormatCtx) avformat_close_input(&pFormatCtx); + + return MMFILE_FORMAT_SUCCESS; + +exception: + if (*frame) { mmfile_free (*frame); *frame = NULL; } + if (pFrame) av_free (pFrame); + if (pFrameRGB) av_free (pFrameRGB); + if (pVideoCodecCtx) avcodec_close (pVideoCodecCtx); + /* Close video file */ + if (pFormatCtx) avformat_close_input(&pFormatCtx); + + return ret; + } diff --git a/formats/ffmpeg/mm_file_format_imelody.c b/formats/ffmpeg/mm_file_format_imelody.c new file mode 100755 index 0000000..5baefbd --- /dev/null +++ b/formats/ffmpeg/mm_file_format_imelody.c @@ -0,0 +1,1699 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <stdio.h> +#include <string.h> /*memcmp*/ +#include <stdlib.h> /*malloc*/ + +#include "mm_debug.h" +#include "mm_file_utils.h" +#include "mm_file_format_private.h" +#include "mm_file_format_imelody.h" +#include "mm_file_format_midi.h" + +/** + * Define + */ +#define AV_MIDI_COUNT_MAX 1600 +#define AV_MIDI_NOTE_MAX 256 +#define AV_MIDI_VOL_MAX 250 +#define MIDI_LIMIT 127 +#define MIDI_MAX 255 + +#define VOL_INTERVAL 12 + +#define MIDI_HEADER_LENGTH 52 + +static unsigned char midiData[AV_MIDI_COUNT_MAX] ; +static unsigned char midiHeader[MIDI_HEADER_LENGTH]= {0x4d,0x54,0x68,0x64,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x30,0x4d,0x54, + 0x72,0x6b,0x00,0x00,0x00,0x00,0x00,0xff,0x58,0x04,0x04,0x02,0x18,0x08,0x00,0xff, + 0x59,0x02,0x00,0x00,0x00,0xff,0x51,0x03,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0xb0, + 0x07,0x00,0x00,0x90}; + + +static unsigned char noteTotal[AV_MIDI_COUNT_MAX]; +static char octave[AV_MIDI_NOTE_MAX]; +static int durationSpec[AV_MIDI_NOTE_MAX]; +static int restSpec[AV_MIDI_NOTE_MAX]; + +static struct { + char flat_sharp; + char note; + char duration; + char duration_specifier; + char rest; + char rest_specifier; + char vol; +} Melody[AV_MIDI_NOTE_MAX]; + +static struct { + int note; + int duration_on; + int duration_off; +} noteData[AV_MIDI_NOTE_MAX]; + +/*imelody key string (to validatation check)*/ +static const char *g_imy_key_str [] = { + "BEGIN:IMELODY", + "VERSION:", + "FORMAT:", + "MELODY:", + "END:IMELODY", +}; + + +static int __is_good_imelody (unsigned char *src, unsigned int size); +static unsigned char* __get_load_memory (char *src, int *out_size); +static unsigned char __AvMIDISetVolume(char* pMelodyBuf); +static int __AvMIDISetBeat(char* pMelodyBuf); +static char __AvMIDISetStyle(char* pMelodyBuf); +static unsigned char* __AvConvertIMelody2MIDI(char* pMelodyBuf, unsigned int* pBufLen); +static int __get_imelody_tag (const char *uriname, tMMFileImelodyTagInfo *tags); + + +/* interface functions */ +int mmfile_format_read_stream_imy (MMFileFormatContext *formatContext); +int mmfile_format_read_frame_imy (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame); +int mmfile_format_read_tag_imy (MMFileFormatContext *formatContext); +int mmfile_format_close_imy (MMFileFormatContext *formatContext); + + +EXPORT_API +int mmfile_format_open_imy (MMFileFormatContext *formatContext) +{ + int ret = 0; + + if (!formatContext) { + debug_error("formatContext is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + if (formatContext->pre_checked == 0) { + ret = MMFileFormatIsValidIMY (formatContext->uriFileName); + if (ret == 0) { + debug_error("It is not imelody file\n"); + return MMFILE_FORMAT_FAIL; + } + } + + formatContext->ReadStream = mmfile_format_read_stream_imy; + formatContext->ReadFrame = mmfile_format_read_frame_imy; + formatContext->ReadTag = mmfile_format_read_tag_imy; + formatContext->Close = mmfile_format_close_imy; + + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = 1; + + formatContext->privateFormatData = NULL; + + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_read_stream_imy (MMFileFormatContext *formatContext) +{ + MIDI_INFO_SIMPLE *info = NULL; + MMFileFormatStream *audioStream = NULL; + + unsigned char *imy = NULL; + int imy_size = 0; + unsigned char *midi = NULL; + unsigned int midi_size = 0; + char src2[MMFILE_URI_MAX_LEN]; + + int ret = 0; + + if (!formatContext) { + debug_error("formatContext is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + /*convert iMelody to Midi*/ + imy = __get_load_memory (formatContext->uriFileName, &imy_size); + if (!imy) { + debug_error ("failed to load memory.\n"); + goto exception; + } + ret = __is_good_imelody (imy, imy_size); + if (ret != MMFILE_FORMAT_SUCCESS) { + debug_error ("it's broken file.\n"); + goto exception; + } + midi = __AvConvertIMelody2MIDI ((char *)imy, &midi_size); + if (!midi) { + debug_error ("failed to convert."); + goto exception; + } + + /*make uri*/ + memset (src2, 0x00, MMFILE_URI_MAX_LEN); + sprintf (src2, "%s%u:%u", MMFILE_MEM_URI, (unsigned int)midi, midi_size); + + /*get infomation*/ + info = mmfile_format_get_midi_infomation (src2); + if (!info) { + debug_error ("failed to get infomation"); + goto exception; + } + + formatContext->duration = info->duration; + formatContext->videoStreamId = -1; + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = info->track_num; + formatContext->nbStreams = 1; + + + audioStream = mmfile_malloc (sizeof(MMFileFormatStream)); + if (NULL == audioStream) { + debug_error ("error: mmfile_malloc audiostream\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + audioStream->streamType = MMFILE_AUDIO_STREAM; + audioStream->codecId = MM_AUDIO_CODEC_IMELODY; + audioStream->bitRate = 0; + audioStream->framePerSec = 0; + audioStream->width = 0; + audioStream->height = 0; + audioStream->nbChannel = 1; + audioStream->samplePerSec = 0; + formatContext->streams[MMFILE_AUDIO_STREAM] = audioStream; + + #ifdef __MMFILE_TEST_MODE__ + mmfile_format_print_contents (formatContext); + #endif + + mmfile_free (imy); + mmfile_free (midi); + mmfile_format_free_midi_infomation (info); + return MMFILE_FORMAT_SUCCESS; + +exception: + mmfile_free (imy); + mmfile_free (midi); + mmfile_format_free_midi_infomation (info); + mmfile_free (audioStream); + return MMFILE_FORMAT_FAIL; +} + +EXPORT_API +int mmfile_format_read_frame_imy (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame) +{ + return MMFILE_FORMAT_SUCCESS; +} + + +EXPORT_API +int mmfile_format_read_tag_imy (MMFileFormatContext *formatContext) +{ + tMMFileImelodyTagInfo taginfo = {0,}; + unsigned int tag_len; + unsigned int cnv_len; + const char *locale = MMFileUtilGetLocale (NULL); + + if (!formatContext) { + debug_error("formatContext is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + __get_imelody_tag (formatContext->uriFileName, &taginfo); + + /** + * UTF8 convert + */ + if (taginfo.title) { + tag_len = strlen (taginfo.title); + cnv_len = 0; + formatContext->title = mmfile_string_convert ((const char*)taginfo.title, + tag_len, + "UTF-8", + locale, + NULL, + (unsigned int*)&cnv_len); + + if (formatContext->title == NULL) { + debug_warning ("failed to UTF8 convert.\n"); + formatContext->title = mmfile_strdup (taginfo.title); + } + mmfile_free (taginfo.title); + } + + if (taginfo.composer) { + tag_len = strlen (taginfo.composer); + cnv_len = 0; + formatContext->composer = mmfile_string_convert ((const char*)taginfo.composer, + tag_len, + "UTF-8", + locale, + NULL, + (unsigned int*)&cnv_len); + if (formatContext->composer == NULL) { + debug_warning ("failed to UTF8 convert.\n"); + formatContext->composer = mmfile_strdup (taginfo.composer); + } + mmfile_free (taginfo.composer); + } + + if (taginfo.comment) { + tag_len = strlen (taginfo.comment); + cnv_len = 0; + formatContext->comment = mmfile_string_convert ((const char*)taginfo.comment, + tag_len, + "UTF-8", + locale, + NULL, + (unsigned int*)&cnv_len); + if (formatContext->comment == NULL) { + debug_warning ("failed to UTF8 convert.\n"); + formatContext->comment = mmfile_strdup (taginfo.comment); + } + mmfile_free (taginfo.comment); + } + + if (taginfo.copyright) { + tag_len = strlen (taginfo.copyright); + cnv_len = 0; + formatContext->copyright = mmfile_string_convert ((const char*)taginfo.copyright, + tag_len, + "UTF-8", + locale, + NULL, + (unsigned int*)&cnv_len); + if (formatContext->copyright == NULL) { + debug_warning ("failed to UTF8 convert.\n"); + formatContext->copyright = mmfile_strdup (taginfo.copyright); + } + mmfile_free (taginfo.copyright); + } + + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_close_imy (MMFileFormatContext *formatContext) +{ + return MMFILE_FORMAT_SUCCESS; +} + +static int __get_imelody_tag (const char *uriname, tMMFileImelodyTagInfo *tags) +{ +#define _MMFILE_IMY_TAG_BUFFER_LENGTH 512 +#define _MMFILE_IMY_HEADER_LENGTH 20 +#define _MMFILE_IMY_KEY_BUFFER_LENGTH 20 +#define _MMFILE_IMY_VALUE_BUFFER_LENGTH 128 + + MMFileIOHandle *fp = NULL; + unsigned char buffer[_MMFILE_IMY_TAG_BUFFER_LENGTH] = {0,}; + int readed = 0; + long long filesize = 0; + unsigned int startoffset = 0; + unsigned int endoffset = 0; + unsigned int i = 0, j = 0; + char imy_key_buffer[_MMFILE_IMY_KEY_BUFFER_LENGTH] = {0,}; + unsigned int imy_key_buffer_index = 0; + char imy_value_buffer[_MMFILE_IMY_VALUE_BUFFER_LENGTH] = {0,}; + unsigned int imy_value_buffer_index = 0; + int isKeyBuffer = 1; + int isDone = 0; + + int ret = MMFILE_FORMAT_FAIL; + + if (!uriname || !tags) + { + debug_error("uriname or tags is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + ret = mmfile_open (&fp, uriname, MMFILE_RDONLY); + if (ret == MMFILE_UTIL_FAIL) + { + debug_error ( "open failed.\n"); + return MMFILE_FORMAT_FAIL; + } + + mmfile_seek (fp, 0, MMFILE_SEEK_END); + filesize = mmfile_tell (fp); + mmfile_seek (fp, 0, MMFILE_SEEK_SET); + + if (filesize < _MMFILE_IMY_HEADER_LENGTH) + { + debug_error ( "header is too small.\n"); + ret = MMFILE_FORMAT_FAIL; + goto exit; + } + + /* set begin and end point at the file */ + startoffset = 0; + endoffset = filesize; + + i = startoffset; + isKeyBuffer = 1; + while (i < endoffset) + { + mmfile_seek (fp, i, MMFILE_SEEK_SET); + readed = mmfile_read (fp, buffer, _MMFILE_IMY_TAG_BUFFER_LENGTH); + if (readed < 0) + { + debug_error ( "read error. size = %d. Maybe end of file.\n", readed); + ret = 0; + break; + } + + j = 0; + while (j < readed) + { + if ( *(buffer+j) == 0x3a ) + { + isKeyBuffer = 0; + } + else if ( *(buffer+j) == 0x0d ) + { + } + else if ( *(buffer+j) == 0x0a ) + { + isKeyBuffer = 1; + isDone = 1; + } + else + { + if (isKeyBuffer) + { + if (imy_key_buffer_index < _MMFILE_IMY_KEY_BUFFER_LENGTH) + { + imy_key_buffer[imy_key_buffer_index++] = *(buffer+j); + } + + } + else + { + if (imy_value_buffer_index < _MMFILE_IMY_VALUE_BUFFER_LENGTH) + { + imy_value_buffer[imy_value_buffer_index++] = *(buffer+j); + } + } + } + + if (isDone) + { + if (!strncmp (imy_key_buffer, "NAME", 4)) + { + tags->title = mmfile_strdup (imy_value_buffer); + } + else if (!strncmp (imy_key_buffer, "COMPOSER", 8)) + { + tags->composer = mmfile_strdup (imy_value_buffer); + } + else if (!strncmp (imy_key_buffer, "COPYRIGHT", 9)) + { + tags->copyright = mmfile_strdup (imy_value_buffer); + } + + memset (imy_key_buffer, 0x00, _MMFILE_IMY_KEY_BUFFER_LENGTH); + memset (imy_value_buffer, 0x00, _MMFILE_IMY_VALUE_BUFFER_LENGTH); + imy_key_buffer_index = 0; + imy_value_buffer_index = 0; + isDone = 0; + } + + j++; + } + + memset (buffer, 0x00, _MMFILE_IMY_TAG_BUFFER_LENGTH); + + i = i + j; + } + +exit: + mmfile_close (fp); + return ret; +} + +static unsigned char * +__get_load_memory (char *src, int *out_size) +{ + unsigned char *buf = NULL; + MMFileIOHandle *fp = NULL; + long long src_size = 0L; + int readed =0; + int ret = 0; + + /*open*/ + ret = mmfile_open (&fp, src, MMFILE_RDONLY); + if (ret == MMFILE_UTIL_FAIL) { + debug_error ( "open failed.\n"); + return NULL; + } + + /*get file size*/ + mmfile_seek (fp, 0L, MMFILE_SEEK_END); + src_size = mmfile_tell (fp); + mmfile_seek (fp, 0L, MMFILE_SEEK_SET); + + if (src_size <= 0) { + debug_error ("failed to get file size.\n"); + goto failed; + } + + /*alloc read buffer*/ + if ((buf = mmfile_malloc (src_size)) == NULL) { + debug_error ( "memory allocation failed.\n"); + goto failed; + } + + /*read data*/ + if ((readed = mmfile_read (fp, buf, src_size) ) != src_size) { + debug_error ( "read error. size = %d\n", readed); + goto failed; + } + + *out_size = (int)src_size; + mmfile_close (fp); + + return buf; + +failed: + if (buf) mmfile_free (buf); + if (fp) mmfile_close (fp); + return NULL; +} + + +static int +__is_good_imelody (unsigned char *src, unsigned int size) +{ + int i, j; + int key_len; + int is_found; + unsigned char *p; + int num = sizeof (g_imy_key_str) / sizeof (g_imy_key_str[0]); + + for (i = 0; i < num; i++) { + key_len = strlen (g_imy_key_str[i]); + p = src; + is_found = 0; + for (j = 0; j <= size - key_len; j++, p++) { + if (memcmp (g_imy_key_str[i], p, key_len) == 0) { + is_found = 1; + break; + } + } + if (is_found) continue; else return MMFILE_FORMAT_FAIL; + } + + return MMFILE_FORMAT_SUCCESS; +} + + +static unsigned char* +__AvConvertIMelody2MIDI(char* pMelodyBuf, unsigned int* pBufLen) +{ + unsigned char* pConvertBuf; + char* pStart; + char* pMelodyStart; + char noteBase[6]; + int octaveCount; + int octaveValue; + int count; + int noteCount = 0; + int MelodyCount; + int number; + int numberCount; + char style = '0'; + int tempoData[3]; + int tempoValue; + int trackSize; + int repeat = 0; + int repeatCount = 0; + char vol = '%'; + int volInterval = 0; + + for(count = 0;count < AV_MIDI_NOTE_MAX;count++) + { + restSpec[count] = 0; + durationSpec[count] = 0; + } + + for(octaveCount = 0;octaveCount < AV_MIDI_NOTE_MAX;octaveCount++) + octave[octaveCount] = '%'; + + for(MelodyCount = 0;MelodyCount < AV_MIDI_NOTE_MAX;MelodyCount++) + { + Melody[MelodyCount].flat_sharp = '%'; + Melody[MelodyCount].note = '%'; + Melody[MelodyCount].duration = '%'; + Melody[MelodyCount].duration_specifier = '%'; + Melody[MelodyCount].rest = '%'; + Melody[MelodyCount].rest_specifier = '%'; + Melody[MelodyCount].vol = '%'; + } + + for(MelodyCount = 0;MelodyCount < AV_MIDI_NOTE_MAX;MelodyCount++) + { + noteData[MelodyCount].note = 0; + noteData[MelodyCount].duration_on = 0; + noteData[MelodyCount].duration_off = 0; + } + + + memset(midiData, 0, sizeof(midiData)); + memcpy(midiData, midiHeader, MIDI_HEADER_LENGTH); + + pStart = pMelodyBuf; + + midiData[49] = __AvMIDISetVolume(pMelodyBuf); + + pMelodyBuf = pStart; + + tempoValue = __AvMIDISetBeat(pMelodyBuf); + + for(number = 0;tempoValue != 0;number++) + { + tempoData[0] = tempoValue % 16; + tempoValue = tempoValue / 16; + + tempoData[1] = tempoValue % 16; + tempoValue = tempoValue / 16; + + tempoData[2] = tempoData[0] + tempoData[1] * 16; + + midiData[42-number] = tempoData[2]; + } + + pMelodyBuf = pStart; + + while(!(*pMelodyBuf == '@' || (*pMelodyBuf == 'E' && *(pMelodyBuf+2) == 'D'))) + pMelodyBuf++; + + pMelodyBuf++; + + if(*pMelodyBuf >= '1' && *pMelodyBuf <= '9') + { + repeat = *pMelodyBuf - '0'; + pMelodyBuf++; + + } + + repeat = 0; + + pMelodyBuf = pStart; + + pMelodyBuf = pMelodyBuf + 42; + + while(!(*pMelodyBuf == 'M' && *(pMelodyBuf+5) == 'Y' && *(pMelodyBuf+6) == ':')) //2007-02-28 AVMS_Sound:k2bogus - UMTS200073205;imy play, [MELODY:] extract fix + pMelodyBuf++; + + pMelodyBuf = pMelodyBuf + 6; + + pMelodyStart = pMelodyBuf; + + /**@note if newline detected, stop reading + * why? mobileBAE player stopped at newline. + * 2009/08/12 + */ + while(!((*pMelodyBuf == 'E' && *(pMelodyBuf+2) == 'D') || (*pMelodyBuf == '\n'))) + { + if(noteCount>=AV_MIDI_NOTE_MAX) + { + debug_warning ("__AvConvertIMelody2MIDI : noteCount>=AV_MIDI_NOTE_MAX\n"); + break; + } + + pMelodyBuf++; + + if(*pMelodyBuf == '*') + { + pMelodyBuf++; + + if(*pMelodyBuf >= '0' && *pMelodyBuf <= '8') + octave[noteCount] = *pMelodyBuf; + } + + if(*pMelodyBuf == '#' || *pMelodyBuf == '&') + Melody[noteCount].flat_sharp = *pMelodyBuf; + + if(*pMelodyBuf == 'r') + { + pMelodyBuf++; + + if(*pMelodyBuf >= '0' && *pMelodyBuf <= '5') + { + Melody[noteCount].rest = *pMelodyBuf; + pMelodyBuf++; + + if(*pMelodyBuf == '.' || *pMelodyBuf == ':' || *pMelodyBuf == ';') + Melody[noteCount].rest_specifier = *pMelodyBuf; + } + } + + if(*pMelodyBuf == 'V') + { + pMelodyBuf++; + + if(*pMelodyBuf == '+' || *pMelodyBuf == '-') + Melody[noteCount].vol = *pMelodyBuf; + } + + if(*pMelodyBuf >= 'a' && *pMelodyBuf <= 'g') + { + Melody[noteCount].note = *pMelodyBuf; + pMelodyBuf++; + + if(*pMelodyBuf >= '0' && *pMelodyBuf <= '5') + { + Melody[noteCount].duration = *pMelodyBuf; + pMelodyBuf++; + + if(*pMelodyBuf == '.' || *pMelodyBuf == ':' || *pMelodyBuf == ';') + Melody[noteCount].duration_specifier = *pMelodyBuf; + + else + pMelodyBuf--; + + noteCount++; + } + } + } + + for(octaveCount = 1;octaveCount < noteCount;octaveCount++) + { + if(octave[octaveCount] == '%') + octave[octaveCount] = octave[octaveCount-1]; + } + + for(number = 0;number < noteCount;number++) + { + if(octave[0] == '%' && octave[number] == '%') + octaveValue = 4; + + else + octaveValue = octave[number] - '0'; + + octaveValue = octaveValue * 12; + + if(Melody[number].flat_sharp == '#') + { + switch(Melody[number].note) + { + case 'c': noteData[number].note = octaveValue + 1; + break; + case 'd': noteData[number].note = octaveValue + 3; + break; + case 'f': noteData[number].note = octaveValue + 6; + break; + case 'g': noteData[number].note = octaveValue + 8; + break; + case 'a': noteData[number].note = octaveValue + 10; + } + } + + else + if(Melody[number].flat_sharp == '&') + { + switch(Melody[number].note) + { + case 'd': noteData[number].note = octaveValue + 1; + break; + case 'e': noteData[number].note = octaveValue + 3; + break; + case 'g': noteData[number].note = octaveValue + 6; + break; + case 'a': noteData[number].note = octaveValue + 8; + break; + case 'b': noteData[number].note = octaveValue + 10; + } + } + + else + { + switch(Melody[number].note) + { + case 'c': noteData[number].note = octaveValue; + break; + case 'd': noteData[number].note = octaveValue + 2; + break; + case 'e': noteData[number].note = octaveValue + 4; + break; + case 'f': noteData[number].note = octaveValue + 5; + break; + case 'g': noteData[number].note = octaveValue + 7; + break; + case 'a': noteData[number].note = octaveValue + 9; + break; + case 'b': noteData[number].note = octaveValue + 11; + } + } + } + + pMelodyBuf = pMelodyStart; + + style = __AvMIDISetStyle(pMelodyBuf); + + for(number = 0;number < noteCount;number++) + { + if(style == '0') + { + switch(Melody[number].duration) + { + case '0': noteData[number].duration_on = 183; + noteData[number].duration_off = 9; + break; + case '1': noteData[number].duration_on = 91; + noteData[number].duration_off = 5; + break; + case '2': noteData[number].duration_on = 46; + noteData[number].duration_off = 2; + break; + case '3': noteData[number].duration_on = 23; + noteData[number].duration_off = 1; + break; + case '4': noteData[number].duration_on = 11; + noteData[number].duration_off = 1; + break; + case '5': noteData[number].duration_on = 5; + noteData[number].duration_off = 1; + } + } + + else + if(style == '1') + { + switch(Melody[number].duration) + { + case '0': noteData[number].duration_on = 192; + noteData[number].duration_off = 0; + break; + case '1': noteData[number].duration_on = 96; + noteData[number].duration_off = 0; + break; + case '2': noteData[number].duration_on = 48; + noteData[number].duration_off = 0; + break; + case '3': noteData[number].duration_on = 24; + noteData[number].duration_off = 0; + break; + case '4': noteData[number].duration_on = 12; + noteData[number].duration_off = 0; + break; + case '5': noteData[number].duration_on = 6; + noteData[number].duration_off = 0; + } + } + + else + { + switch(Melody[number].duration) + { + case '0': noteData[number].duration_on = 96; + noteData[number].duration_off = 96; + break; + case '1': noteData[number].duration_on = 48; + noteData[number].duration_off = 48; + break; + case '2': noteData[number].duration_on = 24; + noteData[number].duration_off = 24; + break; + case '3': noteData[number].duration_on = 12; + noteData[number].duration_off = 12; + break; + case '4': noteData[number].duration_on = 6; + noteData[number].duration_off = 6; + break; + case '5': noteData[number].duration_on = 3; + noteData[number].duration_off = 3; + } + } + + switch(Melody[number].duration) + { + case '0': durationSpec[number] = 192; + break; + case '1': durationSpec[number] = 96; + break; + case '2': durationSpec[number] = 48; + break; + case '3': durationSpec[number] = 24; + break; + case '4': durationSpec[number] = 12; + break; + case '5': durationSpec[number] = 6; + } + + if(Melody[number].duration_specifier != '%') + { + switch(Melody[number].duration_specifier) + { + case '.': noteData[number].duration_on += (durationSpec[number] / 2); + break; + case ':': noteData[number].duration_on += durationSpec[number]; + break; + case ';': noteData[number].duration_on -= (durationSpec[number] / 3); + } + + if(noteData[number].duration_on > MIDI_MAX) + noteData[number].duration_on = MIDI_LIMIT; + } + } + + for(number = 1;number < noteCount;number++) + { + if(Melody[number].rest >= '0' && Melody[number].rest <= '5') + { + switch(Melody[number].rest) + { + case '0': noteData[number-1].duration_off += 192; + restSpec[number] = 192; + break; + case '1': noteData[number-1].duration_off += 96; + restSpec[number] = 96; + break; + case '2': noteData[number-1].duration_off += 48; + restSpec[number] = 48; + break; + case '3': noteData[number-1].duration_off += 24; + restSpec[number] = 24; + break; + case '4': noteData[number-1].duration_off += 12; + restSpec[number] = 12; + break; + case '5': noteData[number-1].duration_off += 6; + restSpec[number] = 6; + } + + if(noteData[number-1].duration_off > MIDI_MAX && Melody[number].rest_specifier == '%') + noteData[number-1].duration_off = MIDI_LIMIT; + } + + if(Melody[number].rest_specifier != '%') + { + switch(Melody[number].rest_specifier) + { + case '.': noteData[number-1].duration_off += (restSpec[number] / 2); + break; + case ':': noteData[number-1].duration_off += restSpec[number]; + break; + case ';': noteData[number-1].duration_off -= (restSpec[number] / 3); + } + + if(noteData[number-1].duration_off > MIDI_MAX) + noteData[number-1].duration_off = MIDI_LIMIT; + } + } + + if(Melody[0].rest >= '0' && Melody[0].rest <= '5') + { + switch(Melody[0].rest) + { + case '0': midiData[50] += 192; + restSpec[0] = 192; + break; + case '1': midiData[50] += 96; + restSpec[0] = 96; + break; + case '2': midiData[50] += 48; + restSpec[0] = 48; + break; + case '3': midiData[50] += 24; + restSpec[0] = 24; + break; + case '4': midiData[50] += 12; + restSpec[0] = 12; + break; + case '5': midiData[50] += 6; + restSpec[0] = 6; + } + + if(Melody[0].rest_specifier != '%') + { + switch(Melody[0].rest_specifier) + { + case '.': midiData[50] += (restSpec[0] / 2); + break; + case ':': midiData[50] += restSpec[0]; + break; + case ';': midiData[50] -= (restSpec[0] / 3); + } + } + + if(midiData[50] > MIDI_LIMIT) + midiData[50] = MIDI_LIMIT; + + if(Melody[0].rest == '0') + midiData[50] = MIDI_LIMIT; + } + + for(number = 0;number < noteCount;number++) + { + noteBase[0] = noteData[number].note; + noteBase[2] = noteData[number].duration_on; + noteBase[3] = noteData[number].note; + noteBase[5] = noteData[number].duration_off; + + noteTotal[6*number] = noteBase[0]; + noteTotal[6*number+2] = noteBase[2]; + noteTotal[6*number+3] = noteBase[3]; + noteTotal[6*number+5] = noteBase[5]; + noteTotal[6*number+4] = 0; + + if(noteTotal[6*number+2] > MIDI_LIMIT) + noteTotal[6*number+2] = MIDI_LIMIT; + + if(noteTotal[6*number+5] > MIDI_LIMIT) + noteTotal[6*number+5] = MIDI_LIMIT; + } + + for(number = 1;number < noteCount;number++) + { + noteTotal[1] = 84; + + if(Melody[0].vol == '+') + noteTotal[1] = 84 + VOL_INTERVAL; + + if(Melody[0].vol == '-') + noteTotal[1] = 84 - VOL_INTERVAL; + + switch(Melody[number].vol) + { + case '+': noteTotal[6*number+1] = noteTotal[6*(number-1)+1] + VOL_INTERVAL; + break; + case '-': noteTotal[6*number+1] = noteTotal[6*(number-1)+1] - VOL_INTERVAL; + } + + if(noteTotal[6*number+1] > MIDI_LIMIT) + noteTotal[6*number+1] = MIDI_LIMIT; + + if((noteTotal[6*(number-1)+1] == 0 || noteTotal[6*(number-1)+1] == 7) && Melody[number].vol == '-') + noteTotal[6*number+1] = 0; + + if(Melody[number].vol == '%') + noteTotal[6*number+1] = noteTotal[6*(number-1)+1]; + } + + for(number = 0;number < 6 * noteCount;number++) + midiData[52+number] = noteTotal[number]; + + for(number = 6 * noteCount;number < 6 * noteCount * (repeat + 1);number++) + { + noteTotal[number] = noteTotal[repeatCount]; + midiData[52+number] = noteTotal[number]; + repeatCount++; + + if(repeatCount == 6 * noteCount) + repeatCount = 0; + } + + if(vol != '%') + { + switch(vol) + { + case '+': midiData[52+(6*noteCount+1)] = midiData[52+(6*(noteCount-1)+1)] + VOL_INTERVAL; + break; + case '-': midiData[52+(6*noteCount+1)] = midiData[52+(6*(noteCount-1)+1)] - VOL_INTERVAL; + } + + if(Melody[0].vol != '%') + { + switch(Melody[0].vol) + { + case '+': midiData[52+(6*noteCount+1)] += VOL_INTERVAL; + break; + case '-': midiData[52+(6*noteCount+1)] -= VOL_INTERVAL; + } + } + + if(midiData[52+(6*noteCount+1)] > MIDI_LIMIT) + midiData[52+(6*noteCount+1)] = MIDI_LIMIT; + + if((midiData[52+(6*(noteCount-1)+1)] == 0 || midiData[52+(6*(noteCount-1)+1)] == 7) && vol == '-') + { + midiData[52+(6*noteCount+1)] = 0; + + if(Melody[0].vol == '+') + midiData[52+(6*noteCount+1)] = 12; + } + + if((midiData[52+(6*(noteCount-1)+1)] == 12 || midiData[52+(6*(noteCount-1)+1)] == 19) && vol == '-' && Melody[0].vol == '-') + midiData[52+(6*noteCount+1)] = 0; + } + + else + if(Melody[0].vol != '%' && vol == '%' && repeat != 0) + { + switch(Melody[0].vol) + { + case '+': midiData[52+(6*noteCount+1)] = midiData[52+(6*(noteCount-1)+1)] + VOL_INTERVAL; + break; + case '-': midiData[52+(6*noteCount+1)] = midiData[52+(6*(noteCount-1)+1)] - VOL_INTERVAL; + } + + if(midiData[52+(6*noteCount+1)] > MIDI_LIMIT) + midiData[52+(6*noteCount+1)] = MIDI_LIMIT; + + if((midiData[52+(6*(noteCount-1)+1)] == 0 || midiData[52+(6*(noteCount-1)+1)] == 7) && Melody[0].vol == '-') + midiData[52+(6*noteCount+1)] = 0; + } + + else + if(Melody[0].vol == '%' && vol == '%' && repeat != 0) + midiData[52+(6*noteCount+1)] = midiData[52+(6*(noteCount-1)+1)]; + + volInterval = midiData[52+(6*noteCount+1)] - midiData[53]; + + for(repeatCount = 0;repeatCount < repeat;repeatCount++) + for(number = 6 * noteCount * repeatCount + 1;number < 6 * noteCount * (repeatCount + 1);number = number + 6) + { + midiData[52+(number+6*noteCount)] = midiData[52+number] + volInterval; + + if(midiData[52+number] + volInterval > MIDI_LIMIT) + midiData[52+(number+6*noteCount)] = MIDI_LIMIT; + + if(midiData[52+number] < volInterval * (-1)) + midiData[52+(number+6*noteCount)] = 0; + } + + for(number = 1;number < 6 * noteCount * (repeat + 1);number = number + 6) + { + if(midiData[52+number] > MIDI_LIMIT) + midiData[52+number] = MIDI_LIMIT; + } + + pMelodyBuf = pMelodyStart; + count = noteCount; + + if(repeat != 0) + { + while(*pMelodyBuf != '@') + pMelodyBuf++; + + pMelodyBuf++; + + if(vol !='%') + pMelodyBuf = pMelodyBuf + 2; + + while(!(*pMelodyBuf == '*' || *pMelodyBuf == '#' || *pMelodyBuf == '&' || *pMelodyBuf == 'r' || *pMelodyBuf == 'V' || *pMelodyBuf == 'E' || (*pMelodyBuf >= 'a' && *pMelodyBuf <= 'g'))) + pMelodyBuf++; + } + + if(*pMelodyBuf != 'E' && *pMelodyBuf != ':') + { + pMelodyBuf--; + + while(*pMelodyBuf != 'E') + { + if(noteCount>=AV_MIDI_NOTE_MAX) + { + debug_warning ("__AvConvertIMelody2MIDI : noteCount>=AV_MIDI_NOTE_MAX\n"); + break; + } + + pMelodyBuf++; + + if(*pMelodyBuf == '*') + { + pMelodyBuf++; + + if(*pMelodyBuf >= '0' && *pMelodyBuf <= '8') + octave[noteCount] = *pMelodyBuf; + } + + if(*pMelodyBuf == '#' || *pMelodyBuf == '&') + Melody[noteCount].flat_sharp = *pMelodyBuf; + + if(*pMelodyBuf == 'r') + { + pMelodyBuf++; + + if(*pMelodyBuf >= '0' && *pMelodyBuf <= '5') + { + Melody[noteCount].rest = *pMelodyBuf; + pMelodyBuf++; + + if(*pMelodyBuf == '.' || *pMelodyBuf == ':' || *pMelodyBuf == ';') + Melody[noteCount].rest_specifier = *pMelodyBuf; + } + } + + if(*pMelodyBuf == 'V') + { + pMelodyBuf++; + + if(*pMelodyBuf == '+' || *pMelodyBuf == '-') + Melody[noteCount].vol = *pMelodyBuf; + } + + if(*pMelodyBuf >= 'a' && *pMelodyBuf <= 'g') + { + Melody[noteCount].note = *pMelodyBuf; + pMelodyBuf++; + + if(*pMelodyBuf >= '0' && *pMelodyBuf <= '5') + { + Melody[noteCount].duration = *pMelodyBuf; + pMelodyBuf++; + + if(*pMelodyBuf == '.' || *pMelodyBuf == ':' || *pMelodyBuf == ';') + Melody[noteCount].duration_specifier = *pMelodyBuf; + + else + pMelodyBuf--; + + noteCount++; + } + } + } + + for(octaveCount = count;octaveCount < noteCount;octaveCount++) + { + if(octave[octaveCount] == '%') + octave[octaveCount] = octave[octaveCount-1]; + } + + for(number = count;number < noteCount;number++) + { + octaveValue = octave[number] - '0'; + + octaveValue = octaveValue * 12; + + if(Melody[number].flat_sharp == '#') + { + switch(Melody[number].note) + { + case 'c': noteData[number].note = octaveValue + 1; + break; + case 'd': noteData[number].note = octaveValue + 3; + break; + case 'f': noteData[number].note = octaveValue + 6; + break; + case 'g': noteData[number].note = octaveValue + 8; + break; + case 'a': noteData[number].note = octaveValue + 10; + } + } + + else + if(Melody[number].flat_sharp == '&') + { + switch(Melody[number].note) + { + case 'd': noteData[number].note = octaveValue + 1; + break; + case 'e': noteData[number].note = octaveValue + 3; + break; + case 'g': noteData[number].note = octaveValue + 6; + break; + case 'a': noteData[number].note = octaveValue + 8; + break; + case 'b': noteData[number].note = octaveValue + 10; + } + } + + else + { + switch(Melody[number].note) + { + case 'c': noteData[number].note = octaveValue; + break; + case 'd': noteData[number].note = octaveValue + 2; + break; + case 'e': noteData[number].note = octaveValue + 4; + break; + case 'f': noteData[number].note = octaveValue + 5; + break; + case 'g': noteData[number].note = octaveValue + 7; + break; + case 'a': noteData[number].note = octaveValue + 9; + break; + case 'b': noteData[number].note = octaveValue + 11; + } + } + + + if(style == '0') + { + switch(Melody[number].duration) + { + case '0': noteData[number].duration_on = 183; + noteData[number].duration_off = 9; + break; + case '1': noteData[number].duration_on = 91; + noteData[number].duration_off = 5; + break; + case '2': noteData[number].duration_on = 46; + noteData[number].duration_off = 2; + break; + case '3': noteData[number].duration_on = 23; + noteData[number].duration_off = 1; + break; + case '4': noteData[number].duration_on = 11; + noteData[number].duration_off = 1; + break; + case '5': noteData[number].duration_on = 5; + noteData[number].duration_off = 1; + } + } + + else + if(style == '1') + { + switch(Melody[number].duration) + { + case '0': noteData[number].duration_on = 192; + noteData[number].duration_off = 0; + break; + case '1': noteData[number].duration_on = 96; + noteData[number].duration_off = 0; + break; + case '2': noteData[number].duration_on = 48; + noteData[number].duration_off = 0; + break; + case '3': noteData[number].duration_on = 24; + noteData[number].duration_off = 0; + break; + case '4': noteData[number].duration_on = 12; + noteData[number].duration_off = 0; + break; + case '5': noteData[number].duration_on = 6; + noteData[number].duration_off = 0; + } + } + + else + { + switch(Melody[number].duration) + { + case '0': noteData[number].duration_on = 96; + noteData[number].duration_off = 96; + break; + case '1': noteData[number].duration_on = 48; + noteData[number].duration_off = 48; + break; + case '2': noteData[number].duration_on = 24; + noteData[number].duration_off = 24; + break; + case '3': noteData[number].duration_on = 12; + noteData[number].duration_off = 12; + break; + case '4': noteData[number].duration_on = 6; + noteData[number].duration_off = 6; + break; + case '5': noteData[number].duration_on = 3; + noteData[number].duration_off = 3; + } + } + + switch(Melody[number].duration) + { + case '0': durationSpec[number] = 192; + break; + case '1': durationSpec[number] = 96; + break; + case '2': durationSpec[number] = 48; + break; + case '3': durationSpec[number] = 24; + break; + case '4': durationSpec[number] = 12; + break; + case '5': durationSpec[number] = 6; + } + + if(Melody[number].duration_specifier != '%') + { + switch(Melody[number].duration_specifier) + { + case '.': noteData[number].duration_on += (durationSpec[number] / 2); + break; + case ':': noteData[number].duration_on += durationSpec[number]; + break; + case ';': noteData[number].duration_on -= (durationSpec[number] / 3); + } + + if(noteData[number].duration_on > MIDI_MAX) + noteData[number].duration_on = MIDI_LIMIT; + } + } + + for(number = count + 1;number < noteCount;number++) + { + if(Melody[number].rest >= '0' && Melody[number].rest <= '5') + { + switch(Melody[number].rest) + { + case '0': noteData[number-1].duration_off += 192; + restSpec[number] = 192; + break; + case '1': noteData[number-1].duration_off += 96; + restSpec[number] = 96; + break; + case '2': noteData[number-1].duration_off += 48; + restSpec[number] = 48; + break; + case '3': noteData[number-1].duration_off += 24; + restSpec[number] = 24; + break; + case '4': noteData[number-1].duration_off += 12; + restSpec[number] = 12; + break; + case '5': noteData[number-1].duration_off += 6; + restSpec[number] = 6; + } + + if(noteData[number-1].duration_off > MIDI_MAX && Melody[number].rest_specifier == '%') + noteData[number-1].duration_off = MIDI_LIMIT; + } + + if(Melody[number].rest_specifier != '%') + { + switch(Melody[number].rest_specifier) + { + case '.': noteData[number-1].duration_off += (restSpec[number] / 2); + break; + case ':': noteData[number-1].duration_off += restSpec[number]; + break; + case ';': noteData[number-1].duration_off -= (restSpec[number] / 3); + } + + if(noteData[number-1].duration_off > MIDI_MAX) + noteData[number-1].duration_off = MIDI_LIMIT; + } + } + + if(Melody[count].rest >= '0' && Melody[count].rest <= '5') + { + switch(Melody[count].rest) + { + case '0': midiData[52+(6*count*(repeat+1)-1)] += 192; + restSpec[count] = 192; + break; + case '1': midiData[52+(6*count*(repeat+1)-1)] += 96; + restSpec[count] = 96; + break; + case '2': midiData[52+(6*count*(repeat+1)-1)] += 48; + restSpec[count] = 48; + break; + case '3': midiData[52+(6*count*(repeat+1)-1)] += 24; + restSpec[count] = 24; + break; + case '4': midiData[52+(6*count*(repeat+1)-1)] += 12; + restSpec[count] = 12; + break; + case '5': midiData[52+(6*count*(repeat+1)-1)] += 6; + restSpec[count] = 6; + } + + if(Melody[count].rest_specifier != '%') + { + switch(Melody[count].rest_specifier) + { + case '.': midiData[52+(6*count*(repeat+1)-1)] += (restSpec[count] / 2); + break; + case ':': midiData[52+(6*count*(repeat+1)-1)] += restSpec[count]; + break; + case ';': midiData[52+(6*count*(repeat+1)-1)] -= (restSpec[count] / 3); + } + } + + if(midiData[52+(6*count*(repeat+1)-1)] > MIDI_LIMIT) + midiData[52+(6*count*(repeat+1)-1)] = MIDI_LIMIT; + + if(Melody[count].rest == '0') + midiData[52+(6*count*(repeat+1)-1)] = MIDI_LIMIT; + } + + for(number = count;number < noteCount;number++) + { + noteBase[0] = noteData[number].note; + noteBase[2] = noteData[number].duration_on; + noteBase[3] = noteData[number].note; + noteBase[5] = noteData[number].duration_off; + + noteTotal[6*number] = noteBase[0]; + noteTotal[6*number+2] = noteBase[2]; + noteTotal[6*number+3] = noteBase[3]; + noteTotal[6*number+5] = noteBase[5]; + noteTotal[6*number+4] = 0; + + if(noteTotal[6*number+2] > MIDI_LIMIT) + noteTotal[6*number+2] = MIDI_LIMIT; + + if(noteTotal[6*number+5] > MIDI_LIMIT) + noteTotal[6*number+5] = MIDI_LIMIT; + } + + for(number = count + 1;number < noteCount;number++) + { + noteTotal[6*count+1] = midiData[52+(6*count*(repeat+1)-5)]; + + if(Melody[count].vol == '+') + noteTotal[6*count+1] = noteTotal[6*count+1] + VOL_INTERVAL; + + if(Melody[count].vol == '-' && (noteTotal[6*count+1] == 0 || noteTotal[6*count+1] == 7)) + noteTotal[6*count+1] = 0; + + if(Melody[count].vol == '-' && noteTotal[6*count+1] >= 12) + noteTotal[6*count+1] = noteTotal[6*count+1] - VOL_INTERVAL; + + if(noteTotal[6*count+1] > MIDI_LIMIT) + noteTotal[6*count+1] = MIDI_LIMIT; + + switch(Melody[number].vol) + { + case '+': noteTotal[6*number+1] = noteTotal[6*(number-1)+1] + VOL_INTERVAL; + break; + case '-': noteTotal[6*number+1] = noteTotal[6*(number-1)+1] - VOL_INTERVAL; + } + + if(noteTotal[6*number+1] > MIDI_LIMIT) + noteTotal[6*number+1] = MIDI_LIMIT; + + if((noteTotal[6*(number-1)+1] == 0 || noteTotal[6*(number-1)+1] == 7) && Melody[number].vol == '-') + noteTotal[6*number+1] = 0; + + if(Melody[number].vol == '%') + noteTotal[6*number+1] = noteTotal[6*(number-1)+1]; + } + + numberCount = 6 * count; + + for(number = 6 * count * (repeat + 1);number < 6 * (count * (repeat + 1) + (noteCount - count));number++) + { + midiData[52+number] = noteTotal[numberCount]; + numberCount++; + } + } + + noteTotal[6*(count*(repeat+1)+(noteCount-count))] = 0; //0x00 + noteTotal[6*(count*(repeat+1)+(noteCount-count))+1] = MIDI_MAX; //0xff + noteTotal[6*(count*(repeat+1)+(noteCount-count))+2] = 47; //0x2f + noteTotal[6*(count*(repeat+1)+(noteCount-count))+3] = 0; //0x00 + + for(number = 6 * (count * (repeat + 1) + (noteCount - count));number <= 6 * (count * (repeat + 1) + (noteCount - count)) + 3;number++) + midiData[51+number] = noteTotal[number]; + + trackSize = (6 * (count * (repeat + 1) + (noteCount - count)) + 56) - 22 -1 ; + + midiData[20] = (trackSize & 0xff00) >> 8; + midiData[21] = (trackSize & 0x00ff); + + + *pBufLen = 6 * (count * (repeat + 1) + (noteCount - count)) + 56 -1; + + pConvertBuf = (unsigned char*) mmfile_malloc (*pBufLen); + if(pConvertBuf == NULL) + { + debug_error ("__AvConvertIMelody2MIDI: malloc failed!\n"); + return NULL; + } + + memcpy(pConvertBuf, midiData, *pBufLen); + + return pConvertBuf; +} + +static unsigned char +__AvMIDISetVolume(char* pMelodyBuf) +{ + unsigned char midiVol; + + pMelodyBuf = pMelodyBuf + 42; + + while(!((*pMelodyBuf == 'V' && (*(pMelodyBuf+1) < 'a' || *(pMelodyBuf+1) > 'z')) || (*pMelodyBuf == 'M' && *(pMelodyBuf+5) == 'Y' && *(pMelodyBuf+6) == ':'))) //2007-02-28 AVMS_Sound:k2bogus - UMTS200073205;imy play, [MELODY:] extract fix + pMelodyBuf++; + + if(*pMelodyBuf != 'V') + midiVol = AV_MIDI_VOL_MAX; + + else + { + pMelodyBuf = pMelodyBuf + 5; + + if(*pMelodyBuf == 'E') + pMelodyBuf = pMelodyBuf + 3; + + else + pMelodyBuf = pMelodyBuf - 4; + + if(*pMelodyBuf == '1') + { + pMelodyBuf++; + + switch(*pMelodyBuf) + { + case '0': midiVol = AV_MIDI_VOL_MAX; + break; + case '1': midiVol = AV_MIDI_VOL_MAX; + break; + case '2': midiVol = AV_MIDI_VOL_MAX; + break; + case '3': midiVol = AV_MIDI_VOL_MAX; + break; + case '4': midiVol = AV_MIDI_VOL_MAX; + break; + case '5': midiVol = AV_MIDI_VOL_MAX; + break; + default : midiVol = AV_MIDI_VOL_MAX; + } + } + + else + switch(*pMelodyBuf) + { + case '0': midiVol = 0; + break; + case '2': midiVol = AV_MIDI_VOL_MAX; + break; + case '3': midiVol = AV_MIDI_VOL_MAX; + break; + case '4': midiVol = AV_MIDI_VOL_MAX; + break; + case '5': midiVol = AV_MIDI_VOL_MAX; + break; + case '6': midiVol = AV_MIDI_VOL_MAX; + break; + case '7': midiVol = AV_MIDI_VOL_MAX; + break; + case '8': midiVol = AV_MIDI_VOL_MAX; + break; + case '9': midiVol = AV_MIDI_VOL_MAX; + break; + default : midiVol = AV_MIDI_VOL_MAX; + } + } + + return midiVol; +} + +static int +__AvMIDISetBeat(char* pMelodyBuf) +{ + int bpmValue[4] = {0}; + int beatValue; + + pMelodyBuf = pMelodyBuf + 42; + + while(!((*pMelodyBuf == 'B' && (*(pMelodyBuf+1) < 'a' || *(pMelodyBuf+1) > 'z')) || (*pMelodyBuf == 'M' && *(pMelodyBuf+5) == 'Y' && *(pMelodyBuf+6) == ':'))) //2007-02-28 AVMS_Sound:k2bogus - UMTS200073205;imy play, [MELODY:] extract fix + pMelodyBuf++; + + if(*pMelodyBuf != 'B') + bpmValue[3] = 120; + + else + { + pMelodyBuf = pMelodyBuf + 4; + + if(*pMelodyBuf == ':') + { + pMelodyBuf++; + + if(*pMelodyBuf >= '1' && *pMelodyBuf <= '9') + { + bpmValue[0] = *pMelodyBuf - '0'; + pMelodyBuf++; + + if(*pMelodyBuf >= '0' && *pMelodyBuf <= '9') + { + bpmValue[1] = *pMelodyBuf - '0'; + pMelodyBuf++; + + if(*pMelodyBuf >= '0' && *pMelodyBuf <= '9') + { + bpmValue[2] = *pMelodyBuf - '0'; + + bpmValue[0] = bpmValue[0] * 100; + bpmValue[1] = bpmValue[1] * 10; + pMelodyBuf++; + } + + else + bpmValue[0] = bpmValue[0] * 10; + } + } + + bpmValue[3] = bpmValue[0] + bpmValue[1] + bpmValue[2]; + } + } + + if(bpmValue[3] < 25 || bpmValue[3] > 900) + bpmValue[3] = 120; + + if(*pMelodyBuf >= '0' && *pMelodyBuf <= '9') + bpmValue[3] = 120; + + beatValue = 1000000 * 60 / bpmValue[3]; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("beat: %d = 1000000 * 60 / %d\n", beatValue, bpmValue[3]); + #endif + + return beatValue; +} + +static char +__AvMIDISetStyle(char* pMelodyBuf) +{ + char styleValue = '0'; + + while(*pMelodyBuf != 'S') + pMelodyBuf--; + + pMelodyBuf++; + + if(*pMelodyBuf >= '0' && *pMelodyBuf <= '2') + pMelodyBuf++; + + if(*pMelodyBuf != '.') + { + pMelodyBuf--; + styleValue = *pMelodyBuf; + } + + if(styleValue < '0' || styleValue > '2') { + debug_warning ("unknown style. use default(S0)\n"); + styleValue = '0'; + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("style: '%c'\n", styleValue); + #endif + + return styleValue; +} + + diff --git a/formats/ffmpeg/mm_file_format_midi.c b/formats/ffmpeg/mm_file_format_midi.c new file mode 100755 index 0000000..c05e5b2 --- /dev/null +++ b/formats/ffmpeg/mm_file_format_midi.c @@ -0,0 +1,1518 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string.h> /*memcmp*/ +#include <stdio.h> +#include <stdlib.h> /*malloc*/ +#include <mm_error.h> + +#include "mm_debug.h" +#include "mm_file_utils.h" +#include "mm_file_format_private.h" +#include "mm_file_format_midi.h" + +/** + * internal defines + */ +enum { + AV_DEC_AUDIO_MIDI, + AV_DEC_AUDIO_XMF, + AV_DEC_AUDIO_RMF, +}; + +#define MMFILE_XMF_100 "XMF_1.00" +#define MMFILE_XMF_101 "XMF_1.01" +#define MMFILE_MXMF_200 "XMF_2.00" +#define MMFILE_RMF "IREZ" + +#define AvSMW_CNVID_MMF (1) /* SMAF/MA-1/MA-2/MA-3/MA-5 */ +#define AvSMW_CNVID_PHR (2) /* SMAF/Phrase L1/L2 */ +#define AvSMW_CNVID_RMD (3) /* Realtime MIDI */ +#define AvSMW_CNVID_AUD (4) /* SMAF/Audio */ +#define AvSMW_CNVID_MID (5) /* SMF */ +#define AvSMW_CNVID_HVS (9) /* HV Script */ +#define AvSMW_CNVID_WAV (11) /* WAVE */ + +#define AvSMW_SUCCESS (0) /* success */ +#define AvSMW_ERROR (-1) /* error */ +#define AvSMW_ERROR_ARGUMENT (-2) /* error of arguments */ +#define AvSMW_ERROR_RESOURCE_OVER (-3) /* over specified resources */ +#define AvSMW_ERROR_ID (-4) /* error id number */ +#define AvSMW_ERROR_TIMEOUT (-5) /* timeout */ +#define AvSMW_ERROR_SOFTRESET (-6) /* error of soft reset for MA-5 */ + +#define AvSMW_ERROR_FILE (-16) /* file error */ +#define AvSMW_ERROR_CONTENTS_CLASS (-17) /* SMAF Contents Class shows can't play */ +#define AvSMW_ERROR_CONTENTS_TYPE (-18) /* SMAF Contents Type shows can't play */ +#define AvSMW_ERROR_CHUNK_SIZE (-19) /* illegal SAvF Chunk Size value */ +#define AvSMW_ERROR_CHUNK (-20) /* illegal SAvF Track Chunk value */ +#define AvSMW_ERROR_UNMATCHED_TAG (-21) /* unmathced specified TAG */ +#define AvSMW_ERROR_SHORT_LENGTH (-22) /* short sequence */ +#define AvSMW_ERROR_LONG_LENGTH (-23) /* long sequence */ +#define AvSMW_ERROR_UNSUPPORTED (-24) /* unsupported format */ +#define AvSMW_ERROR_NO_INFORMATION (-25) /* no specified information */ +#define AvSMW_ERROR_HV_CONFLICT (-26) /* conflict about HV resource */ + +#define AvSMW_ERROR_SMF_FORMAT (-50) /* invalid format type != 0/1 */ +#define AvSMW_ERROR_SMF_TRACKNUM (-51) /* invalid number of tracks */ +#define AvSMW_ERROR_SMF_TIMEUNIT (-52) /* invalid time unit */ +#define AvSMW_ERROR_SMF_CMD (-53) /* invalid command byte */ + + +#define SINT signed int +#define SINT8 signed char +#define SINT16 signed short +#define SINT32 signed long +#define UINT unsigned int +#define UINT8 unsigned char +#define UINT16 unsigned short +#define UINT32 unsigned long +#define ULONG unsigned long + +/*--------------------------------------------------------------------------*/ +/* Defines */ +/*--------------------------------------------------------------------------*/ +#define SMF_TIMEBASE_SHIFT 2 /* */ +#define SMF_TIMEBASE (1L<<SMF_TIMEBASE_SHIFT) /* [ms] */ + +#define MAX_SMF_MESSAGES 256 /* */ +#define MAX_SMF_TRACKS 32 /* Should be <= 32 */ +#define SMF_MAX_GAIN 76 /* - 6[dB] : 90 */ + /* -18[dB] : 45 */ +#define MINIMUM_LENGTH (20) + +#define MELODY_MAP (0) +#define DRUM_MAP (1) +#define NUM_OF_MAPS (2) + + +/*--------------------------------------------------------------------------*/ +/* Types */ +/*--------------------------------------------------------------------------*/ +typedef struct _tagMidChInfo +{ + UINT32 dBank; /* BankH&L (0x00:00..0x7F7F) */ + UINT32 dCurrBank; /* BankH&L (0x00:00..0x7F7F) */ + UINT32 dProg; /* ProgramChange (0..127) */ + UINT32 dVolume; /* ChannelVolume (0..127) */ + UINT32 dExpression; /* Expression (0..127) */ + UINT32 dModulation; /* Modulation (0..127) */ + UINT32 dPitchBend; /* PitchBendH (0..127) */ + UINT32 dBendRange; /* CurrentBendRange (0..24) */ + UINT32 dPreBendRange; /* LatestBendRange (0..24) */ + UINT32 dPanpot; /* Panpot (0..127) */ + UINT32 dHold1; /* Hold1 (0..127) */ + UINT32 dMode; /* 0:MONO, 1:POLY */ + UINT32 dRPN; /* RPN (0x00:00..0xFF7F) */ + UINT32 dMipMute; /* Mute switch (1:mute) */ + UINT32 dKeyCon; /* 0:Melady, 1:OFF, 2:ON */ + UINT32 dLedSync; /* 0:OFF, 1:ON */ + UINT32 dVibSync; /* 0:OFF, 1:ON */ + UINT32 dFineTune; /* 0..0x3FFF */ + UINT32 dCoaseTune; /* 0..0x7F */ +} MIDCHINFO, *PMIDCHINFO; + +typedef struct _tagMIDPACKET +{ + SINT32 sdDeltaTime; + UINT32 dMsgID; + UINT32 dP1; + UINT32 dP2; + UINT32 dP3; +} MIDPACKET, *PMIDPACKET; + +typedef struct _tagTrack +{ + UINT32 dSmfCmd; /* CMD @ now */ + UINT32 dSize; /* [byte] 0 measns nothing in it. */ + UINT8* pbBase; /* NULL measns nothing in it. */ + UINT32 dOffset; /* offset byte */ + SINT32 sdTicks; /* */ +} TRACKINFO, *PTRACKINFO; + +typedef struct _tagOrderList +{ + struct _tagOrderList* pPrev; + struct _tagOrderList* pNext; + UINT32 dTrack; + UINT32 dTicks; +} ORDERLIST, *PORDERLIST; + +typedef struct _tagMidInfo +{ + UINT32 dTimeResolution; /* 0..0x7fff */ + UINT8* pbText; /* */ + UINT32 dSizeText; /* */ + UINT8* pbTitle; /* */ + UINT32 dSizeTitle; /* */ + UINT8* pbCopyright; /* */ + UINT32 dSizeCopyright; /* */ + UINT32 dNumOfTracks; /* 1..32 */ + UINT32 dSmfFormat; /* 0..1 */ + UINT32 dSetupBar; /* 0:No, 1:Yes */ + UINT32 dStart; /* Index after SetupBar */ + UINT32 dVibNoteVoice; /* 0:No, 1:Yes */ + + SINT32 sdTotalTicks; /* Total ticks */ + SINT32 sdDataEndTime; /* (22.10)[ms] */ + SINT32 sdDelta; /* (22.10)[ms] */ + + UINT32 dEndFlag; /* */ + TRACKINFO TrackInfo[MAX_SMF_TRACKS]; + + struct _tagOrderList* pTopOrderList; + struct _tagOrderList* pDoneOrderList; + struct _tagOrderList* pBottomOrderList; + ORDERLIST OrderList[MAX_SMF_TRACKS + 3]; + + MIDCHINFO ChInfo[16]; /* */ + UINT32 dValid; /* 0:none, 1:Valid */ + + UINT8 bVoiceMap[NUM_OF_MAPS][128];/* 0:Empty, 1:Valid */ +} MIDINFO, *PMIDINFO; + +typedef struct _tagMidGlobals +{ + SINT32 sdSeqID; /* Sequence ID */ + SINT32 sdFileID; /* File ID */ + UINT32 dEnable; /* 0:disable */ + UINT32 dSetup; /* 1: Just after seek */ + + UINT32 dRamBase; /* */ + UINT32 dRamOffset; /* */ + UINT32 dRamSize; /* */ + + MIDINFO DataInfo[2]; /* */ + + SINT32 sdSeekTime; /* [ms] */ + SINT32 sdLastMsgTime; /* (22.10)[ms] */ + SINT32 sdSmfCurrentTicks; /* Ticks @ now */ + SINT32 sdSmfCurrentTime; /* (22.10)[ms] */ + SINT32 sdSmfDataEndTime; /* (22.10)[ms] */ + SINT32 sdSmfEndTime; /* (22.10)[ms] */ + SINT32 sdSmfDelta; /* (22.10)[ms] */ + + UINT32 dMaxGain; /* MaxGain (0..127) */ + UINT32 dMasterVolume; /* MsaterVolume (0..127) */ + + UINT32 dHoldMsgs; /* Number of messages in Q */ + UINT32 dHoldPtrR; /* Pointer for Read */ + MIDPACKET MsgBuffer[MAX_SMF_MESSAGES]; /* Message Q */ + + UINT32 dMuteFlag; /* 0:Normal, 1:MUTE */ + + UINT32 dSyncNoteCh; /* 0..15 */ + UINT32 dSyncNoteKey; /* 0..127, 255:OFF */ + UINT32 dVibNote; /* 0:No VibiNote, 1:Yes,VibNote */ + +} MIDGLOBAL, *PMIDGLOBAL; + + +/*---------------------------------------------------------------------------*/ +/* Globals */ +/*---------------------------------------------------------------------------*/ +static PMIDGLOBAL gpMidInfo; +static PMIDINFO gPi; + + +static SINT32 __AvMidFile_Initialize (void); +static void __AvMidFile_Deinitialize (void); +static void __AvMidInitializeOrderList (PMIDINFO pI); +static void __AvMidSortOrderList (PMIDINFO pI); +static void __AvMidInsertOrderList (PMIDINFO pI, UINT32 dTrack, SINT32 sdTicks); +static void __AvMidRemoveFromOrderList (PMIDINFO pI); +static SINT32 __AvMidGetTrackTime (PMIDINFO pI, UINT32 dTrack); +static SINT32 __AvMidUpdateTrackTime (PMIDINFO pI, UINT32 dTrack); +static void __AvMidResetTimeInfo (PMIDINFO pI); +static SINT32 __AvMidGetLeastTimeTrack (PMIDINFO pI); +static SINT32 __AvGetSizeOfFileInfo (PMIDINFO pI); +static SINT32 __AvCheckSizeOfMidFile (UINT8* fp, UINT32 dFsize); +static int __AvParseSkipXmf2Mid (UINT8* pbFile, UINT32 dFSize); +static int __AvGetMidiDuration (char* szFileName, MIDI_INFO_SIMPLE *info); + + +/* mm plugin interface */ +int mmfile_format_read_stream_mid (MMFileFormatContext *formatContext); +int mmfile_format_read_frame_mid (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame); +int mmfile_format_read_tag_mid (MMFileFormatContext *formatContext); +int mmfile_format_close_mid (MMFileFormatContext *formatContext); + +EXPORT_API +int mmfile_format_open_mid (MMFileFormatContext *formatContext) +{ + int res = MMFILE_FORMAT_FAIL; + + if (NULL == formatContext || NULL == formatContext->uriFileName) { + debug_error ("error: mmfile_format_open_mid\n"); + return MMFILE_FORMAT_FAIL; + } + + if (formatContext->pre_checked == 0) { + res = MMFileFormatIsValidMID (formatContext->uriFileName); + if ( res == 0 ) { + debug_error("It is not MIDI file\n"); + return MMFILE_FORMAT_FAIL; + } + } + + formatContext->ReadStream = mmfile_format_read_stream_mid; + formatContext->ReadFrame = mmfile_format_read_frame_mid; + formatContext->ReadTag = mmfile_format_read_tag_mid; + formatContext->Close = mmfile_format_close_mid; + + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = 1; + + formatContext->privateFormatData = NULL; + + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_read_stream_mid (MMFileFormatContext *formatContext) +{ + MMFileFormatStream *audioStream = NULL; + int ret = MMFILE_FORMAT_FAIL; + MIDI_INFO_SIMPLE *info = NULL; + + if (NULL == formatContext) { + debug_error ("error: invalid params\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + /*get infomation*/ + info = mmfile_format_get_midi_infomation (formatContext->uriFileName); + if (!info) { + debug_error ("failed to get infomation"); + goto exception; + } + + formatContext->duration = info->duration; + formatContext->videoStreamId = -1; + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = info->track_num; + formatContext->nbStreams = 1; + + audioStream = mmfile_malloc (sizeof(MMFileFormatStream)); + if (NULL == audioStream) { + debug_error ("error: mmfile_malloc audiostream\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + audioStream->streamType = MMFILE_AUDIO_STREAM; + audioStream->codecId = (info->is_xmf == 1) ? MM_AUDIO_CODEC_MXMF : MM_AUDIO_CODEC_MIDI; + audioStream->bitRate = 0; + audioStream->framePerSec = 0; + audioStream->width = 0; + audioStream->height = 0; + audioStream->nbChannel = 1; + audioStream->samplePerSec = 0; + formatContext->streams[MMFILE_AUDIO_STREAM] = audioStream; + + #ifdef __MMFILE_TEST_MODE__ + mmfile_format_print_contents (formatContext); + #endif + + mmfile_format_free_midi_infomation (info); + return MMFILE_FORMAT_SUCCESS; + +exception: + mmfile_format_free_midi_infomation (info); + mmfile_free (audioStream); + + return ret; +} + +EXPORT_API +int mmfile_format_read_tag_mid (MMFileFormatContext *formatContext) +{ + int ret= MMFILE_FORMAT_FAIL; + MIDI_INFO_SIMPLE *info = NULL; + const char *locale = MMFileUtilGetLocale (NULL); + unsigned int tag_len; + unsigned int cnv_len; + + if (NULL == formatContext) { + debug_error ("error: invalid params\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + /*get infomation*/ + info = mmfile_format_get_midi_infomation (formatContext->uriFileName); + if (!info) { + debug_error ("failed to get infomation"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + /** + * UTF8 converting. + */ + if (info->title) { + tag_len = strlen (info->title); + cnv_len = 0; + formatContext->title = mmfile_string_convert ((const char*)info->title, + tag_len, + "UTF-8", + locale, + NULL, + (unsigned int*)&cnv_len); + if (formatContext->title == NULL) { + debug_warning ("failed to UTF8 convert.\n"); + formatContext->title = mmfile_strdup (info->title); + } + } + + if (info->copyright) { + tag_len = strlen (info->copyright); + cnv_len = 0; + formatContext->copyright = mmfile_string_convert ((const char*)info->copyright, + tag_len, + "UTF-8", + locale, + NULL, + (unsigned int*)&cnv_len); + if (formatContext->copyright == NULL) { + debug_warning ("failed to UTF8 convert.\n"); + formatContext->copyright = mmfile_strdup (info->copyright); + } + } + + if (info->comment) { + tag_len = strlen (info->comment); + cnv_len = 0; + formatContext->comment = mmfile_string_convert ((const char*)info->comment, + tag_len, + "UTF-8", + locale, + NULL, + (unsigned int*)&cnv_len); + if (formatContext->comment == NULL) { + debug_warning ("failed to UTF8 convert.\n"); + formatContext->comment = mmfile_strdup (info->comment); + } + } + +#ifdef __MMFILE_TEST_MODE__ + mmfile_format_print_contents (formatContext); +#endif + + mmfile_format_free_midi_infomation (info); + return MMFILE_FORMAT_SUCCESS; + +exception: + mmfile_format_free_midi_infomation (info); + return ret; +} + +EXPORT_API +int mmfile_format_read_frame_mid (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame) +{ + debug_error ("error: mmfile_format_read_frame_midi, no handling\n"); + + return MMFILE_FORMAT_FAIL; +} + +EXPORT_API +int mmfile_format_close_mid (MMFileFormatContext *formatContext) +{ + if (NULL == formatContext ) { + debug_error ("error: invalid params\n"); + return MMFILE_FORMAT_FAIL; + } + + if(formatContext->streams[MMFILE_AUDIO_STREAM]) { + mmfile_free(formatContext->streams[MMFILE_AUDIO_STREAM]); + formatContext->streams[MMFILE_AUDIO_STREAM] = NULL; + } + + formatContext->ReadStream = NULL; + formatContext->ReadFrame = NULL; + formatContext->ReadTag = NULL; + formatContext->Close = NULL; + + return MMFILE_FORMAT_SUCCESS; +} + +static char * _lc_strdup (const char *str, unsigned int size) +{ + char *t = NULL; + t = mmfile_malloc (size+1); + if (t) { + memset (t, 0x00, size+1); + memcpy (t, str, size); + } + return t; +} + +MIDI_INFO_SIMPLE * +mmfile_format_get_midi_infomation (char* szFileName) +{ + int duration = -1; + MIDI_INFO_SIMPLE *info = NULL; + + info = mmfile_malloc (sizeof (MIDI_INFO_SIMPLE)); + if (!info) + return NULL; + + /*get infomation*/ + duration = __AvGetMidiDuration (szFileName, info); + + return info; +} + +void +mmfile_format_free_midi_infomation (MIDI_INFO_SIMPLE *info) +{ + if (info) { + if (info->title) mmfile_free (info->title); + if (info->copyright) mmfile_free (info->copyright); + if (info->comment) mmfile_free (info->comment); + mmfile_free (info); + } +} + + + +/** + * internal midi parsing functions + */ + +/**************************************************************************** + * __AvGetMidiDuration(char* szFileName) + * + * Desc. + * Load SMF data + * Param + * pbFile pointer to the data + * dFileSize size fo the data + * dMode error check (0:No, 1:Yes, 2:Check, 3:OnlyInfo) + * pfnFunc pointer of rhe callback function + * pvExtArgs Reserved + * Return + * >= 0 : FileID, < 0 : Error code + ****************************************************************************/ +static int +__AvGetMidiDuration(char* szFileName, MIDI_INFO_SIMPLE *info) +{ + + int xmfheaderSkip=0; + MMFileIOHandle * hFile = NULL; + UINT8 * pbFile= NULL; + UINT8 * pIMYbuf= NULL; + SINT32 dFileSize; + int sdCurrentTime = 0; + // void* pvExtArgs = "mid"; + int readed =0; + int ret; + int codecType = AV_DEC_AUDIO_MIDI; + int is_xmf = 0; + + if ( szFileName == NULL || info == NULL) + return -1; + + // printf ("URI: %s\n", szFileName); + /*open*/ + ret = mmfile_open (&hFile, szFileName, MMFILE_RDONLY); + if (ret == MMFILE_UTIL_FAIL) { + debug_error ( "open failed.\n"); + return -1; + } + + /*get file size*/ + mmfile_seek (hFile, 0L, MMFILE_SEEK_END); + dFileSize = mmfile_tell (hFile); + mmfile_seek (hFile, 0L, MMFILE_SEEK_SET); + + if (dFileSize <= 0) { + debug_error ("failed to get file size.\n"); + goto _RELEASE_RESOURCE; + } + + /*alloc read buffer*/ + pbFile = (UINT8 *) mmfile_malloc (sizeof(UINT8) * (dFileSize + 1)); + if (!pbFile) { + debug_error ( "memory allocation failed.\n"); + goto _RELEASE_RESOURCE; + } + + /*read data*/ + if ((readed = mmfile_read (hFile, pbFile, dFileSize) ) != dFileSize) { + debug_error ( "read error. size = %d\n", readed); + goto _RELEASE_RESOURCE; + } + + /*init global workspace*/ + if(__AvMidFile_Initialize()) + goto _RELEASE_RESOURCE; + + /*check format*/ + if (!(memcmp (pbFile, MMFILE_XMF_100, 8)) || + !(memcmp (pbFile, MMFILE_XMF_101, 8)) || + !(memcmp (pbFile, MMFILE_MXMF_200, 8))) { + + is_xmf = 1; + codecType = AV_DEC_AUDIO_XMF; + } else if (!(memcmp (pbFile, MMFILE_RMF, 4))) { + is_xmf = 0; + codecType = AV_DEC_AUDIO_RMF; + } else { + is_xmf = 0; + codecType = AV_DEC_AUDIO_MIDI; + } + + /*set output param*/ + if (codecType == AV_DEC_AUDIO_RMF) { + + info->duration = sdCurrentTime = 0; /*not yet implemented.*/ + info->track_num = 1; /*not yet implemented.*/ + info->is_xmf = is_xmf; + + } else { + + /*get duration. XMF/MIDI*/ + if(codecType == AV_DEC_AUDIO_XMF) { + xmfheaderSkip = __AvParseSkipXmf2Mid(pbFile, dFileSize); + if(xmfheaderSkip == -1) + goto _RELEASE_RESOURCE; + + sdCurrentTime = __AvCheckSizeOfMidFile(pbFile+xmfheaderSkip, dFileSize); + } else { + sdCurrentTime = __AvCheckSizeOfMidFile(pbFile, dFileSize); + } + + if(sdCurrentTime < 0) { + debug_error ("__AvGetMidiDuration: sdResult's error Code!(%d)\n", sdCurrentTime); + goto _RELEASE_RESOURCE; + } + + if(sdCurrentTime > 0) + sdCurrentTime /= 1000; + + info->duration = sdCurrentTime; + info->track_num = gPi->dNumOfTracks; + info->is_xmf = is_xmf; + + info->title = _lc_strdup ((const char *)gPi->pbTitle, gPi->dSizeTitle); + info->copyright = _lc_strdup ((const char *)gPi->pbCopyright, gPi->dSizeCopyright); + info->comment = _lc_strdup ((const char *)gPi->pbText, gPi->dSizeText); + } + +_RELEASE_RESOURCE: + + /*resource release*/ + __AvMidFile_Deinitialize (); + mmfile_close (hFile); + mmfile_free (pbFile); + mmfile_free (pIMYbuf); + + return sdCurrentTime; +} + +static SINT32 +__AvMidFile_Initialize(void) +{ + gpMidInfo = mmfile_malloc (sizeof (MIDGLOBAL)); + + if (!gpMidInfo) + return (AvSMW_ERROR); + + memset (gpMidInfo, 0x00, sizeof (MIDGLOBAL)); + + gpMidInfo->sdSeqID = -1; /* Sequence ID */ + gpMidInfo->sdFileID = -1; /* File ID */ + gpMidInfo->dEnable = 0; /* 0:disabel */ + gpMidInfo->DataInfo[0].dValid = 0; + gpMidInfo->DataInfo[1].dValid = 0; + + return (AvSMW_SUCCESS); +} + +static void +__AvMidFile_Deinitialize(void) +{ + mmfile_free (gpMidInfo); +} + + +/*---------------------------------------------------------------------------*/ +/* Functions (internal use only) */ +/*---------------------------------------------------------------------------*/ + +/**************************************************************************** + * __AvMidInitializeOrderList(PMIDINFO pI) + * + * Description: + * Initialize OrderList. + * Param: + * pI ... pointer to the data info + * Return: + * none + ****************************************************************************/ +static void +__AvMidInitializeOrderList(PMIDINFO pI) +{ + int ix2; + + for (ix2 = 1; ix2 <= MAX_SMF_TRACKS + 1; ix2++) + { + pI->OrderList[ix2].pPrev = &pI->OrderList[ix2 - 1]; + pI->OrderList[ix2].pNext = &pI->OrderList[ix2 + 1]; + pI->OrderList[ix2].dTrack = 0xFF; + pI->OrderList[ix2].dTicks = 0xFFFFFFFFL; + } + pI->OrderList[0].pPrev = NULL; + pI->OrderList[0].pNext = &pI->OrderList[1]; + pI->OrderList[MAX_SMF_TRACKS + 2].pPrev = &pI->OrderList[MAX_SMF_TRACKS + 1]; + pI->OrderList[MAX_SMF_TRACKS + 2].pNext = NULL; + pI->pTopOrderList = &pI->OrderList[0]; + pI->pDoneOrderList = &pI->OrderList[1]; + pI->pBottomOrderList = &pI->OrderList[MAX_SMF_TRACKS + 2]; +} + + +/**************************************************************************** + * __AvMidSortOrderList(PMIDINFO pI) + * + * Description: + * Sort OrderList. (Ascending order) + * Param: + * pI ... pointer to the data info + * Return: + * none + ****************************************************************************/ +static void +__AvMidSortOrderList(PMIDINFO pI) +{ + PORDERLIST pSlot; + PORDERLIST pTerget; + + pSlot = (pI->pTopOrderList)->pNext; + (pSlot->pPrev)->pNext = pSlot->pNext; + (pSlot->pNext)->pPrev = pSlot->pPrev; + pSlot->dTicks = ((UINT32)pI->TrackInfo[pSlot->dTrack].sdTicks << 5) + pSlot->dTrack; + + pTerget = pSlot->pNext; + while (pTerget != pI->pDoneOrderList) + { + if (pSlot->dTicks <= pTerget->dTicks) break; + pTerget = pTerget->pNext; + } + + (pTerget->pPrev)->pNext = pSlot; + pSlot->pPrev = pTerget->pPrev; + pTerget->pPrev = pSlot; + pSlot->pNext = pTerget; +} + + +/**************************************************************************** + * __AvMidInsertOrderList(PMIDINFO pI, UINT32 dTrack, SINT32 sdTicks) + * + * Description: + * Add item to the top of the list. + * Param: + * pI ... pointer to the data info + * Return: + * none + ****************************************************************************/ +static void +__AvMidInsertOrderList(PMIDINFO pI, UINT32 dTrack, SINT32 sdTicks) +{ + PORDERLIST pTerget; + + if (pI->dNumOfTracks == 1) return; + if (((pI->dEndFlag >> dTrack) & 0x01) == 0) return; + + pTerget = pI->pDoneOrderList->pNext; + if (pTerget == pI->pBottomOrderList) return; + + pI->pDoneOrderList->pNext = pTerget->pNext; + (pTerget->pNext)->pPrev = pI->pDoneOrderList; + + pTerget->dTrack = dTrack; + pTerget->dTicks = ((UINT32)sdTicks << 5) + dTrack; + pTerget->pPrev = pI->pTopOrderList; + pTerget->pNext = (pI->pTopOrderList)->pNext; + ((pI->pTopOrderList)->pNext)->pPrev = pTerget; + (pI->pTopOrderList)->pNext = pTerget; + + __AvMidSortOrderList(pI); +} + + +/**************************************************************************** + * __AvMidRemoveFromOrderList(PMIDINFO pI) + * + * Description: + * delete Item from the top of the list. + * Param: + * pI ... pointer to the data info + * Return: + * none + ****************************************************************************/ +static void +__AvMidRemoveFromOrderList(PMIDINFO pI) +{ + PORDERLIST pSlot; + PORDERLIST pTerget; + + pSlot = (pI->pTopOrderList)->pNext; + (pSlot->pPrev)->pNext = pSlot->pNext; + (pSlot->pNext)->pPrev = pSlot->pPrev; + + pTerget = pI->pBottomOrderList; + (pTerget->pPrev)->pNext = pSlot; + pSlot->pPrev = pTerget->pPrev; + pTerget->pPrev = pSlot; + pSlot->pNext = pTerget; +} + + +/**************************************************************************** + * __AvMidGetTrackTime(PMIDINFO pI, UINT32 dTrack) + * + * Description: + * Get the 1st DT from the list. + * Param: + * pI ... pointer to the data info + * bTrack ... #Track + * Return: + * 0 : NoError, < 0 : Error code + ****************************************************************************/ +static SINT32 +__AvMidGetTrackTime(PMIDINFO pI, UINT32 dTrack) +{ + UINT32 dTemp; + SINT32 dTime; + PTRACKINFO pMt; + + if (((pI->dEndFlag >> dTrack) & 0x01) == 0) return (-1); + + pMt = &(pI->TrackInfo[dTrack]); + + dTime = 0; + do + { + if (pMt->dOffset >= pMt->dSize) + { + pI->dEndFlag &= ~(1L << dTrack); + return (-1); + } + dTemp = (UINT32)pMt->pbBase[pMt->dOffset++]; + dTime = (dTime << 7) + (dTemp & 0x7f); + } while (dTemp >= 0x80); + //printf("dTime is %d\n", dTime); + pMt->sdTicks += dTime; + + return (0); +} + + +/**************************************************************************** + * __AvMidUpdateTrackTime(PMIDINFO pI, UINT32 dTrack) + * + * Description: + * Update the 1st DT on the Track and OrderList + * Param: + * pI ... pointer to the data info + * bTrack ... #Track + * Return: + * 0 : NoError, < 0 : Error code + ****************************************************************************/ +static SINT32 +__AvMidUpdateTrackTime(PMIDINFO pI, UINT32 dTrack) +{ + UINT32 dTemp; + SINT32 dTime; + PTRACKINFO pMt; + + if (pI->dNumOfTracks == 1) + { + /* Single track */ + if (((pI->dEndFlag >> dTrack) & 0x01) == 0) + { + return (-1); + } + + pMt = &(pI->TrackInfo[dTrack]); + + dTime = 0; + do + { + if (pMt->dOffset >= pMt->dSize) + { + pI->dEndFlag &= ~(1L << dTrack); + return (-1); + } + dTemp = (UINT32)pMt->pbBase[pMt->dOffset++]; + dTime = (dTime << 7) + (dTemp & 0x7f); + } while (dTemp >= 0x80); + + pMt->sdTicks += dTime; + } + else + { + /* Multi track */ + if (((pI->dEndFlag >> dTrack) & 0x01) == 0) + { + __AvMidRemoveFromOrderList(pI); + return (-1); + } + + pMt = &(pI->TrackInfo[dTrack]); + + dTime = 0; + do + { + if (pMt->dOffset >= pMt->dSize) + { + pI->dEndFlag &= ~(1L << dTrack); + __AvMidRemoveFromOrderList(pI); + return (-1); + } + dTemp = (UINT32)pMt->pbBase[pMt->dOffset++]; + dTime = (dTime << 7) + (dTemp & 0x7f); + } while (dTemp >= 0x80); + + pMt->sdTicks += dTime; + __AvMidSortOrderList(pI); + } + + return (0); +} + + +/**************************************************************************** + * __AvMidResetTimeInfo(PMIDINFO pI) + * + * Description: + * Reset time info + * Param: + * pI ... pointer to the data info + * Return: + * none + ****************************************************************************/ +static void +__AvMidResetTimeInfo(PMIDINFO pI) +{ + SINT32 sdTrack; + PTRACKINFO pMt; + + pI->dEndFlag = 0; + + for (sdTrack = 0; sdTrack < (UINT32)pI->dNumOfTracks; sdTrack++) + { + pMt = &(pI->TrackInfo[sdTrack]); + + pMt->dSmfCmd = 0; + pMt->dOffset = 0; + pMt->sdTicks = 0; + if (pMt->dSize > 0) pI->dEndFlag |= (1L << sdTrack); + } + + __AvMidInitializeOrderList(pI); + + if((UINT32)pI->dNumOfTracks > MAX_SMF_TRACKS) + { + debug_error ("__AvMidResetTimeInfo: Num of tracks is over MAX track number. !!\n"); + return; + } + + for (sdTrack = 0; sdTrack < (UINT32)pI->dNumOfTracks; sdTrack++) + { + __AvMidGetTrackTime(pI, (UINT32)sdTrack); + pMt = &(pI->TrackInfo[sdTrack]); + __AvMidInsertOrderList(pI, (UINT32)sdTrack, pMt->sdTicks); + } +} + + +/**************************************************************************** + * __AvMidGetLeastTimeTrack(PMIDINFO pI) + * + * Description: + * Get the track@LeasetTime + * Param: + * pI ... pointer to the setup storage + * Return: + * 0 : NoError, < 0 : Error + ****************************************************************************/ +static SINT32 +__AvMidGetLeastTimeTrack(PMIDINFO pI) +{ + PORDERLIST pTerget; + + pTerget = (pI->pTopOrderList)->pNext; + if (pTerget == pI->pBottomOrderList) return (-1); + + return ((UINT32)pTerget->dTrack); +} + + +/**************************************************************************** + * __AvGetSizeOfFileInfo(PMIDINFO pI) + * + * Description: + * Get SMF info from the file + * Param: + * pI ... pointer to the setup storage + * Return: + * 0 : NoError, < 0 : Error + ****************************************************************************/ +static SINT32 +__AvGetSizeOfFileInfo(PMIDINFO pI) +{ + UINT32 dCmd; + UINT32 dCmd2; + UINT32 dSize; + + UINT32 dTemp; + UINT32 dTime; + SINT32 sdTotalTicks; + SINT32 sdCurrentTime; + SINT32 sdDelta; + PMIDCHINFO pCh; + UINT32 dCh; + + UINT32 dSetup; /* bit0:beat@0, bit1:tempo@0, bit2:GmOn@0, bit3:tempo@1 */ + PTRACKINFO pMt; + SINT32 sdTr; + + static UINT32 dBank[16]; + static UINT32 dCurrBank[16]; + + SINT32 sdNonConductorTime; + SINT32 sdNonConductorTicks; + UINT32 dConductorNote; + dSetup = 0; + sdTotalTicks = 0; + sdCurrentTime = 0; + sdNonConductorTime = 0x7FFFFFFF; + sdNonConductorTicks = 0; + dConductorNote = 0; + sdDelta = (UINT32)(500 << 10) / pI->dTimeResolution; /* default=0.5sec */ + + pI->pbText = NULL; + pI->dSizeText = 0; + pI->pbTitle = NULL; + pI->dSizeTitle = 0; + pI->pbCopyright = NULL; + pI->dSizeCopyright = 0; + pI->dStart = 0; + pI->dSetupBar = 0; + pI->dVibNoteVoice = 0; + + for (dCh = 0; dCh < NUM_OF_MAPS; dCh++) + { + for (dTemp = 0; dTemp < 128; dTemp++) + { + pI->bVoiceMap[dCh][dTemp] = 0; + } + } + pI->bVoiceMap[MELODY_MAP][0] = 1; /* GM Default Piano */ + + for (dCh = 0; dCh < 16; dCh++) + { + dBank[dCh] = 0; + dCurrBank[dCh] = 0; + pCh = &pI->ChInfo[dCh]; + pCh->dKeyCon = 0; + pCh->dVibSync = 0; + pCh->dLedSync = 0; + } + + __AvMidResetTimeInfo(pI); + + if (pI->dSmfFormat != 0) dSetup |= 0x20; + + while (pI->dEndFlag != 0) + { + if ((pI->dEndFlag == 1) && (sdNonConductorTime == 0x7FFFFFFF)) + { + sdNonConductorTime = sdCurrentTime; + sdNonConductorTicks = sdTotalTicks; + dConductorNote |= 2; + } + + if (pI->dNumOfTracks == 1) + { + sdTr = 0; + } + else + { + sdTr = __AvMidGetLeastTimeTrack(pI); + if (sdTr < 0) break; + } + pMt = &(pI->TrackInfo[sdTr]); + + dTime = pMt->sdTicks - sdTotalTicks; + sdCurrentTime += dTime * sdDelta; + sdTotalTicks = pMt->sdTicks; + if ((sdCurrentTime < 0) || (sdTotalTicks > 0x07FFFFFFL)) + { + return (AvSMW_ERROR_LONG_LENGTH); + } + + dCmd = (UINT32)pMt->pbBase[pMt->dOffset++]; + + if (dCmd < 0xf0) + { + /*--- MidiMsg ---*/ + if (dCmd < 0x80) + { + dCmd = pMt->dSmfCmd; + if (dCmd < 0x80) return (AvSMW_ERROR_SMF_CMD); + pMt->dOffset--; + } else { + pMt->dSmfCmd = dCmd; + } + + dCh = dCmd & 0x0f; + + switch (dCmd & 0xf0) + { + case 0x90: /* NoteOn */ + /* Conductor Track Note Check */ + if (sdTr == 0) dConductorNote |= 1; + switch (dCurrBank[dCh] >> 8) + { + case 0x79: + /* Melody */ + break; + + case 0x78: + /* Drum */ + pI->bVoiceMap[DRUM_MAP][pMt->pbBase[pMt->dOffset] & 0x7F] = 1; + break; + + default: + if (dCh == 9) + { + /* Unknown: default GM Drum */ + pI->bVoiceMap[DRUM_MAP][pMt->pbBase[pMt->dOffset] & 0x7F] = 1; + } + } + pMt->dOffset += 2; + break; + + case 0xC0: /* Program change */ + switch (dBank[dCh] >> 8) + { + case 0x79: + if (dBank[dCh] != 0x7906) + { + /* Melody */ + pI->bVoiceMap[MELODY_MAP][pMt->pbBase[pMt->dOffset] & 0x7F] = 1; + } + else + { + /* Vibration Note */ + pI->dVibNoteVoice = 1; + } + break; + + case 0x78: + /* Drum */ + break; + + default: + /* default GM Melody */ + if (dCh != 9) + { + pI->bVoiceMap[MELODY_MAP][pMt->pbBase[pMt->dOffset] & 0x7F] = 1; + } + } + + dCurrBank[dCh] = dBank[dCh]; + pMt->dOffset++; + break; + + case 0xD0: /* Channel pressure */ + pMt->dOffset++; + break; + + case 0xB0: /* Control Change */ + switch (pMt->pbBase[pMt->dOffset]) + { + case 0x00: /* Bank select(MSB) */ + dBank[dCh] = (dBank[dCh] & 0x00FF) | (pMt->pbBase[pMt->dOffset + 1] << 8); + break; + + case 0x20: /* Bank select (LSB) */ + dBank[dCh] = (dBank[dCh] & 0xFF00) | pMt->pbBase[pMt->dOffset + 1]; + break; + } + pMt->dOffset += 2; + break; + + default: + pMt->dOffset += 2; + } + } + else + { + switch (dCmd) + { + case 0xF0: /* SysEx */ + case 0xF7: /* SysEx */ + pMt->dSmfCmd = 0; + dSize = 0; + do + { + dTemp = (UINT32)pMt->pbBase[pMt->dOffset++]; + dSize = (dSize << 7) + (dTemp & 0x7f); + } while (dTemp >= 0x80); + + if ((dSize == 5) && + (pMt->pbBase[pMt->dOffset] == 0x7e) && + (pMt->pbBase[pMt->dOffset + 1] == 0x7f) && + (pMt->pbBase[pMt->dOffset + 2] == 0x09) && + (pMt->pbBase[pMt->dOffset + 3] == 0x01)) + { + /* System On */ + if (sdTotalTicks == 0) + { + dSetup |= 0x04; + } + } + else + { + if (pI->dSetupBar == 0) + { + if ((dSize == 22) && + (pMt->pbBase[pMt->dOffset] == 0x43) && + (pMt->pbBase[pMt->dOffset + 1] == 0x79) && + (pMt->pbBase[pMt->dOffset + 2] == 0x06) && + (pMt->pbBase[pMt->dOffset + 3] == 0x7C)&& + (pMt->pbBase[pMt->dOffset + 4] == 0x02)) + { + /* Channel status */ + for (dCh = 0; dCh < 16; dCh++) + { + pCh = &pI->ChInfo[dCh]; + dTemp = pMt->pbBase[pMt->dOffset + 5 + dCh]; + pCh->dKeyCon = (dTemp >> 2) & 0x03; + pCh->dVibSync = (dTemp >> 1) & 0x01; + pCh->dLedSync = dTemp & 0x01; + } + } + } + } + + pMt->dOffset += dSize; + break; + + case 0xF1: /* System Msg */ + case 0xF3: /* System Msg */ + pMt->dOffset++; + break; + + case 0xF2: /* System Msg */ + pMt->dOffset += 2; + break; + + case 0xFF: /* Meta */ + dCmd2 = (UINT32)pMt->pbBase[pMt->dOffset++]; /* Meta Command */ + dSize = 0; /* Size */ + do + { + dTemp = (UINT32)pMt->pbBase[pMt->dOffset++]; + dSize = (dSize << 7) + (dTemp & 0x7f); + } while (dTemp >= 0x80); + + switch (dCmd2) + { + case 0x01: /* Text */ + if (pI->pbText == NULL) + { + pI->pbText = &pMt->pbBase[pMt->dOffset]; + pI->dSizeText = dSize; + } + break; + + case 0x02: /* Copyright */ + if (pI->pbCopyright == NULL) + { + pI->pbCopyright = &pMt->pbBase[pMt->dOffset]; + pI->dSizeCopyright = dSize; + } + break; + + case 0x03: /* Title */ + if (pI->pbTitle == NULL) + { + pI->pbTitle = &pMt->pbBase[pMt->dOffset]; + pI->dSizeTitle = dSize; + } + break; + + case 0x2f: /* End */ + pI->dEndFlag &= ~(1L << sdTr); + break; + + case 0x51: /* Set Tempo */ + switch (dSize) + { + case 3: + case 4: + dTime = ((UINT32)pMt->pbBase[pMt->dOffset] << 16) + + ((UINT32)pMt->pbBase[pMt->dOffset + 1] << 8) + + (UINT32)pMt->pbBase[pMt->dOffset + 2]; + if ((sdTotalTicks == 0) && (dTime == 250000)) dSetup |= 0x02; + if (sdTotalTicks == (UINT32)pI->dTimeResolution) dSetup |= 0x08; + + /*<== I Think that Below Code is Trash!! and Erase it! (Actually I Don Know ^^) + dTime = (dTime << 7) / 125; */ + + sdDelta = (UINT32)(dTime / pI->dTimeResolution); + } + break; + + case 0x58: /* Set TimeSignature */ + if ((sdTotalTicks == 0) && + (pMt->pbBase[pMt->dOffset] == 1) && + (pMt->pbBase[pMt->dOffset + 1] == 2)) dSetup |= 0x01; + break; + } + pMt->dOffset += dSize; + break; + } + } + + if((UINT32)sdTr >= MAX_SMF_TRACKS) + { + debug_error ("__AvGetSizeOfFileInfo: Num of tracks is over MAX track number. !!\n"); + return AvSMW_ERROR_SMF_CMD; + } + __AvMidUpdateTrackTime(pI, (UINT32)sdTr); + + if (dSetup == 0x0F) + { + dSetup |= 0x10; + sdCurrentTime = 0; + pI->dSetupBar = 1; + pI->dStart = pI->TrackInfo[0].dOffset; + } + } + + if ((dConductorNote != 2) || (pI->dSmfFormat == 0)) + { + pI->sdTotalTicks = sdTotalTicks; + pI->sdDataEndTime = sdCurrentTime; + } + else + { + pI->sdTotalTicks = sdNonConductorTicks; + pI->sdDataEndTime = sdNonConductorTime; + } + + if (pI->dSetupBar == 0) + { + for (dCh = 0; dCh < 16; dCh++) + { + pCh = &pI->ChInfo[dCh]; + pCh->dKeyCon = 0; + pCh->dVibSync = 0; + pCh->dLedSync = 0; + } + } + if ((pI->sdDataEndTime >> 10) <= MINIMUM_LENGTH) return (AvSMW_ERROR_SHORT_LENGTH); + + // printf("__AvGetSizeOfFileInfo/Done\n"); + + return pI->sdDataEndTime; +} + + +/**************************************************************************** + * __AvCheckSizeOfMidFile(UINT8* fp, UINT32 dFsize) + * + * Description: + * Check SMF structure + * Param: + * fp ... pointer to the data + * dFsize ... size fo the data + * dMode ... error check (0:No, 1:Yes, 2:ErrorCheck, 3:CNTI) + * Return: + * 0 : NoError, < 0 : Error + ****************************************************************************/ +static SINT32 +__AvCheckSizeOfMidFile(UINT8* src_fp, UINT32 dFsize) +{ + UINT32 dTemp; + UINT32 dSize; + PMIDINFO pI = NULL; + UINT32 dFormat; + UINT32 dNumOfTracks; + UINT32 i; + UINT8 *fp = src_fp; + // printf ("input param: %p, %d\n", fp , dFsize); + while (dFsize >= 22) + { + dTemp = ((UINT32)fp[0] << 24) + ((UINT32)fp[1] << 16) + + ((UINT32)fp[2] << 8) + (UINT32)fp[3]; + if (dTemp == 0x4D546864) break; /* 'MThd' */ + fp ++; + dFsize --; + } + + // printf("__AvCheckSizeOfMidFile(): MThd Position is dFsize(%d)\n", dFsize); + + if (dFsize < 22) + { + debug_error ("__AvCheckSizeOfMidFile Error / Too small size\n"); + return (AvSMW_ERROR_FILE); + } + + fp += 4; + dFsize -= 4; + + /*--- Check size ----------------------------------------------------*/ + dTemp = ((UINT32)fp[0] << 24) + ((UINT32)fp[1] << 16) + + ((UINT32)fp[2] << 8) + (UINT32)fp[3]; + + if (dTemp != 6) + { + debug_error ("__AvCheckSizeOfMidFile Error / Size != 6\n"); + return (AvSMW_ERROR_CHUNK_SIZE); + } + + fp += 4; + dFsize -= 4; + + if (gpMidInfo->DataInfo[1].dValid == 1) return (AvSMW_ERROR); + pI = &gpMidInfo->DataInfo[1]; + + /** + * set global val + */ + + /*--- Check format -------------------------------------------------*/ + dFormat = ((UINT32)fp[0] << 8) + (UINT32)fp[1]; + if (dFormat > 1) + { + debug_error ("__AvCheckSizeOfMidFile Error/ Not Format 0 or 1\n"); + return (AvSMW_ERROR_SMF_FORMAT); + } + + /*--- Check number of tracks ---------------------------------------*/ + dNumOfTracks = (SINT32)((UINT32)fp[2] << 8) + (UINT32)fp[3]; + if (dNumOfTracks == 0) + { + debug_error ("__AvCheckSizeOfMidFile Error/ Number of Tracks = 0\n"); + return (AvSMW_ERROR_SMF_TRACKNUM); + } + if ((dFormat == 0) && (dNumOfTracks != 1)) + { + debug_error ("__AvCheckSizeOfMidFile Error/ Number of Tracks > 1\n"); + return (AvSMW_ERROR_SMF_TRACKNUM); + } + + if (dNumOfTracks > MAX_SMF_TRACKS) dNumOfTracks = MAX_SMF_TRACKS; + pI->dNumOfTracks = (UINT8)dNumOfTracks; + + /*--- Check Time unit --------------------------------------------*/ + dTemp = ((UINT32)fp[4] << 8) + (UINT32)fp[5]; + pI->dTimeResolution = dTemp & 0x7fff; + if (((dTemp & 0x8000) != 0) || (pI->dTimeResolution == 0)) + { + debug_error ("__AvCheckSizeOfMidFile Error/ Unknown TimeUnit\n"); + return (AvSMW_ERROR_SMF_TIMEUNIT); + } + fp += 6; + dFsize -= 6; + + for (i = 0; i < dNumOfTracks; i++) + { + /*--- Check chunk name --------------------------------------------*/ + while (dFsize >= 8) + { + dTemp = ((UINT32)fp[0] << 24) + ((UINT32)fp[1] << 16) + + ((UINT32)fp[2] << 8) + (UINT32)fp[3]; + if (dTemp == 0x4D54726B) break; /* 'MTrk' */ + fp ++; + dFsize --; + } + + if (dFsize < 8) + { + debug_error ("__AvCheckSizeOfMidFile Error/ Bad size\n"); + return (AvSMW_ERROR_CHUNK_SIZE); + } + + /*--- Check size ----------------------------------------------------*/ + dSize = ((UINT32)fp[4] << 24) + ((UINT32)fp[5] << 16) + + ((UINT32)fp[6] << 8) + (UINT32)fp[7]; + + if (dFsize < (dSize + 8)) + { + debug_error ("__AvCheckSizeOfMidFile Error/ Bad size [%ld] vs [%ld]\n", dFsize, dSize + 22); + return (AvSMW_ERROR_CHUNK_SIZE); + } + pI->TrackInfo[i].pbBase = &fp[8]; + pI->TrackInfo[i].dSize = dSize; + fp += (dSize + 8); + dFsize -= (dSize + 8); + } + pI->dSmfFormat = dFormat; + + /** + * set global + */ + gPi = pI; + + return (__AvGetSizeOfFileInfo(pI)); +} + +static int +__AvParseSkipXmf2Mid(UINT8* pbFile, UINT32 dFSize) +{ + UINT32 skipVal = 0, sizeOfpbFile= dFSize; + while(1) + { + if(pbFile[skipVal] == 'M' && pbFile[skipVal+1] == 'T' && pbFile[skipVal+2] == 'h' && pbFile[skipVal+3] == 'd') + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("__AvParseSkipForXMF : MThd Header found!\n"); + #endif + break; + } + else + { + skipVal++; + if(skipVal >= sizeOfpbFile) + { + debug_error ("__AvParseSkipForXMF : MThd Header is not found!\n"); + debug_error ("__AvParseSkipForXMF :skipVal(%d) sizeOfpbFile(%d) \n", skipVal, sizeOfpbFile); + return -1; + } + } + } + + // printf("__AvParseSkipForXMF : skip value(%d)\n", skipVal); + + return skipVal; +} + diff --git a/formats/ffmpeg/mm_file_format_mmf.c b/formats/ffmpeg/mm_file_format_mmf.c new file mode 100755 index 0000000..37a4bb1 --- /dev/null +++ b/formats/ffmpeg/mm_file_format_mmf.c @@ -0,0 +1,2934 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string.h> /*memcmp*/ +#include <stdlib.h> /*malloc*/ + +#include "mm_debug.h" + +#include "mm_file_utils.h" +#include "mm_file_format_mmf.h" +#include "mm_file_format_private.h" + + +/** + *MMF + * + */ + +#define AV_MMF_CONTENTS_CLASS_0 0x00 +#define AV_MMF_CONTENTS_CLASS_1 0x20 +#define AV_MMF_CONTENTS_CLASS_2 0x10 + +#define AV_MMF_CONTENTS_TYPE_0 0x00 +#define AV_MMF_CONTENTS_TYPE_1 0x10 +#define AV_MMF_CONTENTS_TYPE_2 0x20 +#define AV_MMF_CONTENTS_TYPE_3 0x30 +#define AV_MMF_CONTENTS_TYPE_4 0x40 +#define AV_MMF_CONTENTS_TYPE_5 0x50 + + +#define AVMASMW_SUCCESS (0) /* success */ +#define AVMASMW_ERROR (-1) /* error */ +#define AVMASMW_ERROR_ARGUMENT (-2) /* error of arguments */ +#define AVMASMW_ERROR_RESOURCE_OVER (-3) /* over specified resources */ +#define AVMASMW_ERROR_ID (-4) /* error id number */ +#define AVMASMW_ERROR_TIMEOUT (-5) /* timeout */ +#define AVMASMW_ERROR_SOFTRESET (-6) /* error of soft reset for MA-5 */ + +#define AVMASMW_ERROR_FILE (-16) /* file error */ +#define AVMASMW_ERROR_CONTENTS_CLASS (-17) /* SAVMAF Contents Class shows can't play */ +#define AVMASMW_ERROR_CONTENTS_TYPE (-18) /* SAVMAF Contents Type shows can't play */ +#define AVMASMW_ERROR_CHUNK_SIZE (-19) /* illegal SMAF Chunk Size value */ +#define AVMASMW_ERROR_CHUNK (-20) /* illegal SMAF Track Chunk value */ +#define AVMASMW_ERROR_UNMATCHED_TAG (-21) /* unmathced specified TAG */ +#define AVMASMW_ERROR_SHORT_LENGTH (-22) /* short sequence */ +#define AVMASMW_ERROR_LONG_LENGTH (-23) /* long sequence */ +#define AVMASMW_ERROR_UNSUPPORTED (-24) /* unsupported format */ +#define AVMASMW_ERROR_NO_INFORMATION (-25) /* no specified information */ +#define AVMASMW_ERROR_HV_CONFLICT (-26) /* conflict about HV resource */ + +#define AVMALIB_MAKEDWORD(a, b, c, d) (unsigned int)(((unsigned int)(a) << 24) | \ + ((unsigned int)(b) << 16) | ((unsigned int)(c) << 8) | (unsigned int)(d)) + +/**************************************************************************** + * define + ****************************************************************************/ +#define AVMALIB_SIZE_OF_CHUNKHEADER (8) +#define AVMALIB_SIZE_OF_CRC (2) +#define AVMALIB_SIZE_OF_MIN_CNTI (5) +/* for Phrase */ +#define AVMALIB_CNTI_CLASS_YAMAHA (0x00) +#define AVMALIB_CNTI_TYPE_PHRASE (0xF0) +#define AVMALIB_PHRASE_TIMEBASE (20) + +/* Chunk ID */ +#define AVMALIB_CHUNKID_MMMD 0x4D4D4D44 +#define AVMALIB_CHUNKID_CNTI 0x434E5449 +#define AVMALIB_CHUNKID_OPDA 0x4F504441 +#define AVMALIB_CHUNKID_DCH 0x44636800 +#define AVMALIB_CHUNKID_M5P 0x50726F00 +#define AVMALIB_CHUNKID_MTR 0x4D545200 +#define AVMALIB_CHUNKID_MSPI 0x4D737049 +#define AVMALIB_CHUNKID_MTSU 0x4D747375 +#define AVMALIB_CHUNKID_MTSQ 0x4D747371 +#define AVMALIB_CHUNKID_MTSP 0x4D747370 +#define AVMALIB_CHUNKID_MWA 0x4D776100 +#define AVMALIB_CHUNKID_ATR 0x41545200 +#define AVMALIB_CHUNKID_ASPI 0x41737049 +#define AVMALIB_CHUNKID_ATSU 0x41747375 +#define AVMALIB_CHUNKID_ATSQ 0x41747371 +#define AVMALIB_CHUNKID_AWA 0x41776100 +#define AVMALIB_CHUNKID_MTHV 0x4D746876 +#define AVMALIB_CHUNKID_MHVS 0x4D687673 +#define AVMALIB_CHUNKID_HVP 0x48565000 +#define AVMALIB_CHUNKID_MHSC 0x4D687363 + +/**************************************************************************** + * table + ****************************************************************************/ +static const unsigned short g_crc_tbl[256] = { + 0x0000U,0x1021U,0x2042U,0x3063U,0x4084U,0x50A5U,0x60C6U,0x70E7U, + 0x8108U,0x9129U,0xA14AU,0xB16BU,0xC18CU,0xD1ADU,0xE1CEU,0xF1EFU, + 0x1231U,0x0210U,0x3273U,0x2252U,0x52B5U,0x4294U,0x72F7U,0x62D6U, + 0x9339U,0x8318U,0xB37BU,0xA35AU,0xD3BDU,0xC39CU,0xF3FFU,0xE3DEU, + 0x2462U,0x3443U,0x0420U,0x1401U,0x64E6U,0x74C7U,0x44A4U,0x5485U, + 0xA56AU,0xB54BU,0x8528U,0x9509U,0xE5EEU,0xF5CFU,0xC5ACU,0xD58DU, + 0x3653U,0x2672U,0x1611U,0x0630U,0x76D7U,0x66F6U,0x5695U,0x46B4U, + 0xB75BU,0xA77AU,0x9719U,0x8738U,0xF7DFU,0xE7FEU,0xD79DU,0xC7BCU, + 0x48C4U,0x58E5U,0x6886U,0x78A7U,0x0840U,0x1861U,0x2802U,0x3823U, + 0xC9CCU,0xD9EDU,0xE98EU,0xF9AFU,0x8948U,0x9969U,0xA90AU,0xB92BU, + 0x5AF5U,0x4AD4U,0x7AB7U,0x6A96U,0x1A71U,0x0A50U,0x3A33U,0x2A12U, + 0xDBFDU,0xCBDCU,0xFBBFU,0xEB9EU,0x9B79U,0x8B58U,0xBB3BU,0xAB1AU, + 0x6CA6U,0x7C87U,0x4CE4U,0x5CC5U,0x2C22U,0x3C03U,0x0C60U,0x1C41U, + 0xEDAEU,0xFD8FU,0xCDECU,0xDDCDU,0xAD2AU,0xBD0BU,0x8D68U,0x9D49U, + 0x7E97U,0x6EB6U,0x5ED5U,0x4EF4U,0x3E13U,0x2E32U,0x1E51U,0x0E70U, + 0xFF9FU,0xEFBEU,0xDFDDU,0xCFFCU,0xBF1BU,0xAF3AU,0x9F59U,0x8F78U, + 0x9188U,0x81A9U,0xB1CAU,0xA1EBU,0xD10CU,0xC12DU,0xF14EU,0xE16FU, + 0x1080U,0x00A1U,0x30C2U,0x20E3U,0x5004U,0x4025U,0x7046U,0x6067U, + 0x83B9U,0x9398U,0xA3FBU,0xB3DAU,0xC33DU,0xD31CU,0xE37FU,0xF35EU, + 0x02B1U,0x1290U,0x22F3U,0x32D2U,0x4235U,0x5214U,0x6277U,0x7256U, + 0xB5EAU,0xA5CBU,0x95A8U,0x8589U,0xF56EU,0xE54FU,0xD52CU,0xC50DU, + 0x34E2U,0x24C3U,0x14A0U,0x0481U,0x7466U,0x6447U,0x5424U,0x4405U, + 0xA7DBU,0xB7FAU,0x8799U,0x97B8U,0xE75FU,0xF77EU,0xC71DU,0xD73CU, + 0x26D3U,0x36F2U,0x0691U,0x16B0U,0x6657U,0x7676U,0x4615U,0x5634U, + 0xD94CU,0xC96DU,0xF90EU,0xE92FU,0x99C8U,0x89E9U,0xB98AU,0xA9ABU, + 0x5844U,0x4865U,0x7806U,0x6827U,0x18C0U,0x08E1U,0x3882U,0x28A3U, + 0xCB7DU,0xDB5CU,0xEB3FU,0xFB1EU,0x8BF9U,0x9BD8U,0xABBBU,0xBB9AU, + 0x4A75U,0x5A54U,0x6A37U,0x7A16U,0x0AF1U,0x1AD0U,0x2AB3U,0x3A92U, + 0xFD2EU,0xED0FU,0xDD6CU,0xCD4DU,0xBDAAU,0xAD8BU,0x9DE8U,0x8DC9U, + 0x7C26U,0x6C07U,0x5C64U,0x4C45U,0x3CA2U,0x2C83U,0x1CE0U,0x0CC1U, + 0xEF1FU,0xFF3EU,0xCF5DU,0xDF7CU,0xAF9BU,0xBFBAU,0x8FD9U,0x9FF8U, + 0x6E17U,0x7E36U,0x4E55U,0x5E74U,0x2E93U,0x3EB2U,0x0ED1U,0x1EF0U +}; + +/* phase */ +#define AVMALIB_CHUNK_PHASE_MMMD 0 +#define AVMALIB_CHUNK_PHASE_CNTI 1 +#define AVMALIB_CHUNK_PHASE_MMMDSUB 2 +#define AVMALIB_CHUNK_PHASE_MTRSUB 3 +#define AVMALIB_CHUNK_PHASE_ATRSUB 4 +#define AVMALIB_CHUNK_PHASE_OPDASUB 5 +#define AVMALIB_CHUNK_PHASE_MTSPSUB 6 +#define AVMALIB_CHUNK_PHASE_MTHVSUB 7 + +/* return value */ +#define AVMALIB_CHUNKCODE_MMMD 0x00 +#define AVMALIB_CHUNKCODE_CNTI 0x01 +#define AVMALIB_CHUNKCODE_OPDA 0x02 +#define AVMALIB_CHUNKCODE_DCH 0x20 +#define AVMALIB_CHUNKCODE_M5P 0x21 +#define AVMALIB_CHUNKCODE_MTR 0x03 +#define AVMALIB_CHUNKCODE_MSPI 0x30 +#define AVMALIB_CHUNKCODE_MTSU 0x31 +#define AVMALIB_CHUNKCODE_MTSQ 0x32 +#define AVMALIB_CHUNKCODE_MTSP 0x33 +#define AVMALIB_CHUNKCODE_MWA 0x3F +#define AVMALIB_CHUNKCODE_ATR 0x04 +#define AVMALIB_CHUNKCODE_ASPI 0x40 +#define AVMALIB_CHUNKCODE_ATSU 0x41 +#define AVMALIB_CHUNKCODE_ATSQ 0x42 +#define AVMALIB_CHUNKCODE_AWA 0x43 + +#define AVMALIB_CHUNKCODE_MTHV 0x35 +#define AVMALIB_CHUNKCODE_MHVS 0x36 +#define AVMALIB_CHUNKCODE_HVP 0x37 +#define AVMALIB_CHUNKCODE_MHSC 0x38 + +#define AVMALIB_CHUNKCODE_UNKNOWN 0xFF + +#define AVMALIB_CHUNK_ID_ERROR -1 +#define AVMALIB_CHUNK_SIZE_ERROR -2 + +#define AV_MMF_MAX_TRACK_NUM 8 +#define AV_MMF_MAX_PHRASE_INFO 7 +#define AV_MMF_MAX_EVENT_NUM 10 +#define AV_MMF_MAX_NOTE_OFF_NUM 256 +#define AV_MMF_MAX_STREAM_DATA_NUM2 62 +#define AV_MMF_MAX_STREAM_DATA_NUM3 32 +#define AV_MMF_MAX_VOICE_DATA_NUM2 16 +#define AV_MMF_MAX_WAVE_DATA_NUM3 127 +#define AV_MMF_CHANNEL_NUM 16 + +#define AV_MMF_FUNC_SUCCESS AVMASMW_SUCCESS +#define AV_MMF_FUNC_ERROR AVMASMW_ERROR + +#define AV_MMF_ERR_ARGUMENT AVMASMW_ERROR_ARGUMENT +#define AV_MMF_ERR_FILE AVMASMW_ERROR_FILE +#define AV_MMF_ERR_CLASS AVMASMW_ERROR_CONTENTS_CLASS +#define AV_MMF_ERR_TYPE AVMASMW_ERROR_CONTENTS_TYPE +#define AV_MMF_ERR_SIZE AVMASMW_ERROR_CHUNK_SIZE +#define AV_MMF_ERR_CHUNK AVMASMW_ERROR_CHUNK +#define AV_MMF_ERR_NOTAG AVMASMW_ERROR_UNMATCHED_TAG +#define AV_MMF_ERR_SLENGTH AVMASMW_ERROR_SHORT_LENGTH +#define AV_MMF_ERR_LLENGTH AVMASMW_ERROR_LONG_LENGTH +#define AV_MMF_ERR_NODATA AVMASMW_ERROR_NO_INFORMATION + +#define AV_MMF_SEQ_ID_NULL -1 + +#define AV_MMF_STATUS_IDLE 0 +#define AV_MMF_STATUS_SAT_PROFILE 1 +#define AV_MMF_STATUS_LOADED 2 + +#define AV_MMF_CRC_NULL 0xFFFF0000 +#define AV_MMF_POSITION_OF_CCLASS 16 +#define AV_MMF_POSITION_OF_CTYPE 17 +#define AV_MMF_STSP_OFFSET_NULL 0xFFFFFFFF +#define AV_MMF_STSP_TIME_NULL 0xFFFFFFFF +#define AV_MMF_CHUNK_HEADER_SIZE 8 +#define AV_MMF_FILE_CRC_SIZE 2 +#define AV_MMF_ATR_TRACK_NO AV_MMF_MAX_TRACK_NUM - 1 +#define AV_MMF_MINIMUM_TRACKSIZE2 6 +#define AV_MMF_MINIMUM_TRACKSIZE3 20 +#define AV_MMF_PLAY_TIME_MIN 20 +#define AV_MMF_PLAY_TIME_MAX 0x00200000 +#define AV_MMF_MA2_VOICE_NOTFOUND 0 +#define AV_MMF_MA2_VOICE_FOUND 1 +#define AV_MMF_FM_VOICE_MODE_4OP 0 +#define AV_MMF_FM_VOICE_MODE_2OP 1 + +#define AV_MMF_STREAM_ID_INI 0xFF +#define AV_MMF_STREAM_ID_REGIST 0x00 +#define AV_MMF_MA2_VOICE_NULL 0xFF +#define AV_MMF_MA3_WAVE_NULL 0xFF + + + +#define AV_MMF_HUFFMAN_TREE_FAILURE 0 +#define AV_MMF_HUFFMAN_TREE_SUCCESS 1 + +#define AV_MMF_PHRAZE_SIZE_A 8 +#define AV_MMF_PHRAZE_SIZE_B 12 + +#define AV_MMF_TAG_STARTPOINT 0x7374 +#define AV_MMF_TAG_STOPPOINT 0x7370 +#define AV_MMF_TAG_PHRASE_A 0x5041 +#define AV_MMF_TAG_PHRASE_B 0x5042 +#define AV_MMF_TAG_PHRASE_E 0x5045 +#define AV_MMF_TAG_PHRASE_I 0x5049 +#define AV_MMF_TAG_PHRASE_K 0x504B +#define AV_MMF_TAG_PHRASE_R 0x5052 +#define AV_MMF_TAG_PHRASE_S 0x5053 + +#define AV_MMF_SMAF_TYPE_NULL 0 +#define AV_MMF_SMAF_TYPE_MA1 1 +#define AV_MMF_SMAF_TYPE_MA2 2 +#define AV_MMF_SMAF_TYPE_MA3 3 +#define AV_MMF_SMAF_TYPE_MA5 5 + +#define AV_MMF_FM_MODE_2OP32 0 +#define AV_MMF_FM_MODE_4OP16 1 +#define AV_MMF_P_SOUNDSET_GMX 0 +#define AV_MMF_P_SOUNDSET_GML1 2 +#define AV_MMF_WT_VOLUME_MA3 0 +#define AV_MMF_WT_VOLUME_MA5 4 + +#define AV_MMF_RESOUCE_MODE_MA3 2 +#define AV_MMF_RESOUCE_MODE_MA12 3 +#define AV_MMF_RESOUCE_MODE_MA5 4 +#define AV_MMF_RESOUCE_MODE_MA5_64 5 + +#define AV_MMF_SEQUENCETYPE_DERAYED 0 +#define AV_MMF_AL_CHANNEL_NULL 0xFF +#define AV_MMF_HV_CHANNEL_NULL 16 + + +#define AV_MMF_BANK_NUMBER_DEF 0 +#define AV_MMF_PROGRAM_NUMBER_DEF 0 +#define AV_MMF_RESONANCE_DEF 0x40 +#define AV_MMF_BRIGHTNESS_DEF 0x40 + +#define AV_MMF_SETVOLUME_3RD_PARAM 0x07 + +#define AV_MMF_LED_SEQ_SYNC_OFF 0 +#define AV_MMF_LED_SEQ_SYNC_ON 1 +#define AV_MMF_VIB_SEQ_SYNC_OFF 0 +#define AV_MMF_VIB_SEQ_SYNC_ON 1 +#define AV_MMF_KEYCONTROL_ON 2 +#define AV_MMF_KEYCONTROL_OFF 1 +#define AV_MMF_KEYCONTROL_DEFAULT 0 + +#define AV_MMF_EVNET_EOS 0xFF + +#define AV_MMF_CHANNEL_MODE_POLY 0 +#define AV_MMF_CHANNEL_MODE_MONO 1 + +#define AV_MMF_BANK_NUMBER_DEF 0 +#define AV_MMF_PROGRAM_NUMBER_DEF 0 +#define AV_MMF_RPN_DEF 0x7F +#define AV_MMF_MODULATION_DEF 0 +#define AV_MMF_CHANNEL_VOLUME_DEF 100 +#define AV_MMF_CHANNEL_PAN_DEF 0x40 +#define AV_MMF_EXPRESSION_DEF 0x7F +#define AV_MMF_HOLD_DEF 0 +#define AV_MMF_MONO_POLY_MODE_DEF AV_MMF_CHANNEL_MODE_POLY +#define AV_MMF_PITCH_BEND_DEF 0x40 +#define AV_MMF_BEND_RANGE_DEF 2 +#define AV_MMF_OCTAVE_SHIFT_DEF 4 +#define AV_MMF_VELOCITY_DEF_MA3 90 +#define AV_MMF_VELOCITY_DEF_MA5 100 +#define AV_MMF_BLOCK_FNUM_DEF 0 +#define AV_MMF_STREAM_PAIR_DEF 0xFF +#define AV_MMF_STREAM_PAN_DEF 0xFF +#define AV_MMF_STREAM_PAN_OFF 0x80 + +#define SNDDRV_DEF_STREAM_PAN 255 +#define SNDDRV_DEF_RESONANCE 64 +#define SNDDRV_DEF_BRIGHTNESS 64 +#define SNDDRV_DEF_CHANNEL_VOLUME 100 +#define SNDDRV_DEF_CHANNEL_PANPOT 64 +#define SNDDRV_DEF_EXPRESSION 127 +#define SNDDRV_DEF_BENDRANGE 2 +#define SNDDRV_DEF_PITCHBEND 0x2000 + +#define AV_MMF_CONVERT_PHASE_PLAY 0x00000000 +#define AV_MMF_CONVERT_PHASE_SEEK_G 0x00010000 +#define AV_MMF_CONVERT_PHASE_SEEK_C 0x00020000 +#define AV_MMF_CONVERT_PHASE_SEEK_END 0x00030000 +#define AV_MMF_CONVERT_PHASE_PLAY_END 0x00FF0000 + +#define AV_MMF_SEEK_EVENT_END 0xFFFF + +/**************************************************************************** + * definition of resources + ****************************************************************************/ +#define AVMAPLIB_MAX_LV2_VOICES (16) + +/**************************************************************************** + * typedef + ****************************************************************************/ + +/* OPDA infomation structure */ +typedef struct AvTagOptionInfo +{ + unsigned char *pbCnti; /* pointer to CNTI Body */ + unsigned int dCntiSize; /* size of CNTI Body */ + unsigned char *pbOpda; /* pointer to OPDA Body */ + unsigned int dOpdaSize; /* size of OPDA Body */ +} OPTIONINFO, *POPTIONINFO; + +/* Track information structure */ +typedef struct AvTagTrackInfo +{ + unsigned char *pbMtr; /* pointer to MTR(ATR) Body */ + unsigned int dMtrSize; /* size of MTR(ATR) Body */ + unsigned char *pbMspi; /* pointer to MspI(AspI) Body */ + unsigned int dMspiSize; /* size of MspI(AspI) Body */ + unsigned char *pbMtsu; /* pointer to Mtsu Body */ + unsigned int dMtsuSize; /* size of Mtsu Body */ + unsigned char *pbMtsq; /* pointer to Mtsq(Atsq) Body */ + unsigned int dMtsqSize; /* size of Mtsq(Atsq) Body */ + unsigned char *pbMtsp; /* pointer to Mtsp Body */ + unsigned int dMtspSize; /* size of Mtsp Body */ + unsigned char *pbMthv; /* pointer to Mthv Body */ + unsigned int dMthvSize; /* size of Mthv Body */ + unsigned int dPlayTime; /* play time (tick) */ + unsigned int dTimeBase; /* time base (msec/tick) */ + unsigned int dStartPoint; /* start point(offset) */ + unsigned int dStopPoint; /* stop point(offset) */ + unsigned int dStartTick; /* start point(tick) */ + unsigned int dStopTick; /* stop point(tick) */ +} TRACKINFO, *PTRACKINFO; + +/* Phrase information structure */ +typedef struct AvTagPhraseInfo +{ + unsigned int dStartPoint; /* start point of phrase(offset) */ + unsigned int dStopPoint; /* stop point of phrase(offset) */ + unsigned int dStartTick; /* start point of phrase(tick) */ + unsigned int dStopTick; /* stop point of phrase(tick) */ +} PHRASEINFO, *PPHRASEINFO; + +/* Huffman information structure */ +typedef struct AvTagHuffmanInfo +{ + unsigned int dMtsqSize; /* size of Mtsq(Atsq) Body */ + unsigned int dSeqSize; /* size of sequence data */ + unsigned int dReadSize; /* read data size */ + short swLeft[512]; /* Huffman Tree (Left) */ + short swRight[512]; /* Huffman Tree (Right) */ + unsigned char *psBuffer; /* pointer to reference area */ + char sbBitC; /* counter of reference bit */ + unsigned char bByte; /* value of reference byte */ + unsigned char *psFBuf; /* pointer to sequence data top */ + char sbFBit; /* counter of sequence data top bit */ + unsigned char bFByte ; /* value of sequence data top byte */ +} HUFFMANINFO, *PHUFFMANINFO; + +/* HV information structure */ +typedef struct AvTagHvInfo +{ + unsigned char *pbVoice; /* pointer to HVP0 chunk header */ + unsigned int dVoiceSize; /* size of HV voice parameter */ + unsigned char *pbScript; /* pointer to Mhsc body */ + unsigned int dScriptSize; /* size of Mhsc body */ + unsigned char bHvChannel; /* HV channel # */ +} HVINFO, *PHVINFO; + + +/* Load information structure */ +typedef struct AvTagLoadInfo +{ + unsigned char *pbMmmd; /* pointer to MMMD top */ + unsigned int dMmmdSize; /* size of MMMD (whole) */ + unsigned int dCrc; /* file CRC */ + unsigned int dSmafType; /* SMAF Type */ + unsigned int dPlayTime; /* play time (tick) */ + unsigned int dStartTime; /* start time (start point tick) */ + unsigned int dTimeBase; /* time base (msec/tick) */ + unsigned char (*pfnGetByte)(PHUFFMANINFO psHuf); + OPTIONINFO sOption_Info; + TRACKINFO sTrack_Info[AV_MMF_MAX_TRACK_NUM]; + PHRASEINFO sPhrase_Info[AV_MMF_MAX_PHRASE_INFO]; + HUFFMANINFO sHuffman_Info; + HVINFO sHV_Info; +} LOADINFO, *PLOADINFO; + +/* Stream information structure(for MA-2) */ +typedef struct AvTagStreamInfo2 +{ + unsigned char bStrmID; /* key number of stream */ + unsigned char *pbWave; /* pointer to Awa body */ + unsigned int dWaveSize; /* size of Awa body */ + unsigned int dFs; /* sampling frequency */ +} STREAMINFO2, *PSTREAMINFO2; + +/* Stream information structure(for MA-3/5) */ +typedef struct AvTagStreamInfo3 +{ + unsigned char fbNote; /* stream data flag */ + unsigned char bPairID; /* stream pair ID */ + unsigned char bPan; /* stream pan */ +} STREAMINFO3, *PSTREAMINFO3; + +/* Stream information structure */ +typedef struct AvTagStreamInfo +{ + STREAMINFO2 sStream_Info2[AV_MMF_MAX_STREAM_DATA_NUM2]; + STREAMINFO3 sStream_Info3[AV_MMF_MAX_STREAM_DATA_NUM3]; +} STREAMINFO, *PSTREAMINFO; + +/* Voice information structure(for MA-1/2) */ +typedef struct AvTagVoiceInfo2 +{ + unsigned char bBank; /* bank number */ + unsigned char bProg; /* program number */ +} VOICEINFO2, *PVOICEINFO2; + +/* Wave information structure(for MA-3/5) */ +typedef struct AvTagWaveInfo3 +{ + unsigned int dAdrs; /* wave address */ + unsigned int dSize; /* wave data size */ +} WAVEINFO3, *PWAVEINFO3; + +/* Voice information structure */ +typedef struct AvTagVoiceInfo +{ + VOICEINFO2 sVoice_Info2[AV_MMF_MAX_VOICE_DATA_NUM2]; + WAVEINFO3 sWave_Info3[AV_MMF_MAX_WAVE_DATA_NUM3]; +} VOICEINFO, *PVOICEINFO; + +/* Channel information structure */ +typedef struct AvTagChannelInfo +{ + unsigned char bBankM; /* bank select MSB */ + unsigned char bBankL; /* bank select LSB */ + unsigned char bBank; /* bank number (sound driver) */ + unsigned char bProg; /* program change */ + unsigned char bRpnM; /* RPN MSB */ + unsigned char bRpnL; /* RPN LSB */ + unsigned char bMod; /* modulation */ + unsigned char bChVol; /* channel volume */ + unsigned char bPan; /* channel pan */ + unsigned char bExp; /* expression */ + unsigned char bHold; /* hold */ + unsigned char bMono; /* channel mode mono/poly */ + unsigned char bPitch; /* pitch bend (MSB) */ + unsigned char bSens1; /* pitch bend lenge 1 */ + unsigned char bSens2; /* pitch bend lenge 2 */ + unsigned char bOct; /* octerve shift */ + unsigned char bVel; /* note on velocity */ + unsigned char bBlockFnum1; /* ma-2 pitch bend (0xB0) */ + unsigned char bBlockFnum2; /* ma-2 pitch bend (0xC0) */ + unsigned char fbLed; /* LED synchronous flag */ + unsigned char fbVib; /* Motor synchronous flag */ +} CHANNELINFO, *PCHANNELINFO; + +/* Event information structure */ +typedef struct AvTagEventBlock +{ + unsigned int dEventTime; /* event activation time */ + unsigned int dSeqID; /* sequencer ID (sound driver) */ + unsigned int dCmd; /* command ID (sound driver) */ + unsigned int dParam1; /* parameter 1 */ + unsigned int dParam2; /* parameter 2 */ + unsigned int dParam3; /* parameter 3 */ + unsigned char *pbSeq; /* pointer to next event data */ + unsigned int dIndex; /* index of next event */ + unsigned int dTrackNo; /* track no. */ + void * pvNext; /* pointer to next event block */ +} EVENTBLOCK, *PEVENTBLOCK; + +/* Note OFF information structure */ +typedef struct AvTagNoteOffBlock +{ + unsigned int dOffTime; /* note off activation time */ + unsigned int dSeqID; /* sequencer ID (sound driver) */ + unsigned int dCmd; /* command ID (sound driver) */ + unsigned int dCh; /* channel no. */ + unsigned int dKey; /* key no. */ + void * pvNext; /* pointer to next note off block */ +} OFFBLOCK, *POFFBLOCK; + +/* Playback information structure */ +typedef struct AvTagPlayInfo +{ + PEVENTBLOCK psNextEvent; /* pointer to next event block */ + PEVENTBLOCK psEmptyEvent; /* pointer to empty event block */ + POFFBLOCK psNextOff; /* pointer to next note off block */ + POFFBLOCK psEmptyOff; /* pointer to empty note off block */ + unsigned int dSmafType; /* SMAF Type */ + unsigned int dHWTimeBase; /* tick to H/W time base */ + unsigned int dPlayTime; /* play time (tick) */ + unsigned int dStartTime; /* start time (start point tick) */ + unsigned int dSeekTime; /* seek time (msec) */ + unsigned int dSeekError; /* seek time error (msec) */ + unsigned int dRamAdrs; /* ram address */ + unsigned int dRamSize; /* ram size */ + unsigned int dSeekParam; /* seek event flag */ + unsigned int dPastTime; /* past time (tick) */ + unsigned char bMasterVol; /* master volume (sequence) */ + unsigned char bEos; /* EOS flag */ + unsigned short fwMono; /* channel mode change flag */ + unsigned char bResonance; /* resonance */ + unsigned char bBrightness; /* brightness */ + unsigned char bStreamReserve; /* stream reserve */ + unsigned char bAlChReserve; /* AL channel number */ + unsigned char bHVChannel; /* HV channel number */ + unsigned char bFmVoice; /* FM Voice Mode 0:4op, 1:2op */ + unsigned char (*pfnGetByte)(void); /* pointer to byte get function */ + int (*pfnGetEvent)(void); /* pointer to event get function */ + STREAMINFO sStream_Info; + VOICEINFO sVoice_Info; + CHANNELINFO sChannel_Info[AV_MMF_CHANNEL_NUM + 1]; + EVENTBLOCK sEvent_Block[AV_MMF_MAX_EVENT_NUM]; + OFFBLOCK sNoteOff_Block[AV_MMF_MAX_NOTE_OFF_NUM]; +} PLAYINFO, *PPLAYINFO; + +/* SMAF information structure */ +typedef struct AvTagSmafInfo +{ + int sdMmfSeqID; /* sequence id (sound driver) */ + unsigned int dStatus; /* converter status */ + LOADINFO sLoad_Info[2]; + PLAYINFO sPlay_Info; +} SMAFINFO, *PSMAFINFO; + +static SMAFINFO g_sSmaf_Info; +static const unsigned char g_abBitMaskTable1[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; +static const unsigned short g_awBitMaskTable2[8] = {0x00FF, 0x01FE, 0x03FC, 0x07F8, 0x0FF0, 0x1FE0, 0x3FC0, 0x7F80}; +static const unsigned char g_abTableA[16] = {0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15}; +static const unsigned char g_abTableD[16] = {0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; +static const unsigned char g_abExpression2[16] = {0x80,0x00,0x1f,0x27,0x2f,0x37,0x3f,0x47,0x4f,0x57,0x5f,0x67,0x6f,0x77,0x7f,0x80}; +static const unsigned char g_abModulation2[16] = {0x80,0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x60,0x70,0x7f,0x80}; + +/** + * Define. + */ +#define MMF_FILE_MMF_HEADER_LEN 18 + +#define MMF_FILE_MMF_MAGIC_STR_LEN 4 +#define MMF_FILE_MMF_MAGIC_STR "MMMD" + +#define MMF_FILE_MMF_TYPE_POSITION ((char)0x11) + + + +/* internals */ +static int mmf_file_mmf_get_duration (char *src, int is_xmf); + + + +/* mm plugin porting */ +/* plugin manadatory API */ +int mmfile_format_read_stream_mmf (MMFileFormatContext *formatContext); +int mmfile_format_read_frame_mmf (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame); +int mmfile_format_read_tag_mmf (MMFileFormatContext *formatContext); +int mmfile_format_close_mmf (MMFileFormatContext *formatContext); + + +EXPORT_API +int mmfile_format_open_mmf (MMFileFormatContext *formatContext) +{ + int ret = 0; + + if (NULL == formatContext) { + debug_error ("error: formatContext is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + if (formatContext->pre_checked == 0) { + ret = MMFileFormatIsValidMMF (formatContext->uriFileName); + if (ret == 0) { + debug_error ("error: it is not MMF file\n"); + return MMFILE_FORMAT_FAIL; + } + } + + formatContext->ReadStream = mmfile_format_read_stream_mmf; + formatContext->ReadFrame = mmfile_format_read_frame_mmf; + formatContext->ReadTag = mmfile_format_read_tag_mmf; + formatContext->Close = mmfile_format_close_mmf; + + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = 1; + + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_read_stream_mmf (MMFileFormatContext *formatContext) +{ + int total = 0; + + total = mmf_file_mmf_get_duration (formatContext->uriFileName, 0 /*not XMF*/); + if ( total < 0 ) + { + debug_error ("error: get duration\n"); + return MMFILE_FORMAT_FAIL; + } + + + formatContext->duration = total; + formatContext->audioTotalTrackNum = 1; + formatContext->nbStreams = 1; + formatContext->streams[MMFILE_AUDIO_STREAM] = mmfile_malloc (sizeof(MMFileFormatStream)); + if (NULL == formatContext->streams[MMFILE_AUDIO_STREAM]) + { + debug_error ("error: mmfile_malloc, audido stream\n"); + return MMFILE_FORMAT_FAIL; + } + + formatContext->streams[MMFILE_AUDIO_STREAM]->codecId =MM_AUDIO_CODEC_MMF; + formatContext->streams[MMFILE_AUDIO_STREAM]->nbChannel = 1; + + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_read_frame_mmf (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame) +{ + return MMFILE_FORMAT_SUCCESS; +} + + +EXPORT_API +int mmfile_format_read_tag_mmf (MMFileFormatContext *formatContext) +{ + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_close_mmf (MMFileFormatContext *formatContext) +{ + return MMFILE_FORMAT_SUCCESS; +} + + +/********************************************************************************* + * _mmf_Get4Byte + * + * Description: + * SMAF data load (error check and regist) + * Argument: + * pbBuf pointer to top data + * Return: + * nothing + ********************************************************************************/ +static unsigned int +_mmf_Get4Byte(unsigned char* pbBuf) +{ + return (unsigned int)( (((unsigned int)pbBuf[0]) << 24) + + (((unsigned int)pbBuf[1]) << 16) + + (((unsigned int)pbBuf[2]) << 8) + + ((unsigned int)pbBuf[3])); +} + + +/********************************************************************************* + * _mmf_CheckInitial + * + * Description: + * SMAF data load (error check and regist) + * Argument: + * psLoad pointer to load information structure + * Return: + * nothing + ********************************************************************************/ +static void +_mmf_CheckInitial(PLOADINFO psLoad) +{ + unsigned char i; + +/* Initialize Load information structure */ + psLoad->pbMmmd = NULL; + psLoad->dMmmdSize = 0; + psLoad->dCrc = AV_MMF_CRC_NULL; + psLoad->dSmafType = AV_MMF_SMAF_TYPE_NULL; + psLoad->dPlayTime = 0; + psLoad->dStartTime = 0; + psLoad->dTimeBase = 0; + psLoad->pfnGetByte = NULL; + + psLoad->sOption_Info.pbCnti = NULL; + psLoad->sOption_Info.dCntiSize = 0; + psLoad->sOption_Info.pbOpda = NULL; + psLoad->sOption_Info.dOpdaSize = 0; + + for ( i = 0; i < AV_MMF_MAX_TRACK_NUM; i++) + { + psLoad->sTrack_Info[i].pbMtr = NULL; + psLoad->sTrack_Info[i].dMtrSize = 0; + psLoad->sTrack_Info[i].pbMspi = NULL; + psLoad->sTrack_Info[i].dMspiSize = 0; + psLoad->sTrack_Info[i].pbMtsu = NULL; + psLoad->sTrack_Info[i].dMtsuSize = 0; + psLoad->sTrack_Info[i].pbMtsq = NULL; + psLoad->sTrack_Info[i].dMtsqSize = 0; + psLoad->sTrack_Info[i].pbMtsp = NULL; + psLoad->sTrack_Info[i].dMtspSize = 0; + psLoad->sTrack_Info[i].pbMthv = NULL; + psLoad->sTrack_Info[i].dMthvSize = 0; + psLoad->sTrack_Info[i].dPlayTime = 0; + psLoad->sTrack_Info[i].dTimeBase = 0; + psLoad->sTrack_Info[i].dStartPoint = AV_MMF_STSP_OFFSET_NULL; + psLoad->sTrack_Info[i].dStopPoint = AV_MMF_STSP_OFFSET_NULL; + psLoad->sTrack_Info[i].dStartTick = AV_MMF_STSP_TIME_NULL; + psLoad->sTrack_Info[i].dStopTick = AV_MMF_STSP_TIME_NULL; + } + + for ( i = 0; i < AV_MMF_MAX_PHRASE_INFO; i++) + { + psLoad->sPhrase_Info[i].dStartPoint = AV_MMF_STSP_OFFSET_NULL; + psLoad->sPhrase_Info[i].dStopPoint = AV_MMF_STSP_OFFSET_NULL; + psLoad->sPhrase_Info[i].dStartTick = AV_MMF_STSP_TIME_NULL; + psLoad->sPhrase_Info[i].dStopTick = AV_MMF_STSP_TIME_NULL; + } + + psLoad->sHV_Info.pbVoice = NULL; + psLoad->sHV_Info.dVoiceSize = 0; + psLoad->sHV_Info.pbScript = NULL; + psLoad->sHV_Info.dScriptSize = 0; + psLoad->sHV_Info.bHvChannel = AV_MMF_HV_CHANNEL_NULL; +} + + +/********************************************************************************* + * _mmf_CheckInitial + * + * Description: + * get time base (data value -> msec/tick) + * Argument: + * bData time base (data value) + * Return: + * >=0 success(time base (msec/tick)) + * < 0 error code + ********************************************************************************/ +static int +_mmf_GetTimebase(unsigned char bData) +{ + switch ( bData ) + { + case 0x02: return 4; /* 4[msec/tick] */ + case 0x03: return 5; /* 5[msec/tick] */ + case 0x10: return 10; /* 10[msec/tick] */ + case 0x11: return 20; /* 20[msec/tick] */ + case 0x12: return 40; /* 40[msec/tick] */ + case 0x13: return 50; /* 50[msec/tick] */ + default: return AV_MMF_FUNC_ERROR; /* Time Base Error */ + } +} + +static int +_mmf_MalibNextChunk(unsigned char* pbFile, unsigned int dSize, unsigned int dState, unsigned int* pdChunkID, unsigned int* pdChunkNo) +{ + unsigned int dChunkID, dChunkSize; + + if (dSize < AVMALIB_SIZE_OF_CHUNKHEADER) + return AVMALIB_CHUNK_SIZE_ERROR; + + dChunkID = AVMALIB_MAKEDWORD(pbFile[0], pbFile[1], pbFile[2], pbFile[3]); + dChunkSize = AVMALIB_MAKEDWORD(pbFile[4], pbFile[5], pbFile[6], pbFile[7]); + + switch (dChunkID) + { + case AVMALIB_CHUNKID_MMMD : + *pdChunkID = AVMALIB_CHUNKCODE_MMMD; + if (dState != AVMALIB_CHUNK_PHASE_MMMD) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_CNTI : + *pdChunkID = AVMALIB_CHUNKCODE_CNTI; + if (dState != AVMALIB_CHUNK_PHASE_CNTI) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_OPDA : + *pdChunkID = AVMALIB_CHUNKCODE_OPDA; + if (dState != AVMALIB_CHUNK_PHASE_MMMDSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_MSPI : + *pdChunkID = AVMALIB_CHUNKCODE_MSPI; + if (dState != AVMALIB_CHUNK_PHASE_MTRSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_MTSU : + *pdChunkID = AVMALIB_CHUNKCODE_MTSU; + if (dState != AVMALIB_CHUNK_PHASE_MTRSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_MTSQ : + *pdChunkID = AVMALIB_CHUNKCODE_MTSQ; + if (dState != AVMALIB_CHUNK_PHASE_MTRSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_MTSP : + *pdChunkID = AVMALIB_CHUNKCODE_MTSP; + if (dState != AVMALIB_CHUNK_PHASE_MTRSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_ASPI : + *pdChunkID = AVMALIB_CHUNKCODE_ASPI; + if (dState != AVMALIB_CHUNK_PHASE_ATRSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_ATSU : + *pdChunkID = AVMALIB_CHUNKCODE_ATSU; + if (dState != AVMALIB_CHUNK_PHASE_ATRSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_ATSQ : + *pdChunkID = AVMALIB_CHUNKCODE_ATSQ; + if (dState != AVMALIB_CHUNK_PHASE_ATRSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + + case AVMALIB_CHUNKID_MTHV : + *pdChunkID = AVMALIB_CHUNKCODE_MTHV; + if (dState != AVMALIB_CHUNK_PHASE_MTRSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_MHVS : + *pdChunkID = AVMALIB_CHUNKCODE_MHVS; + if (dState != AVMALIB_CHUNK_PHASE_MTHVSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_MHSC : + *pdChunkID = AVMALIB_CHUNKCODE_MHSC; + if (dState != AVMALIB_CHUNK_PHASE_MTHVSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + + default : + *pdChunkNo = (unsigned char)(dChunkID & 0x000000FF); + switch (dChunkID & 0xFFFFFF00) + { + case AVMALIB_CHUNKID_MTR : + *pdChunkID = AVMALIB_CHUNKCODE_MTR; + if (dState != AVMALIB_CHUNK_PHASE_MMMDSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_ATR : + *pdChunkID = AVMALIB_CHUNKCODE_ATR; + if (dState != AVMALIB_CHUNK_PHASE_MMMDSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + + case AVMALIB_CHUNKID_DCH : + *pdChunkID = AVMALIB_CHUNKCODE_DCH; + if (dState != AVMALIB_CHUNK_PHASE_OPDASUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + case AVMALIB_CHUNKID_M5P : + *pdChunkID = AVMALIB_CHUNKCODE_M5P; + if (dState != AVMALIB_CHUNK_PHASE_OPDASUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + + case AVMALIB_CHUNKID_MWA : + *pdChunkID = AVMALIB_CHUNKCODE_MWA; + if (dState != AVMALIB_CHUNK_PHASE_MTSPSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + + case AVMALIB_CHUNKID_AWA : + *pdChunkID = AVMALIB_CHUNKCODE_AWA; + if (dState != AVMALIB_CHUNK_PHASE_ATRSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + + case AVMALIB_CHUNKID_HVP : + *pdChunkID = AVMALIB_CHUNKCODE_HVP; + if (dState != AVMALIB_CHUNK_PHASE_MTHVSUB) + return AVMALIB_CHUNK_ID_ERROR; + break; + + default : + *pdChunkID = AVMALIB_CHUNKCODE_UNKNOWN; + break; + } + break; + } + + if (dChunkSize > (dSize - AVMALIB_SIZE_OF_CHUNKHEADER)) + return AVMALIB_CHUNK_SIZE_ERROR; + else + return (int)dChunkSize; +} + + + +/********************************************************************************* + * _mmf_MTRCheck + * + * Description: + * score track chunk check (header information and chunk construction) + * Argument: + * psTrack pointer to track information structure + * bSmafType SMAF type + * Return: + * 0 success + * < 0 error code + ********************************************************************************/ +static int +_mmf_MTRCheck(PTRACKINFO psTrack, unsigned char bSmafType) +{ + int sdResult, sdChunkSize; + unsigned int dSize, dIndex; + unsigned char* pbBuf; + unsigned int dChunkID, dChunkNo; + +/* Check Format Type */ + switch (bSmafType) + { + case AV_MMF_SMAF_TYPE_MA1 : + case AV_MMF_SMAF_TYPE_MA2 : + if (psTrack->pbMtr[0] != 0x00) + { + return AV_MMF_ERR_CHUNK; + } + break; + case AV_MMF_SMAF_TYPE_MA3 : + if ((psTrack->pbMtr[0] != 0x01) && (psTrack->pbMtr[0] != 0x02)) + { + return AV_MMF_ERR_CHUNK; + } + break; + case AV_MMF_SMAF_TYPE_MA5 : + if (psTrack->pbMtr[0] != 0x02) + { + return AV_MMF_ERR_CHUNK; + } + break; + } + +/* Check Sequence Type */ + if (psTrack->pbMtr[1] != 0x00) + { + return AV_MMF_ERR_CHUNK; + } + +/* Check Time Base */ + if (psTrack->pbMtr[2] != psTrack->pbMtr[3]) + { + return AV_MMF_ERR_CHUNK; + } + sdResult = _mmf_GetTimebase(psTrack->pbMtr[2]); + if (sdResult == AV_MMF_FUNC_ERROR) + { + return AV_MMF_ERR_CHUNK; + } + psTrack->dTimeBase = (unsigned int)sdResult; + +/* Check sub chunk disposition */ + if ((bSmafType == AV_MMF_SMAF_TYPE_MA1) || (bSmafType == AV_MMF_SMAF_TYPE_MA2)) + dIndex = AV_MMF_MINIMUM_TRACKSIZE2; + else + dIndex = AV_MMF_MINIMUM_TRACKSIZE3; + pbBuf = psTrack->pbMtr; + dSize = psTrack->dMtrSize; + while (dSize > (dIndex + AV_MMF_CHUNK_HEADER_SIZE)) + { + sdChunkSize = _mmf_MalibNextChunk(&pbBuf[dIndex], (dSize-dIndex), + AVMALIB_CHUNK_PHASE_MTRSUB, &dChunkID, &dChunkNo); + if (sdChunkSize < 0) + { + return AV_MMF_ERR_CHUNK; + } + dIndex += AV_MMF_CHUNK_HEADER_SIZE; + switch (dChunkID) { + case AVMALIB_CHUNKCODE_MSPI : + psTrack->pbMspi = &(pbBuf[dIndex]); + psTrack->dMspiSize = sdChunkSize; + break; + case AVMALIB_CHUNKCODE_MTSU : + psTrack->pbMtsu = &(pbBuf[dIndex]); + psTrack->dMtsuSize = sdChunkSize; + break; + case AVMALIB_CHUNKCODE_MTSQ : + psTrack->pbMtsq = &(pbBuf[dIndex]); + psTrack->dMtsqSize = sdChunkSize; + break; + case AVMALIB_CHUNKCODE_MTSP : + psTrack->pbMtsp = &(pbBuf[dIndex]); + psTrack->dMtspSize = sdChunkSize; + break; + case AVMALIB_CHUNKCODE_MTHV : + psTrack->pbMthv = &(pbBuf[dIndex]); + psTrack->dMthvSize = sdChunkSize; + break; + default : + break; + } + dIndex += sdChunkSize; + } + + if ((psTrack->pbMtsq == NULL) ||(psTrack->dMtsqSize == 0)) + { + return AV_MMF_ERR_SLENGTH; + } + return AV_MMF_FUNC_SUCCESS; +} + + +/********************************************************************************* + * _mmf_ATRCheck + * + * Description: + * audio track chunk check (header information and chunk construction) + * Argument: + * psTrack pointer to track information structure + * Return: + * 0 success + * < 0 error code + ********************************************************************************/ +static int +_mmf_ATRCheck(PTRACKINFO psTrack) +{ + int sdResult, sdChunkSize; + unsigned int dSize, dIndex; + unsigned char* pbBuf; + unsigned int dChunkID, dChunkNo; + unsigned char fbWave; + +/* Check Format Type */ + if (psTrack->pbMtr[0] != 0x00) + { + return AV_MMF_ERR_CHUNK; + } + +/* Check Sequence Type */ + if (psTrack->pbMtr[1] != 0x00) + { + return AV_MMF_ERR_CHUNK; + } + +/* Check Wave Type */ + if (((psTrack->pbMtr[2] != 0x10) && (psTrack->pbMtr[2] != 0x11)) || + ((psTrack->pbMtr[3] & 0xF0) != 0x00)) + { + return AV_MMF_ERR_CHUNK; + } + +/* Check Time Base */ + if (psTrack->pbMtr[4] != psTrack->pbMtr[5]) + { + return AV_MMF_ERR_CHUNK; + } + sdResult = _mmf_GetTimebase(psTrack->pbMtr[4]); + if (sdResult == AV_MMF_FUNC_ERROR) + { + return AV_MMF_ERR_CHUNK; + } + psTrack->dTimeBase = (unsigned int)sdResult; + + pbBuf = psTrack->pbMtr; + dSize = psTrack->dMtrSize; + dIndex = 6; + fbWave = AV_MMF_MA2_VOICE_NULL; + +/* Check sub chunk disposition */ + while (dSize > (dIndex + AV_MMF_CHUNK_HEADER_SIZE)) + { + sdChunkSize = _mmf_MalibNextChunk(&pbBuf[dIndex], (dSize-dIndex), + AVMALIB_CHUNK_PHASE_ATRSUB, &dChunkID, &dChunkNo); + if (sdChunkSize < 0) + { + return AV_MMF_ERR_CHUNK; + } + dIndex += AV_MMF_CHUNK_HEADER_SIZE; + switch (dChunkID) + { + case AVMALIB_CHUNKCODE_ASPI : + psTrack->pbMspi = &(pbBuf[dIndex]); + psTrack->dMspiSize = sdChunkSize; + break; + case AVMALIB_CHUNKCODE_ATSQ : + psTrack->pbMtsq = &(pbBuf[dIndex]); + psTrack->dMtsqSize = sdChunkSize; + break; + case AVMALIB_CHUNKCODE_AWA : + if ((0x01 <= dChunkNo) && (dChunkNo <= 0x3E)) + fbWave = AV_MMF_MA2_VOICE_FOUND; + break; + default : + break; + } + dIndex += sdChunkSize; + } + + if ((psTrack->pbMtsq == NULL) ||(psTrack->dMtsqSize == 0) ||(fbWave == AV_MMF_MA2_VOICE_NULL)) + { + return AV_MMF_ERR_SLENGTH; + } + return AV_MMF_FUNC_SUCCESS; +} + + +/********************************************************************************* + * _mmf_MspICheck + * + * Description: + * seek & phrase info chunk check (get phrase information) + * Argument: + * psTrack pointer to track information structure + * psPhrase pointer to phrase information structure + * Return: + * 0 success + * < 0 error code + ********************************************************************************/ +static void +_mmf_MspICheck(PTRACKINFO psTrack, PPHRASEINFO psPhrase) +{ + unsigned char* pbBuf; + unsigned int dSize, dIndex; + unsigned short wTag; + + if (psTrack->pbMspi == NULL) + return; + + pbBuf = psTrack->pbMspi; + dSize = psTrack->dMspiSize; + dIndex = 0; + + while (dSize >= dIndex + AV_MMF_PHRAZE_SIZE_A) + { + wTag = (unsigned short)((((unsigned short)pbBuf[dIndex]) << 8) + (unsigned short)pbBuf[dIndex+1]); + switch (wTag) { + case AV_MMF_TAG_STARTPOINT : /* start point */ + psTrack->dStartPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 3])); + dIndex += AV_MMF_PHRAZE_SIZE_A; + break; + case AV_MMF_TAG_STOPPOINT : /* stop point */ + psTrack->dStopPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 3])); + dIndex += AV_MMF_PHRAZE_SIZE_A; + break; + case AV_MMF_TAG_PHRASE_A : /* A melody */ + if (dSize < dIndex + AV_MMF_PHRAZE_SIZE_B) + return ; + psPhrase[0].dStartPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 3])); + psPhrase[0].dStopPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 7])); + dIndex += AV_MMF_PHRAZE_SIZE_B; + break; + case AV_MMF_TAG_PHRASE_B : /* B melody */ + if (dSize < dIndex + AV_MMF_PHRAZE_SIZE_B) + return ; + psPhrase[1].dStartPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 3])); + psPhrase[1].dStopPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 7])); + dIndex += AV_MMF_PHRAZE_SIZE_B; + break; + case AV_MMF_TAG_PHRASE_E : /* Ending */ + if (dSize < dIndex + AV_MMF_PHRAZE_SIZE_B) + return ; + psPhrase[2].dStartPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 3])); + psPhrase[2].dStopPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 7])); + dIndex += AV_MMF_PHRAZE_SIZE_B; + break; + case AV_MMF_TAG_PHRASE_I : /* Intro */ + if (dSize < dIndex + AV_MMF_PHRAZE_SIZE_B) + return ; + psPhrase[3].dStartPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 3])); + psPhrase[3].dStopPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 7])); + dIndex += AV_MMF_PHRAZE_SIZE_B; + break; + case AV_MMF_TAG_PHRASE_K : /* Interlude */ + if (dSize < dIndex + AV_MMF_PHRAZE_SIZE_B) + return ; + psPhrase[4].dStartPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 3])); + psPhrase[4].dStopPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 7])); + dIndex += AV_MMF_PHRAZE_SIZE_B; + break; + case AV_MMF_TAG_PHRASE_R : /* Refrain */ + if (dSize < dIndex + AV_MMF_PHRAZE_SIZE_B) + return ; + psPhrase[5].dStartPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 3])); + psPhrase[5].dStopPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 7])); + dIndex += AV_MMF_PHRAZE_SIZE_B; + break; + case AV_MMF_TAG_PHRASE_S : /* Bridge */ + if (dSize < dIndex + AV_MMF_PHRAZE_SIZE_B) + return ; + psPhrase[6].dStartPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 3])); + psPhrase[6].dStopPoint = _mmf_Get4Byte(&(pbBuf[dIndex + 7])); + dIndex += AV_MMF_PHRAZE_SIZE_B; + break; + default : + return; + } + } + return; +} + + +/********************************************************************************* + * _mmf_STSPCheck + * + * Description: + * track chunk check (offset of start point and stop point) + * Argument: + * psTrack pointer to track information structure + * Return: + * 0 success + * < 0 error code + ********************************************************************************/ +static int +_mmf_STSPCheck( PTRACKINFO psTrack) +{ + unsigned int dStart, dStop, dSize; + + dSize = psTrack->dMtsqSize; + + if (psTrack->dStartPoint == AV_MMF_STSP_OFFSET_NULL) + dStart = 0; + else + dStart = psTrack->dStartPoint; + + if (psTrack->dStopPoint == AV_MMF_STSP_OFFSET_NULL) + dStop = dSize; + else + dStop = psTrack->dStopPoint; + + if ((dStart >= dStop) || (dStart > dSize) || (dStop > dSize)) + return AV_MMF_ERR_CHUNK; + + return AV_MMF_FUNC_SUCCESS; +} + + +/********************************************************************************* + * _mmf_MtsuCheck2 + * + * Description: + * track chunk check (existence voice parameter) + * Argument: + * psTrack pointer to track information structure + * bSmafType SMAF type + * Return: + * 0 success + * < 0 error code + ********************************************************************************/ +static int +_mmf_MtsuCheck2(PTRACKINFO psTrack, unsigned char bSmafType) +{ + unsigned char* pbBuf; + unsigned int dSize, dIndex; + + if (psTrack->pbMtsu == NULL) + { + return AV_MMF_MA2_VOICE_NOTFOUND; + } + + pbBuf = psTrack->pbMtsu; + dSize = psTrack->dMtsuSize; + dIndex = 0; + + while (dSize > dIndex + 20) + { + if ((pbBuf[dIndex] != 0xFF) || (pbBuf[dIndex + 1] != 0xF0)) + { + return AV_MMF_MA2_VOICE_NOTFOUND; + } + if (pbBuf[dIndex + 3] == 0x43) + { + if ((bSmafType == AV_MMF_SMAF_TYPE_MA1) && (pbBuf[dIndex + 4] == 0x02)) + return AV_MMF_MA2_VOICE_FOUND; + if ((bSmafType == AV_MMF_SMAF_TYPE_MA2) && (pbBuf[dIndex + 4] == 0x03)) + return AV_MMF_MA2_VOICE_FOUND; + } + dIndex += (pbBuf[dIndex + 2] + 3); + } + + return AV_MMF_MA2_VOICE_NOTFOUND; +} + + +/********************************************************************************* + * _mmf_GetFlex2L + * + * Description: + * get flex data (duration, gate time) + * Argument: + * pbBuf pointer to data top + * dSize size of data (remain) + * pdRead pointer to size of flex data + * Return: + * >=0 success(flex data value) + * < 0 error code + ********************************************************************************/ +static int +_mmf_GetFlex2L(unsigned char* pbBuf, unsigned int dSize, unsigned int* pbRead) +{ + int sdTemp; + + if ((dSize < 1) || ((dSize < 2) && (pbBuf[0] >= 0x80))) + return AV_MMF_FUNC_ERROR; + if (dSize >= 4) + { + sdTemp = pbBuf[0] + pbBuf[1] + pbBuf[2] + pbBuf[3]; + if (sdTemp == 0) + return AV_MMF_FUNC_ERROR; + } + if (pbBuf[0] >= 0x80) + { + sdTemp = (int)( (((int)(pbBuf[0] & 0x7F)) << 7) + + ((int)(pbBuf[1] & 0x7F)) + 128 ); + *pbRead = 2; + } + else + { + sdTemp = (int)(pbBuf[0] & 0x7F); + *pbRead = 1; + } + return sdTemp; +} + + +/********************************************************************************* + * _mmf_SeqDataCheck2 + * + * Description: + * track chunk check (sequence massage) + * Argument: + * psTrack pointer to track information structure + * bSmafType SMAF type + * Return: + * 0 success + * < 0 error code + ********************************************************************************/ +static int +_mmf_SeqDataCheck2(PTRACKINFO psTrack, unsigned char bSmafType) +{ + unsigned char* pbBuf; + unsigned int dSize, dIndex; + int sdTemp; + unsigned int dPast, dGate, dFlexSize; + + if (psTrack->pbMtsq == NULL) + { + return AV_MMF_ERR_SLENGTH; + } + + dPast = 0; + dGate = 0; + pbBuf = psTrack->pbMtsq; + dSize = psTrack->dMtsqSize; + dIndex = 0; + +/* scanning to EOS or stop point */ + while ( dSize > dIndex ) + { + if (psTrack->dStartPoint== dIndex) /* start point */ + psTrack->dStartTick = dPast; + if (psTrack->dStopPoint == dIndex) + { /* stop point */ + psTrack->dStopTick = dPast; + break; + } + + if (dSize >= dIndex + 4) + { + sdTemp=pbBuf[dIndex]+pbBuf[dIndex+1]+pbBuf[dIndex+2]+pbBuf[dIndex+3]; + if (sdTemp == 0) + { /* EOS */ + if (bSmafType == AV_MMF_SMAF_TYPE_MA1) + psTrack->dStopTick = dPast + dGate; + else + psTrack->dStopTick = dPast; + break; + } + } + + sdTemp = _mmf_GetFlex2L(&pbBuf[dIndex], (dSize - dIndex), &dFlexSize); + if (sdTemp < 0) + { + return AV_MMF_ERR_CHUNK; + } + dPast += sdTemp; + if (dPast >= AV_MMF_PLAY_TIME_MAX) + { + return AV_MMF_ERR_LLENGTH; + } + + if ((unsigned int)sdTemp >= dGate) /* calculate remain of GT */ + dGate = 0; + else + dGate -= sdTemp; + dIndex += dFlexSize; + + if (dSize < dIndex + 2) + { + return AV_MMF_ERR_CHUNK; + } + + switch (pbBuf[dIndex]) + { + case 0x00 : + if ((pbBuf[dIndex + 1] & 0x30) != 0x30) + dIndex += 2; + else + dIndex += 3; + break; + case 0xFF : + switch (pbBuf[dIndex + 1]) + { + case 0x00 : + dIndex += 2; + break; + case 0xF0 : + if (dSize < dIndex + 3) + { + return AV_MMF_ERR_CHUNK; + } + dIndex += (pbBuf[dIndex + 2] + 3); + if (dSize < dIndex) { + return AV_MMF_ERR_CHUNK; + } + if (pbBuf[dIndex - 1] != 0xF7) + { + return AV_MMF_ERR_CHUNK; + } + break; + default : + return AV_MMF_ERR_CHUNK; + } + break; + default : + sdTemp = _mmf_GetFlex2L(&pbBuf[dIndex+1], (dSize - dIndex- 1), &dFlexSize); + if (sdTemp < 0) + { + return AV_MMF_ERR_CHUNK; + } + if (dGate < (unsigned int)sdTemp) + dGate = (unsigned int)sdTemp; + dIndex += (1 + dFlexSize); + break; + } + if (dSize < dIndex) { + return AV_MMF_ERR_CHUNK; + } + } + + + if (psTrack->dStartTick == AV_MMF_STSP_TIME_NULL) + { + if (psTrack->dStartPoint != AV_MMF_STSP_OFFSET_NULL) + { + return AV_MMF_ERR_CHUNK; + } + psTrack->dStartTick = 0; + } + +/* check start/stop point potision */ + if (psTrack->dStopTick == AV_MMF_STSP_TIME_NULL) + { + if ((psTrack->dStopPoint != AV_MMF_STSP_OFFSET_NULL) && (psTrack->dStopPoint != dIndex)) + { + return AV_MMF_ERR_CHUNK; + } + if (bSmafType == AV_MMF_SMAF_TYPE_MA1) + psTrack->dStopTick = dPast + dGate; + else + psTrack->dStopTick = dPast; + } + +/* calculate playback time of this track */ + psTrack->dPlayTime = psTrack->dStopTick - psTrack->dStartTick; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Play time: %ld\n", psTrack->dPlayTime); + #endif + + return AV_MMF_FUNC_SUCCESS; +} + + +/********************************************************************************* + * TrackChunk_Check2 + * + * Description: + * MA-1/2 track chunk check + * Argument: + * psLoad pointer to load information structure + * Return: + * 0 success + * < 0 error code + ********************************************************************************/ +static int +_mmf_TrackChunkCheck2(PLOADINFO psLoad) +{ + PTRACKINFO psTrack; + int sdResult; + unsigned char i, fbVoice; + +/* delete track information of MA-3/5 */ + psLoad->sTrack_Info[5].pbMtr = NULL; + psLoad->sTrack_Info[5].dMtrSize = 0; + psLoad->sTrack_Info[6].pbMtr = NULL; + psLoad->sTrack_Info[6].dMtrSize = 0; + +/* fix SMAF Type */ + psLoad->dSmafType = AV_MMF_SMAF_TYPE_MA1; + for (i = 1; i < 5; i++) + { + if (psLoad->sTrack_Info[i].pbMtr != NULL) + { + psLoad->dSmafType = AV_MMF_SMAF_TYPE_MA2; + break; + } + } + if (psLoad->sTrack_Info[AV_MMF_ATR_TRACK_NO].pbMtr != NULL) + psLoad->dSmafType = AV_MMF_SMAF_TYPE_MA2; + + if (psLoad->dSmafType == AV_MMF_SMAF_TYPE_MA1) + { /* MA-1 */ + if (psLoad->sTrack_Info[0].pbMtr == NULL) + { + return AV_MMF_ERR_SLENGTH; + } + psTrack = &(psLoad->sTrack_Info[0]); + if (psTrack->dMtrSize <= AV_MMF_MINIMUM_TRACKSIZE2) + { + return AV_MMF_ERR_CHUNK; + } + sdResult = _mmf_MTRCheck(psTrack, AV_MMF_SMAF_TYPE_MA1); + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + _mmf_MspICheck(psTrack, &(psLoad->sPhrase_Info[0])); + sdResult = _mmf_STSPCheck(psTrack); + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + sdResult = _mmf_MtsuCheck2(psTrack, AV_MMF_SMAF_TYPE_MA1); + if (sdResult != AV_MMF_MA2_VOICE_FOUND) + { + return AV_MMF_ERR_CHUNK; + } + sdResult = _mmf_SeqDataCheck2(psTrack, AV_MMF_SMAF_TYPE_MA1); + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + psLoad->dPlayTime = psTrack->dPlayTime; + psLoad->dStartTime = psTrack->dStartTick; + psLoad->dTimeBase = psTrack->dTimeBase; + return AV_MMF_FUNC_SUCCESS; + } + else + { /* MA-2 */ + psLoad->sTrack_Info[0].pbMtr = NULL; + psLoad->sTrack_Info[0].dMtrSize = 0; + + for (i = 1; i < 5; i++) + { + psTrack = &(psLoad->sTrack_Info[i]); + if (psTrack->pbMtr == NULL) + continue; + if (psTrack->dMtrSize <= AV_MMF_MINIMUM_TRACKSIZE2) + { + return AV_MMF_ERR_CHUNK; + } + sdResult = _mmf_MTRCheck(psTrack, AV_MMF_SMAF_TYPE_MA2); + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + _mmf_MspICheck(psTrack, &(psLoad->sPhrase_Info[0])); + sdResult = _mmf_STSPCheck(psTrack); + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + sdResult = _mmf_SeqDataCheck2(psTrack, AV_MMF_SMAF_TYPE_MA2); + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + psLoad->dPlayTime = psTrack->dPlayTime; + psLoad->dStartTime = psTrack->dStartTick; + psLoad->dTimeBase = psTrack->dTimeBase; + } + if (psLoad->sTrack_Info[AV_MMF_ATR_TRACK_NO].pbMtr != NULL) + { + psTrack = &(psLoad->sTrack_Info[AV_MMF_ATR_TRACK_NO]); + + if (psTrack->dMtrSize <= AV_MMF_MINIMUM_TRACKSIZE2) + { + return AV_MMF_ERR_CHUNK; + } + sdResult = _mmf_ATRCheck(psTrack); + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + _mmf_MspICheck(psTrack, &(psLoad->sPhrase_Info[0])); + sdResult = _mmf_STSPCheck(psTrack); + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + sdResult = _mmf_SeqDataCheck2(psTrack, AV_MMF_SMAF_TYPE_MA2); + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + psLoad->dPlayTime = psTrack->dPlayTime; + psLoad->dStartTime = psTrack->dStartTick; + psLoad->dTimeBase = psTrack->dTimeBase; + } + +/* totaling of track information */ + for (i = 1; i < AV_MMF_MAX_TRACK_NUM; i++) + { + psTrack = &(psLoad->sTrack_Info[i]); + if (psTrack->pbMtr == NULL) + continue; + + if (psLoad->dPlayTime < psTrack->dPlayTime) + psLoad->dPlayTime = psTrack->dPlayTime; + if (psLoad->dTimeBase != psTrack->dTimeBase) + { + return AV_MMF_ERR_CHUNK; + } + if (psLoad->dStartTime != psTrack->dStartTick) + { + return AV_MMF_ERR_CHUNK; + } + if (_mmf_MtsuCheck2(psTrack, AV_MMF_SMAF_TYPE_MA2) == AV_MMF_FUNC_SUCCESS) + fbVoice = AV_MMF_MA2_VOICE_FOUND; + } + + fbVoice = AV_MMF_MA2_VOICE_FOUND; + for (i = 1; i < 5; i++) + { + psTrack = &(psLoad->sTrack_Info[i]); + if (psTrack->pbMtr == NULL) + continue; + if (_mmf_MtsuCheck2(psTrack, AV_MMF_SMAF_TYPE_MA2) == AV_MMF_MA2_VOICE_FOUND) + { + fbVoice = AV_MMF_MA2_VOICE_FOUND; + break; + } + else + fbVoice = AV_MMF_MA2_VOICE_NOTFOUND; + } + + if (fbVoice == AV_MMF_MA2_VOICE_NOTFOUND) + { + return AV_MMF_ERR_CHUNK; + } + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Play time: %ld\n", psLoad->dPlayTime); + #endif + + return AV_MMF_FUNC_SUCCESS; +} + +static unsigned char +_mmf_GetByte3L(PHUFFMANINFO psHuf) +{ + psHuf->dReadSize ++; + if (psHuf->dReadSize > psHuf->dMtsqSize) + { + return 0; + } + return *(psHuf->psBuffer++); +} + + +/********************************************************************************* + * _mmf_GetFlex3L + * + * Description: + * MA-3 sequence data check + * Argument: + * psLoad pointer to load information structure + * pdRead pointer to size of flex data + * Return: + * >=0 success + * < 0 error code + ********************************************************************************/ +static int +_mmf_GetFlex3L(PLOADINFO psLoad, unsigned int* pdRead) +{ + unsigned int dTemp, dRead; + unsigned char bTemp; + + dRead = 1; + bTemp = psLoad->pfnGetByte( &(psLoad->sHuffman_Info) ); + dTemp = (unsigned int)(bTemp & 0x7F); + while (bTemp & 0x80) + { + if (dRead >= 4) + return AV_MMF_FUNC_ERROR; + dRead ++; + bTemp = psLoad->pfnGetByte( &(psLoad->sHuffman_Info) ); + dTemp = (dTemp << 7) + (unsigned int)(bTemp & 0x7F); + } + if (dTemp >= AV_MMF_PLAY_TIME_MAX) + return AV_MMF_FUNC_ERROR; + *pdRead = dRead; + return (int)dTemp; +} + + +/********************************************************************************* + * _mmf_SeqDataCheck3 + * + * Description: + * MA-3 sequence data check + * Argument: + * psLoad pointer to load information structure + * bSmafType SMAF type + * Return: + * 0 success + * < 0 error code + ********************************************************************************/ +static int +_mmf_SeqDataCheck3(PLOADINFO psLoad, unsigned char bSmafType) +{ + PTRACKINFO psTrk; + PPHRASEINFO psPhr; + PHUFFMANINFO psHuf; + unsigned int dIndex, fdPhrase, dSize, dPast, dGate, dReadSize, i; + unsigned int dStartTick, dStopTick; + int sdTemp; + unsigned char bTemp; + + if (bSmafType == AV_MMF_SMAF_TYPE_MA3) + { /* MA-3 */ + psTrk = &(psLoad->sTrack_Info[5]); + dStartTick = AV_MMF_STSP_TIME_NULL; + dStopTick = AV_MMF_STSP_TIME_NULL; + } + else + { /* MA-5 */ + psTrk = &(psLoad->sTrack_Info[6]); + dStartTick = psTrk->dStartTick; + dStopTick = psTrk->dStopTick; + psTrk->dStartTick = AV_MMF_STSP_TIME_NULL; + psTrk->dStopTick = AV_MMF_STSP_TIME_NULL; + } + + psPhr = &(psLoad->sPhrase_Info[0]); + psHuf = &(psLoad->sHuffman_Info); + fdPhrase= 0; + dIndex = 0; + dPast = 0; + dGate = 0; + dSize = psHuf->dSeqSize; + + if (psHuf->dSeqSize == 0) + { + return AV_MMF_ERR_SLENGTH; + } + + for (i = 0; i < AV_MMF_MAX_PHRASE_INFO; i++) + { + if (psPhr[i].dStartPoint != AV_MMF_STSP_OFFSET_NULL) + fdPhrase = 1; + } + +/* scanning sequence data to EOS or stop point */ + while (dSize >= dIndex) + { + + if (psTrk->dStartPoint == dIndex) + psTrk->dStartTick = dPast; + if (psTrk->dStopPoint == dIndex) /* stop point */ + psTrk->dStopTick = dPast; + if (fdPhrase) + { + for (i = 0; i < AV_MMF_MAX_PHRASE_INFO; i++) + { + if (psPhr[i].dStartPoint== dIndex) + psPhr[i].dStartTick = dPast; + if (psPhr[i].dStopPoint == dIndex) + psPhr[i].dStopTick = dPast; + } + } + + if ((psTrk->dStopTick != AV_MMF_STSP_TIME_NULL) || (dSize == dIndex)) + break; + + sdTemp = _mmf_GetFlex3L(psLoad, &dReadSize); /* Duration */ + if (sdTemp < 0) { + return AV_MMF_ERR_CHUNK; + } + dPast += (unsigned int)sdTemp; + if (dPast >= AV_MMF_PLAY_TIME_MAX) + { + return AV_MMF_ERR_LLENGTH; + } + + if ((unsigned int)sdTemp >= dGate) + dGate = 0; + else + dGate -= (unsigned int)sdTemp; + dIndex += dReadSize; + + bTemp = psLoad->pfnGetByte(psHuf); + dIndex ++; + + switch (bTemp & 0xF0) + { + case 0x90 : + psLoad->pfnGetByte(psHuf); /*Note number*/ + dIndex ++; + psLoad->pfnGetByte(psHuf); /*Key Velocity*/ + dIndex ++; + sdTemp = _mmf_GetFlex3L(psLoad, &dReadSize); + if (sdTemp < 0) { + return AV_MMF_ERR_CHUNK; + } + dIndex += dReadSize; + if ((unsigned int)sdTemp > dGate) + dGate = sdTemp; + break; + case 0x80 : + psLoad->pfnGetByte(psHuf); /*Note number*/ + dIndex ++; + sdTemp = _mmf_GetFlex3L(psLoad, &dReadSize); + if (sdTemp < 0) { + return AV_MMF_ERR_CHUNK; + } + dIndex += dReadSize; + if ((unsigned int)sdTemp > dGate) + dGate = sdTemp; + break; + case 0xA0 : + case 0xB0 : + case 0xE0 : + bTemp = psLoad->pfnGetByte(psHuf); /*B0: Conrol number, E0:Pitch Bend Change LSB*/ + dIndex ++; + bTemp = psLoad->pfnGetByte(psHuf); /*B0: Conrol value, E0:Pitch Bend Change MSB*/ + dIndex ++; + break; + case 0xC0 : + case 0xD0 : + bTemp = psLoad->pfnGetByte(psHuf); + dIndex ++; + break; + default : + switch (bTemp) + { + case 0xF0 : + sdTemp = _mmf_GetFlex3L(psLoad, &dReadSize); + if (sdTemp < 0) { + return AV_MMF_ERR_CHUNK; + } + for (i = 0; i < (unsigned int)sdTemp; i++) + bTemp = psLoad->pfnGetByte(psHuf); + if (bTemp != 0xF7) { + return AV_MMF_ERR_CHUNK; + } + dIndex += (unsigned int)sdTemp + dReadSize; + break; + case 0xFF : + bTemp = psLoad->pfnGetByte(psHuf); + dIndex ++; + switch (bTemp) + { + case 0x00 : + break; + case 0x2F : + bTemp = psLoad->pfnGetByte(psHuf); + dIndex ++; + if (bTemp != 0x00) + { + return AV_MMF_ERR_CHUNK; + } + dGate = 0; + psTrk->dStopTick = dPast; + dIndex = dSize; + break; + default : + return AV_MMF_ERR_CHUNK; + } + break; + default : + return AV_MMF_ERR_CHUNK; + } + break; + } + if ((dSize < dIndex) || (psHuf->dReadSize > psHuf->dMtsqSize)) + { + return AV_MMF_ERR_CHUNK; + } + } + + if (bSmafType == AV_MMF_SMAF_TYPE_MA3) + { /* MA-3 */ +/* check start point */ + if (psTrk->dStartTick == AV_MMF_STSP_TIME_NULL) + { + if (psTrk->dStartPoint != AV_MMF_STSP_OFFSET_NULL) + { + return AV_MMF_ERR_CHUNK; + } + psTrk->dStartPoint = 0; + psTrk->dStartTick = 0; + } +/* check stop point */ + if (psTrk->dStopTick == AV_MMF_STSP_TIME_NULL) + { + if (psTrk->dStopPoint != AV_MMF_STSP_OFFSET_NULL) + { + return AV_MMF_ERR_CHUNK; + } + psTrk->dStopPoint = dSize; + psTrk->dStopTick = dPast + dGate; + } +/* adjust phrase information */ + for (i = 0; i < AV_MMF_MAX_PHRASE_INFO; i++) + { + if (psPhr[i].dStartPoint <= psTrk->dStartPoint) + psPhr[i].dStartTick = psTrk->dStartTick; + if (psPhr[i].dStopPoint >= psTrk->dStopPoint) + psPhr[i].dStopTick = psTrk->dStopTick; + if (psPhr[i].dStopPoint <= psTrk->dStartPoint) + psPhr[i].dStopTick = AV_MMF_STSP_TIME_NULL; + } + } + else + { /* MA-5 */ +/* check stop point */ + if (dStopTick > dPast) + { + return AV_MMF_ERR_CHUNK; + } + psTrk->dStartTick = dStartTick; + psTrk->dStopTick = dStopTick; + } + +/* calculate playback time of this track */ + psTrk->dPlayTime = psTrk->dStopTick - psTrk->dStartTick; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Play time: %ld\n", psTrk->dPlayTime); + #endif + + return AV_MMF_FUNC_SUCCESS; +} + + + + +/********************************************************************************* + * _mmf_DecodeGetbitL + * + * Description: + * get 1 bit with error check + * Argument: + * psHuf pointer to huffman information structure + * Return: + * read data + ********************************************************************************/ + + +static unsigned char +_mmf_DecodeGetbitL(PHUFFMANINFO psHuf) +{ + char czero = 0; + + if (--psHuf->sbBitC < czero) + { + if (psHuf->dReadSize >= psHuf->dMtsqSize) + return 0; + psHuf->sbBitC = 7; + psHuf->bByte = *(psHuf->psBuffer++); + psHuf->dReadSize ++; + } + return (unsigned char)(psHuf->bByte & g_abBitMaskTable1[(int)(psHuf->sbBitC)]); +} + + +/********************************************************************************* + * _mmf_DecodeGetbits + * + * Description: + * get 8 bit + * Argument: + * psHuf pointer to huffman information structure + * Return: + * read data + ********************************************************************************/ +static unsigned char +_mmf_DecodeGetbits(PHUFFMANINFO psHuf) +{ + unsigned short wTemp; + unsigned char bData1, bData2; + + if (psHuf->dReadSize >= psHuf->dMtsqSize) + return 0; + bData1 = psHuf->bByte; + bData2 = *(psHuf->psBuffer++); + psHuf->bByte = bData2; + psHuf->dReadSize ++; + wTemp = (unsigned short)((((unsigned short)bData1) << 8) + ((unsigned short)bData2)); + return (unsigned char)((wTemp & g_awBitMaskTable2[(int)(psHuf->sbBitC)]) >> psHuf->sbBitC); +} + + +/********************************************************************************* + * _mmf_DecodeTree + * + * Description: + * make MA-3 huffman decode tree + * Argument: + * psHuf pointer to huffman information structure + * Return: + * !0 success(sequence data size) + * 0 error + ********************************************************************************/ +static int +_mmf_DecodeTree( PHUFFMANINFO psHuf) +{ + unsigned int dNode, dEmpty, dIndex, i; + short *pswLeft, *pswRight, *pswPNode; + unsigned char bTemp; + + if (_mmf_DecodeGetbitL(psHuf)) + { + if (psHuf->dReadSize >= psHuf->dMtsqSize) + return AV_MMF_HUFFMAN_TREE_FAILURE; + + pswLeft = &(psHuf->swLeft[256]); + pswRight= &(psHuf->swRight[256]); + pswPNode= &(psHuf->swRight[0]); + for (i = 0; i < 256; i++) + { + pswLeft[i] = -1; + pswRight[i] = -1; + pswPNode[i] = 0; + } + dNode = 2; + dEmpty = 1; + dIndex = 0; + } + else + return AV_MMF_HUFFMAN_TREE_FAILURE; + + while (dNode != 0) + { + if ((dEmpty >= 256) || (dNode >= 257)) + return AV_MMF_HUFFMAN_TREE_FAILURE; + + bTemp = _mmf_DecodeGetbitL(psHuf); + if (psHuf->dReadSize >= psHuf->dMtsqSize) + return AV_MMF_HUFFMAN_TREE_FAILURE; + + if (bTemp) + { + dNode ++; + if (pswLeft[dIndex] == -1) + pswLeft[dIndex] = (short)(dEmpty + 256); + else + pswRight[dIndex]= (short)(dEmpty + 256); + pswPNode[dEmpty] = (short)dIndex; + dIndex = dEmpty; + dEmpty ++; + } + else + { + dNode --; + bTemp = _mmf_DecodeGetbits(psHuf); + if (psHuf->dReadSize >= psHuf->dMtsqSize) + return AV_MMF_HUFFMAN_TREE_FAILURE; + + if (pswLeft[dIndex] == -1) + pswLeft[dIndex] = (short)bTemp; + else + { + pswRight[dIndex]= (short)bTemp; + while ((pswRight[dIndex] != -1) && (dIndex != 0)) + dIndex = (unsigned int)pswPNode[dIndex]; + } + } + } + + for (i = 0; i < 256; i++) + { + if (pswLeft[i] == -1) + pswLeft[i] = 0; + if (pswRight[i] == -1) + pswRight[i] = 0; + } + return AV_MMF_HUFFMAN_TREE_SUCCESS; +} + + +/********************************************************************************* + * _mmf_DecodeInit + * + * Description: + * make MA-3 huffman decode tree + * Argument: + * psHuf pointer to huffman information structure + * Return: + * !0 success(sequence data size) + * 0 error + ********************************************************************************/ +static unsigned int +_mmf_DecodeInit(PHUFFMANINFO psHuf) +{ + unsigned int dSeqSize; + + if (psHuf->dMtsqSize <= 5) + { + return AV_MMF_HUFFMAN_TREE_FAILURE; + } + + dSeqSize = _mmf_Get4Byte(psHuf->psBuffer); + psHuf->psBuffer += 4; + psHuf->dReadSize = 4; + psHuf->sbBitC = 0; + psHuf->bByte = 0; + if (_mmf_DecodeTree(psHuf) == AV_MMF_HUFFMAN_TREE_FAILURE) + return AV_MMF_HUFFMAN_TREE_FAILURE; + else + return dSeqSize; +} + + +/********************************************************************************* + * _mmf_DecodeByte3L + * + * Description: + * get 1 byte (from compressed data) + * Argument: + * psHuf pointer to huffman information structure + * Return: + * unsigned char success(read data) + * 0 error code + ********************************************************************************/ +static unsigned char +_mmf_DecodeByte3L (PHUFFMANINFO psHuf) +{ + unsigned int bData, bIndex; + char czero = 0; + bIndex = 256; + while (bIndex >= 256) + { + if (--psHuf->sbBitC < czero) + { + psHuf->dReadSize ++; + if (psHuf->dReadSize > psHuf->dMtsqSize) + return 0; + psHuf->sbBitC = 7; + psHuf->bByte = *(psHuf->psBuffer++); + } + bData = (unsigned char)(psHuf->bByte & g_abBitMaskTable1[(int)(psHuf->sbBitC)]); + if (bData) + bIndex = psHuf->swRight[bIndex]; + else + bIndex = psHuf->swLeft[bIndex]; + } + return (unsigned char)bIndex; +} + + +/********************************************************************************* + * _mmf_TrackChunkCheck3 + * + * Description: + * MA-3 track chunk check + * Argument: + * psLoad pointer to load information structure + * Return: + * 0 success + * < 0 error code + ********************************************************************************/ +static int +_mmf_TrackChunkCheck3(PLOADINFO psLoad) +{ + PTRACKINFO psTrack; + int sdResult; + + if (psLoad->sTrack_Info[5].pbMtr == NULL) + { + return AV_MMF_ERR_SLENGTH; + } + psTrack = &(psLoad->sTrack_Info[5]); + if (psTrack->dMtrSize <= AV_MMF_MINIMUM_TRACKSIZE3) + { + return AV_MMF_ERR_CHUNK; + } + sdResult = _mmf_MTRCheck(psTrack, AV_MMF_SMAF_TYPE_MA3); + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + _mmf_MspICheck(psTrack, &(psLoad->sPhrase_Info[0])); + + psLoad->sHuffman_Info.psBuffer = psTrack->pbMtsq; + psLoad->sHuffman_Info.dMtsqSize = psTrack->dMtsqSize; + +/* Initialize Huffman information structure */ + if (psTrack->pbMtr[0] == 0x01) + { /* Compressed Foramt */ + psLoad->sHuffman_Info.dSeqSize = _mmf_DecodeInit( &(psLoad->sHuffman_Info) ); + if (psLoad->sHuffman_Info.dSeqSize == AV_MMF_HUFFMAN_TREE_FAILURE) + { + return AV_MMF_ERR_CHUNK; + } + psLoad->pfnGetByte = _mmf_DecodeByte3L; + psLoad->sHuffman_Info.psFBuf = psLoad->sHuffman_Info.psBuffer; + psLoad->sHuffman_Info.sbFBit = psLoad->sHuffman_Info.sbBitC; + psLoad->sHuffman_Info.bFByte = psLoad->sHuffman_Info.bByte; + } + else + { /* No Compressed Foramt */ + psLoad->pfnGetByte = _mmf_GetByte3L; + psLoad->sHuffman_Info.dSeqSize = psTrack->dMtsqSize; + psLoad->sHuffman_Info.dReadSize = 0; + psLoad->sHuffman_Info.psFBuf = psTrack->pbMtsq; + psLoad->sHuffman_Info.sbFBit = 0; + psLoad->sHuffman_Info.bFByte = 0; + } + + psTrack->dMtsqSize = psLoad->sHuffman_Info.dSeqSize; + sdResult = _mmf_STSPCheck(psTrack); + psTrack->dMtsqSize = psLoad->sHuffman_Info.dMtsqSize; + + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + sdResult = _mmf_SeqDataCheck3(psLoad, AV_MMF_SMAF_TYPE_MA3); + if (sdResult != AV_MMF_FUNC_SUCCESS) + { + return sdResult; + } + psLoad->dPlayTime = psTrack->dPlayTime; + psLoad->dStartTime = psTrack->dStartTick; + psLoad->dTimeBase = psTrack->dTimeBase; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Play time: %ld\n", psLoad->dPlayTime); + #endif + + return AV_MMF_FUNC_SUCCESS; +} + + +/********************************************************************************* + * _mmf_CheckM5P + * + * Description: + * MA-5 profile data chunk check + * Argument: + * psLoad pointer to load information structure + * Return: + * 0 success + * < 0 error code + ********************************************************************************/ +static int +_mmf_CheckM5P(PLOADINFO psLoad) +{ + PTRACKINFO psTrk; + PPHRASEINFO psPhr; + POPTIONINFO psOptn; + unsigned char *pbOpda, *pbM5p; + unsigned int dOSize, dMSize, dIndex, dPhraseFlag, i; + unsigned int dChunkID, dChunkNo; + int sdChunkSize; + + psOptn = &(psLoad->sOption_Info); + psTrk = &(psLoad->sTrack_Info[6]); + psPhr = &(psLoad->sPhrase_Info[0]); + pbOpda = psOptn->pbOpda; + dOSize = psOptn->dOpdaSize; + dIndex = 0; + pbM5p = NULL; + dMSize = 0; + +/* search Pro5 Chunk */ + while (dOSize > (dIndex + AV_MMF_CHUNK_HEADER_SIZE)) + { + sdChunkSize = _mmf_MalibNextChunk(&pbOpda[dIndex], (dOSize - dIndex), + AVMALIB_CHUNK_PHASE_OPDASUB, &dChunkID, &dChunkNo); + if (sdChunkSize < AVMASMW_SUCCESS) return AVMASMW_ERROR; + dIndex += AV_MMF_CHUNK_HEADER_SIZE; + if ((dChunkID == AVMALIB_CHUNKCODE_M5P) && (dChunkNo == 0x05)) + { + pbM5p = &pbOpda[dIndex]; + dMSize = (unsigned int)sdChunkSize; + break; + } + dIndex += sdChunkSize; + } + + if ((pbM5p == NULL) || (dMSize < 12)) return AVMASMW_ERROR; + + dPhraseFlag = _mmf_Get4Byte(&pbM5p[0]); + psTrk->dStartTick = _mmf_Get4Byte(&pbM5p[4]); /* start point */ + psTrk->dStopTick = _mmf_Get4Byte(&pbM5p[8]); /* stop point */ + dIndex = 12; + + if (psTrk->dStartTick >= psTrk->dStopTick) return AVMASMW_ERROR; + + for (i = 0; i < AV_MMF_MAX_PHRASE_INFO; i ++) + { + if (dMSize < (dIndex + 8)) break; + if (dPhraseFlag & (0x80000000 >> i)) + { + psPhr[i].dStartTick = _mmf_Get4Byte(&pbM5p[dIndex]); + psPhr[i].dStopTick = _mmf_Get4Byte(&pbM5p[dIndex + 4]); + if (psPhr[i].dStartTick >= psPhr[i].dStopTick) + { + psPhr[i].dStartTick = AV_MMF_STSP_TIME_NULL; + psPhr[i].dStopTick = AV_MMF_STSP_TIME_NULL; + } + if (psPhr[i].dStartTick < psTrk->dStartTick) + psPhr[i].dStartTick = psTrk->dStartTick; + if (psPhr[i].dStopTick > psTrk->dStopTick) + psPhr[i].dStopTick = psTrk->dStopTick; + dIndex += 8; + } + } + return AVMASMW_SUCCESS; +} + + +/********************************************************************************* + * _mmf_TrackChunkCheck5 + * + * Description: + * MA-5 track chunk check + * Argument: + * psLoad pointer to load information structure + * dMode load mode + * Return: + * 0 success + * < 0 error code + ********************************************************************************/ +static int +_mmf_TrackChunkCheck5(PLOADINFO psLoad) +{ + PTRACKINFO psTrack; + int sdResult; + + if (psLoad->sTrack_Info[6].pbMtr == NULL) + { + return AV_MMF_ERR_SLENGTH; + } + psTrack = &(psLoad->sTrack_Info[6]); + if (psTrack->dMtrSize <= AV_MMF_MINIMUM_TRACKSIZE3) + { + return AV_MMF_ERR_CHUNK; + } + sdResult = _mmf_MTRCheck(psTrack, AV_MMF_SMAF_TYPE_MA5); + if (sdResult != AV_MMF_FUNC_SUCCESS) return sdResult; + + psLoad->sHuffman_Info.psBuffer = psTrack->pbMtsq; + psLoad->sHuffman_Info.dMtsqSize = psTrack->dMtsqSize; + psLoad->sHuffman_Info.dSeqSize = psTrack->dMtsqSize; + psLoad->sHuffman_Info.dReadSize = 0; + psLoad->pfnGetByte = _mmf_GetByte3L; + + + sdResult = _mmf_SeqDataCheck3(psLoad, AV_MMF_SMAF_TYPE_MA5); + if (sdResult != AV_MMF_FUNC_SUCCESS) return sdResult; + + psLoad->dPlayTime = psTrack->dPlayTime; + psLoad->dStartTime = psTrack->dStartTick; + psLoad->dTimeBase = psTrack->dTimeBase; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Play time: %ld\n", psLoad->dPlayTime); + #endif + + return AV_MMF_FUNC_SUCCESS; +} +/********************************************************************************* + * _mmf_GetHvData + * + * Description: + * HV Data Chunk Check + * Argument: + * psLoad pointer to load information structure + * bCType Contents Type + * Return: + * nothing + ********************************************************************************/ +static void +_mmf_GetHvData(PLOADINFO psLoad, unsigned char bCType) +{ + unsigned char* pbHvData; + unsigned int dHvDataSize; + + unsigned char* pbVoice; + unsigned char* pbScript; + unsigned char* pbSetup; + unsigned int dVoiceSize, dScriptSize, dSetupSize, dIndex; + unsigned char bHvCh; + unsigned short wTag, wSize; + + unsigned int dChunkID, dChunkNo; + int sdChunkSize; + + if ((psLoad->dSmafType == AV_MMF_SMAF_TYPE_MA1) ||(psLoad->dSmafType == AV_MMF_SMAF_TYPE_MA2)) + return ; + + if ((bCType & 0x0F) == 0x08) + return ; + + if (psLoad->dSmafType == AV_MMF_SMAF_TYPE_MA3) + { + pbHvData = psLoad->sTrack_Info[5].pbMthv; + dHvDataSize = psLoad->sTrack_Info[5].dMthvSize; + } + else + { + pbHvData = psLoad->sTrack_Info[6].pbMthv; + dHvDataSize = psLoad->sTrack_Info[6].dMthvSize; + } + + if ((pbHvData == NULL) || (dHvDataSize < AV_MMF_CHUNK_HEADER_SIZE)) + return ; + + pbVoice = NULL; + pbScript = NULL; + pbSetup = NULL; + dVoiceSize = 0; + dScriptSize = 0; + dSetupSize = 0; + bHvCh = AV_MMF_HV_CHANNEL_NULL; + dIndex = 0; + + while (dHvDataSize > (dIndex + AV_MMF_CHUNK_HEADER_SIZE)) + { + sdChunkSize = _mmf_MalibNextChunk(&(pbHvData[dIndex]), (dHvDataSize - dIndex), + AVMALIB_CHUNK_PHASE_MTHVSUB, &dChunkID, &dChunkNo); + if (sdChunkSize < 0) return ; + dIndex += AV_MMF_CHUNK_HEADER_SIZE; + switch (dChunkID) + { + case AVMALIB_CHUNKCODE_MHVS : + pbSetup = &(pbHvData[dIndex]); + dSetupSize = (unsigned int)sdChunkSize; + break; + case AVMALIB_CHUNKCODE_HVP : + if (dChunkNo != 0) break; + pbVoice = &(pbHvData[dIndex - AV_MMF_CHUNK_HEADER_SIZE]); + dVoiceSize = (unsigned int)(sdChunkSize + AV_MMF_CHUNK_HEADER_SIZE); + break; + case AVMALIB_CHUNKCODE_MHSC : + pbScript = &(pbHvData[dIndex]); + dScriptSize = (unsigned int)sdChunkSize; + break; + } + dIndex += sdChunkSize; + } + + dIndex = 0; + while (dSetupSize >= dIndex + 4 ) + { + wTag = (unsigned short)(((unsigned short)(pbSetup[dIndex ]) << 8) + pbSetup[dIndex + 1]); + wSize = (unsigned short)(((unsigned short)(pbSetup[dIndex + 2]) << 8) + pbSetup[dIndex + 3]); + dIndex += 4; + if (dSetupSize < (dIndex + wSize)) return ; + if ((wTag == 0x4348) && (wSize == 1)) bHvCh = pbSetup[dIndex]; + dIndex += wSize; + } + + if ((pbScript == NULL) || (bHvCh >= AV_MMF_HV_CHANNEL_NULL)) + return ; + + psLoad->sHV_Info.pbVoice = pbVoice; + psLoad->sHV_Info.dVoiceSize = dVoiceSize; + psLoad->sHV_Info.pbScript = pbScript; + psLoad->sHV_Info.dScriptSize = dScriptSize; + psLoad->sHV_Info.bHvChannel = bHvCh; + + return ; +} + +/********************************************************************************* + * Avdecode_byte3 + * + * Description: + * get 1 byte (from compressed data) + * Argument: + * nothing + * Return: + * unsigned char success(read data) + ********************************************************************************/ +static unsigned short +_mmf_MalibMakeCRC(unsigned int dSize, unsigned char* pbData) +{ + unsigned short wRes; + unsigned char bData; + + wRes = 0xFFFFU; + while ( --dSize >= 2 ) + { + bData = *pbData++; + wRes = (unsigned short)((wRes << 8) ^ g_crc_tbl[(unsigned char)(wRes >> 8) ^ bData]); + } + return (unsigned short)(~wRes & 0xFFFFU); +} + + +/********************************************************************************* + * _mmf_MALoad + * + * Description: + * SMAF data load (error check and regist) + * Argument: + * pbFile pointer to SMAF data + * dFsize size of SMAF data + * dMode load mode + * Return: + * 0 or 1 success(file id) + * < 0 error code + ********************************************************************************/ +static int +_mmf_MALoad( unsigned char* pbFile, unsigned int dFSize) +{ + PLOADINFO psLoad_Info; + unsigned int bNo = 0; + unsigned int dChunkID = 0, dChunkNo = 0; + unsigned char* pbBuf = NULL; + unsigned int dSize = 0, dIndex = 0; + int sdChunkSize = 0, sdResult = 0; + unsigned int dCalcCrc = 0, dFileCrc = 0; + int rVal = 0; + + pbBuf = pbFile; + dSize = dFSize; + psLoad_Info = &(g_sSmaf_Info.sLoad_Info[bNo]); + _mmf_CheckInitial( psLoad_Info ); + +/* check File Chunk(ID/Size) */ + sdChunkSize = _mmf_MalibNextChunk(pbBuf, dSize, AVMALIB_CHUNK_PHASE_MMMD, + &dChunkID, &dChunkNo); + if ((sdChunkSize < 0) || (dChunkID != AVMALIB_CHUNKCODE_MMMD)) + { + return AV_MMF_ERR_FILE; + } + dSize = (unsigned int)(sdChunkSize + AV_MMF_CHUNK_HEADER_SIZE); + dCalcCrc = AV_MMF_CRC_NULL; + + dCalcCrc = _mmf_MalibMakeCRC(dSize, pbBuf); + dFileCrc = (unsigned int)((((unsigned int)pbBuf[dSize - 2]) << 8) + pbBuf[dSize - 1]); + if (dCalcCrc != dFileCrc) { + return AV_MMF_ERR_FILE; + } + + +/* check Contents Info Chunk */ + dIndex = AV_MMF_CHUNK_HEADER_SIZE; + sdChunkSize = _mmf_MalibNextChunk(&pbBuf[dIndex], (dSize-dIndex), + AVMALIB_CHUNK_PHASE_CNTI, &dChunkID, &dChunkNo); + if ((sdChunkSize < 5) || (dChunkID != AVMALIB_CHUNKCODE_CNTI)) + { + return AV_MMF_ERR_FILE; + } + +/* check Contents Class */ + if ((pbBuf[AV_MMF_POSITION_OF_CCLASS] != AV_MMF_CONTENTS_CLASS_0) && + (pbBuf[AV_MMF_POSITION_OF_CCLASS] != AV_MMF_CONTENTS_CLASS_1) && + (pbBuf[AV_MMF_POSITION_OF_CCLASS] != AV_MMF_CONTENTS_CLASS_2) ) + { + return AV_MMF_ERR_CLASS; + } + +/* check Contents Type */ + dIndex += AV_MMF_CHUNK_HEADER_SIZE; + if (((pbBuf[AV_MMF_POSITION_OF_CTYPE] & 0xF0) == AV_MMF_CONTENTS_TYPE_0) || + ((pbBuf[AV_MMF_POSITION_OF_CTYPE] & 0xF0) == AV_MMF_CONTENTS_TYPE_1) || + ((pbBuf[AV_MMF_POSITION_OF_CTYPE] & 0xF0) == AV_MMF_CONTENTS_TYPE_2) ) + { + psLoad_Info->dSmafType = AV_MMF_SMAF_TYPE_MA2; + } + else if (((pbBuf[AV_MMF_POSITION_OF_CTYPE] & 0xF0) == AV_MMF_CONTENTS_TYPE_3) || + ((pbBuf[AV_MMF_POSITION_OF_CTYPE] & 0xF0) == AV_MMF_CONTENTS_TYPE_4) || + ((pbBuf[AV_MMF_POSITION_OF_CTYPE] & 0xF0) == AV_MMF_CONTENTS_TYPE_5) ) + { + switch (pbBuf[AV_MMF_POSITION_OF_CTYPE] & 0x0F) + { + case 0x00 : + case 0x01 : + psLoad_Info->dSmafType = AV_MMF_SMAF_TYPE_MA2; + break; + case 0x02 : + case 0x03 : + psLoad_Info->dSmafType = AV_MMF_SMAF_TYPE_MA3; + break; + case 0x04 : + case 0x05 : + case 0x06 : + case 0x07 : + case 0x08 : + psLoad_Info->dSmafType = AV_MMF_SMAF_TYPE_MA5; + break; + default : + return AV_MMF_ERR_TYPE; + } + } + else + { + return AV_MMF_ERR_TYPE; + } + +/* get pointer & size of option information */ + psLoad_Info->sOption_Info.pbCnti = &pbBuf[dIndex]; + psLoad_Info->sOption_Info.dCntiSize = (unsigned int)(sdChunkSize); + dIndex += sdChunkSize; + + if (pbBuf[AV_MMF_POSITION_OF_CTYPE] >= 0x30) + { + sdChunkSize = _mmf_MalibNextChunk(&pbBuf[dIndex], (dSize-dIndex), + AVMALIB_CHUNK_PHASE_MMMDSUB, &dChunkID, &dChunkNo); + if ((sdChunkSize >= 12) && (dChunkID == AVMALIB_CHUNKCODE_OPDA)) + { + dIndex += AV_MMF_CHUNK_HEADER_SIZE; + psLoad_Info->sOption_Info.pbOpda = &pbBuf[dIndex]; + psLoad_Info->sOption_Info.dOpdaSize = (unsigned int)sdChunkSize; + dIndex += sdChunkSize; + } + } + +/* get Track Chunk information */ + while (dSize > (dIndex + AV_MMF_CHUNK_HEADER_SIZE + AV_MMF_FILE_CRC_SIZE)) + { + sdChunkSize = _mmf_MalibNextChunk(&pbBuf[dIndex], (dSize-dIndex), + AVMALIB_CHUNK_PHASE_MMMDSUB, &dChunkID, &dChunkNo); + if (sdChunkSize < 0) + { + if (sdChunkSize == AVMALIB_CHUNK_ID_ERROR) + { + return AV_MMF_ERR_FILE; + } + else { + return AV_MMF_ERR_SIZE; + } + } + dIndex += AV_MMF_CHUNK_HEADER_SIZE; + switch (dChunkID) + { + case AVMALIB_CHUNKCODE_MTR : + if (dChunkNo > 6) + break; + psLoad_Info->sTrack_Info[dChunkNo].pbMtr = &(pbBuf[dIndex]); + psLoad_Info->sTrack_Info[dChunkNo].dMtrSize = (unsigned int)sdChunkSize; + break; + case AVMALIB_CHUNKCODE_ATR : + if (dChunkNo != 0) + break; + psLoad_Info->sTrack_Info[AV_MMF_ATR_TRACK_NO].pbMtr = &(pbBuf[dIndex]); + psLoad_Info->sTrack_Info[AV_MMF_ATR_TRACK_NO].dMtrSize = (unsigned int)sdChunkSize; + break; + default : + break; + } + dIndex += sdChunkSize; + } + + + +/* Error Check of Track Chunk */ + switch (psLoad_Info->dSmafType) + { + case AV_MMF_SMAF_TYPE_MA2 : + sdResult = _mmf_TrackChunkCheck2(psLoad_Info); + break; + case AV_MMF_SMAF_TYPE_MA3 : + sdResult = _mmf_TrackChunkCheck3(psLoad_Info); + break; + default : + if (_mmf_CheckM5P(psLoad_Info)!= AV_MMF_FUNC_SUCCESS) return AV_MMF_ERR_CHUNK; + sdResult = _mmf_TrackChunkCheck5(psLoad_Info); + break; + } + +/* check playback time */ + if (sdResult != AV_MMF_FUNC_SUCCESS) return sdResult; + + debug_msg ( "SUM %ld\n", psLoad_Info->dPlayTime * psLoad_Info->dTimeBase); + + if ((psLoad_Info->dPlayTime * psLoad_Info->dTimeBase) <= AV_MMF_PLAY_TIME_MIN) + { + return AV_MMF_ERR_SLENGTH; + } + if ((psLoad_Info->dPlayTime * psLoad_Info->dTimeBase) >= AV_MMF_PLAY_TIME_MAX) + { + return AV_MMF_ERR_LLENGTH; + } + rVal = psLoad_Info->dPlayTime*psLoad_Info->dTimeBase; + _mmf_GetHvData(psLoad_Info, pbBuf[AV_MMF_POSITION_OF_CTYPE]); + + psLoad_Info->pbMmmd = pbBuf; + psLoad_Info->dMmmdSize = dSize; + psLoad_Info->dCrc = (unsigned int)dCalcCrc; + return rVal; +} + + +/********************************************************************************* + * _mmf_RenewalProfile + * + * Description: + * renew profile data + * Argument: + * pbFile pointer to SMAF data + * Return: + * 0 or 1 success(file id) + * < 0 error code + ********************************************************************************/ +static int +_mmf_RenewalProfile(unsigned char* pbFile) +{ + PLOADINFO psLoad; + POPTIONINFO psOptn; + PTRACKINFO psTrk; + PHUFFMANINFO psHuf; + + psLoad = &(g_sSmaf_Info.sLoad_Info[1]); + psOptn = &(psLoad->sOption_Info); + psTrk = &(psLoad->sTrack_Info[5]); + psHuf = &(psLoad->sHuffman_Info); + +/* renew pointer offset to pointer */ + psLoad->pbMmmd = pbFile; + psLoad->dCrc = AV_MMF_CRC_NULL; + + psOptn->pbCnti = &(pbFile[(unsigned int)psOptn->pbCnti]); + psOptn->pbOpda = &(pbFile[(unsigned int)psOptn->pbOpda]); + + psTrk->pbMtr = &(pbFile[(unsigned int)psTrk->pbMtr]); + psTrk->pbMspi = &(pbFile[(unsigned int)psTrk->pbMspi]); + psTrk->pbMtsu = &(pbFile[(unsigned int)psTrk->pbMtsu]); + psTrk->pbMtsq = &(pbFile[(unsigned int)psTrk->pbMtsq]); + psTrk->pbMtsp = &(pbFile[(unsigned int)psTrk->pbMtsp]); + +/* Initialize Huffman information structure */ + psHuf->psBuffer = psTrk->pbMtsq; + psHuf->dMtsqSize = psTrk->dMtsqSize; + + if (psTrk->pbMtr[0] == 0x01) + { + psHuf->dSeqSize = _mmf_DecodeInit(psHuf); + if (psHuf->dSeqSize == AV_MMF_HUFFMAN_TREE_FAILURE) + { + return AV_MMF_FUNC_ERROR; + } + } + psHuf->psFBuf = psHuf->psBuffer; + psHuf->sbFBit = psHuf->sbBitC; + psHuf->bFByte = psHuf->bByte; + + return AV_MMF_FUNC_SUCCESS; +} + + + + + + +static int +_mmf_ParseSkipXmf2Mmf(unsigned char* pbFile, unsigned int dFSize) +{ + unsigned int skipVal = 0, sizeOfpbFile= dFSize; + char cmpXmfCMMD[5]; + if (pbFile) + memcpy(cmpXmfCMMD, pbFile, 4); + else + { + debug_msg ( "NULL pointer!\n"); + return -1; + } + + cmpXmfCMMD[4] = 0; + + if (strncmp(cmpXmfCMMD, "CMMD", 4) == 0) + { + while (1) + { + if (pbFile[skipVal] == 'M' && pbFile[skipVal+1] == 'M' && pbFile[skipVal+2] == 'M' && pbFile[skipVal+3] == 'D') + { + debug_msg ( "MMMD Header found!\n"); + break; + } + else + { + skipVal++; + if (skipVal >= sizeOfpbFile) + { + debug_msg ( "MMMD Header is not found!\n"); + return -1; + } + } + + } + } + else + debug_msg ( "File header is not started CMMD\n"); + + debug_msg ( "skip value: %d\n", skipVal); + + return skipVal; +} + +static int +mmf_file_mmf_get_duration (char *src, int is_xmf) +{ + int readed = 0; + int xmf_skip_offset = 0; + MMFileIOHandle *fp = NULL; + unsigned char *buf = 0; + long long src_size = 0L; + + PLOADINFO load_info; + unsigned char *p_crc = NULL; + unsigned int dCrc = 0; + int ret = 0; + + /*total time (millisecond)*/ + int ret_msec = 0; + + debug_msg ( "\n"); + + /*open*/ + ret = mmfile_open (&fp, src, MMFILE_RDONLY); + if (ret == MMFILE_UTIL_FAIL) + { + debug_error ( "open failed.\n"); + return -1; + } + + /*get file size*/ + mmfile_seek (fp, 0L, MMFILE_SEEK_END); + src_size = mmfile_tell (fp); + mmfile_seek (fp, 0L, MMFILE_SEEK_SET); + + if (src_size <= 0) { + debug_msg ( "failed to get file size.\n"); + ret_msec = -1; + goto _RELEASE_RESOURCE; + } + + /*alloc work buffer*/ + buf = mmfile_malloc (src_size + 1); + + /*read data*/ + if ((readed = mmfile_read (fp, buf, src_size) ) <= 0 ) { + debug_error ( "read error. size = %d\n", readed); + + ret_msec = -1; + goto _RELEASE_RESOURCE; + } + + /*if XMF, get skip offset.*/ + if (is_xmf) { + xmf_skip_offset = _mmf_ParseSkipXmf2Mmf (buf, src_size); + if (xmf_skip_offset == -1) { + ret_msec = -1; + goto _RELEASE_RESOURCE; + } + } + + if (g_sSmaf_Info.dStatus == AV_MMF_STATUS_SAT_PROFILE) { + load_info = &(g_sSmaf_Info.sLoad_Info[1]); + if (load_info->dMmmdSize <= src_size) { + p_crc = &(buf[load_info->dMmmdSize -2+xmf_skip_offset]); + dCrc = (unsigned int)((((unsigned int)p_crc[0]) << 8) + (unsigned int)p_crc[1]); + } else { + dCrc = AV_MMF_CRC_NULL; + } + + if (dCrc == load_info->dCrc) { + if (_mmf_RenewalProfile (buf + xmf_skip_offset) == AV_MMF_FUNC_SUCCESS) { + g_sSmaf_Info.dStatus = AV_MMF_STATUS_LOADED; + ret_msec = -1; + goto _RELEASE_RESOURCE; + } + } + } + + ret_msec = _mmf_MALoad (buf + xmf_skip_offset, src_size); + +_RELEASE_RESOURCE: + + mmfile_close (fp); + + if (buf) mmfile_free (buf); + + return ret_msec; +} + diff --git a/formats/ffmpeg/mm_file_format_mp3.c b/formats/ffmpeg/mm_file_format_mp3.c new file mode 100755 index 0000000..5d01e99 --- /dev/null +++ b/formats/ffmpeg/mm_file_format_mp3.c @@ -0,0 +1,1459 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string.h> /*memcmp*/ + +#include <sys/types.h> +#include <sys/stat.h> /*stat*/ +#include <unistd.h> + +#include <stdlib.h> /*malloc*/ + +#include "mm_debug.h" +#include "mm_file_utils.h" +#include "mm_file_format_private.h" +#include "mm_file_format_audio.h" +#include "mm_file_format_mp3.h" + + +#define __MMFILE_NEW_FRAME_FUNC + +#ifdef __MMFILE_NEW_TAGLIBS__ +#include "mm_file_format_tags.h" +#endif + + +#define AV_MP3_FIND_SYNC_LEN 1024*30 +#undef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + + +static const unsigned char mp3FrameMasking[4] = {0xFF,0xFE,0x0C,0x00}; +static unsigned char mp3FrameDataValid[4]; + +static const int mp3BitRateTable[2][3][16] = { + { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,}, + {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,}, + {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} }, + + { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,}, + {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,}, + {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} } +}; + +static const int mp3SamRateTable[3][3] = +{ {44100, 48000, 32000}, + {22050, 24000, 16000} , + {11025, 12000, 8000} +}; + +#define IS_VALID_FRAME_MP3(x) \ + ((((x)[0] & mp3FrameMasking[0]) == mp3FrameDataValid[0]) && \ + (((x)[1] & mp3FrameMasking[1]) == mp3FrameDataValid[1]) && \ + (((x)[2] & mp3FrameMasking[2]) == mp3FrameDataValid[2]) && \ + (((x)[3] & mp3FrameMasking[3]) == mp3FrameDataValid[3])) + + + +/* interface functions */ +int mmfile_format_read_stream_mp3 (MMFileFormatContext *formatContext); +int mmfile_format_read_frame_mp3 (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame); +int mmfile_format_read_tag_mp3 (MMFileFormatContext *formatContext); +int mmfile_format_close_mp3 (MMFileFormatContext *formatContext); + +/* internal */ +static int mmf_file_mp3_get_infomation (char *src, AvFileContentInfo* pInfo ); + +EXPORT_API +int mmfile_format_open_mp3 (MMFileFormatContext *formatContext) +{ + AvFileContentInfo *privateData = NULL;; + int ret = 0; + + debug_fenter(); + + if (NULL == formatContext) + { + debug_error("formatContext is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + if (formatContext->pre_checked == 0) { + ret = MMFileFormatIsValidMP3 (formatContext->uriFileName,5); + if ( ret == 0 ) + { + debug_error("It is not mp3 file\n"); + return MMFILE_FORMAT_FAIL; + } + } + + + formatContext->ReadStream = mmfile_format_read_stream_mp3; + formatContext->ReadFrame = mmfile_format_read_frame_mp3; + formatContext->ReadTag = mmfile_format_read_tag_mp3; + formatContext->Close = mmfile_format_close_mp3; + + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = 1; + + privateData = mmfile_malloc (sizeof (AvFileContentInfo)); + if (!privateData) + { + debug_error ("error: mmfile_malloc MP3 privateData\n"); + return MMFILE_FORMAT_FAIL; + } + + formatContext->privateFormatData = privateData; + + ret = mmf_file_mp3_get_infomation (formatContext->uriFileName, privateData); + if ( ret == -1 ) + { + debug_error ("error: mmfile_format_read_stream_mp3\n"); + goto exception; + } + + return MMFILE_FORMAT_SUCCESS; + +exception: + mmfile_format_close_mp3 (formatContext); + return MMFILE_FORMAT_FAIL; +} + + +EXPORT_API +int mmfile_format_read_stream_mp3 (MMFileFormatContext *formatContext) +{ + AvFileContentInfo *privateData = NULL; + debug_fenter(); + + if (!formatContext || !formatContext->privateFormatData) + { + debug_error("formatContext is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + privateData = formatContext->privateFormatData; + + formatContext->duration = privateData->duration; + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = 1; + formatContext->nbStreams = 1; + formatContext->streams[MMFILE_AUDIO_STREAM] = mmfile_malloc (sizeof (MMFileFormatStream)); + if (NULL == formatContext->streams[MMFILE_AUDIO_STREAM]) + { + debug_error ("formatContext->streams[MMFILE_AUDIO_STREAM] is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + formatContext->streams[MMFILE_AUDIO_STREAM]->streamType = MMFILE_AUDIO_STREAM; + formatContext->streams[MMFILE_AUDIO_STREAM]->codecId = MM_AUDIO_CODEC_MP3; + formatContext->streams[MMFILE_AUDIO_STREAM]->bitRate = (privateData->bitRate*1000); + formatContext->streams[MMFILE_AUDIO_STREAM]->framePerSec = (privateData->duration == 0 ? 0 : privateData->frameNum/privateData->duration); + formatContext->streams[MMFILE_AUDIO_STREAM]->width = 0; + formatContext->streams[MMFILE_AUDIO_STREAM]->height = 0; + formatContext->streams[MMFILE_AUDIO_STREAM]->nbChannel = privateData->channels; + formatContext->streams[MMFILE_AUDIO_STREAM]->samplePerSec = privateData->sampleRate; + + return MMFILE_FORMAT_SUCCESS; +} + + +EXPORT_API +int mmfile_format_read_frame_mp3 (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame) +{ + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_read_tag_mp3 (MMFileFormatContext *formatContext) +{ + AvFileContentInfo *privateData = NULL; + debug_fenter(); + + if (!formatContext || !formatContext->privateFormatData) + { + debug_error("formatContext is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + privateData = formatContext->privateFormatData; + + if (privateData->pTitle) formatContext->title = mmfile_strdup(privateData->pTitle); + if (privateData->pArtist) formatContext->artist = mmfile_strdup(privateData->pArtist); + if (privateData->pAuthor) formatContext->author = mmfile_strdup(privateData->pAuthor); + if (privateData->pCopyright) formatContext->copyright = mmfile_strdup(privateData->pCopyright); + if (privateData->pDescription) formatContext->comment = mmfile_strdup(privateData->pDescription); + if (privateData->pAlbum) formatContext->album = mmfile_strdup(privateData->pAlbum); + if (privateData->pYear) formatContext->year = mmfile_strdup(privateData->pYear); + if (privateData->pGenre) formatContext->genre = mmfile_strdup(privateData->pGenre); + if (privateData->pTrackNum) formatContext->tagTrackNum = mmfile_strdup(privateData->pTrackNum); + if (privateData->pComposer) formatContext->composer = mmfile_strdup(privateData->pComposer); + if (privateData->pContentGroup) formatContext->classification = mmfile_strdup(privateData->pContentGroup); + if (privateData->pConductor) formatContext->conductor = mmfile_strdup(privateData->pConductor); + if (privateData->pUnsyncLyrics) formatContext->unsyncLyrics= mmfile_strdup(privateData->pUnsyncLyrics); + if (privateData->pSyncLyrics) formatContext->syncLyrics= privateData->pSyncLyrics; + if (privateData->syncLyricsNum) formatContext->syncLyricsNum= privateData->syncLyricsNum; + if (privateData->pRecDate) formatContext->recDate= mmfile_strdup(privateData->pRecDate); + + if(privateData->imageInfo.imageLen > 0) + { + formatContext->artwork = mmfile_malloc (privateData->imageInfo.imageLen); + if(formatContext->artwork) + { + formatContext->artworkSize = privateData->imageInfo.imageLen; + memcpy (formatContext->artwork, privateData->imageInfo.pImageBuf, privateData->imageInfo.imageLen); + if (strlen(privateData->imageInfo.imageMIMEType) > 0) + formatContext->artworkMime= mmfile_strdup(privateData->imageInfo.imageMIMEType); + else if(strlen(privateData->imageInfo.imageExt) > 0) { + debug_msg("ID3 tag V2 File"); + formatContext->artworkMime= mmfile_strdup(privateData->imageInfo.imageExt); + } + else { + debug_error("Album art image exist but there is no type information of album art\n"); + } + } + } + + return MMFILE_FORMAT_SUCCESS; +} + +EXPORT_API +int mmfile_format_close_mp3 (MMFileFormatContext *formatContext) +{ + AvFileContentInfo *privateData = NULL; + + if (formatContext) + { + privateData = formatContext->privateFormatData; + if (privateData) + { + mm_file_free_AvFileContentInfo (privateData); + mmfile_free (privateData); + formatContext->privateFormatData = NULL; + } + } + + return MMFILE_FORMAT_SUCCESS; +} + +static int +__AvExtractI4(unsigned char *buf) +{ + int I4; + + I4 = buf[0]; + I4 <<= 8; + I4 |= buf[1]; + I4 <<= 8; + I4 |= buf[2]; + I4 <<= 8; + I4 |= buf[3]; + + return I4; +} + +static int +__AvExtractI2(unsigned char *buf) +{ + int I2; + + I2 = buf[0]; + I2 <<= 8; + I2 |= buf[1]; + I2 <<= 8; + + return I2; +} + +static int +__AvGetXingHeader( AvXHeadData* headData, unsigned char *buf ) +{ + int index, headFlags; + int hId, hMode, hSrIndex; + static int mp3SamRateTable[4] = { 44100, 48000, 32000, 99999 }; + + // get Xing header data + headData->flags = 0; + + // get selected MP3 header data + hId = (buf[1] >> 3) & 1; + hSrIndex = (buf[2] >> 2) & 3; + hMode = (buf[3] >> 6) & 3; + + + // determine offset of header + if( hId ) // mpeg1 + { + if( hMode != 3 ) + buf += (32+4); + else + buf += (17+4); + } + else // mpeg2 + { + if( hMode != 3 ) + buf += (17+4); + else + buf += (9+4); + } + + if( buf[0] != 'X' ) return 0; // fail + if( buf[1] != 'i' ) return 0; // header not found + if( buf[2] != 'n' ) return 0; + if( buf[3] != 'g' ) return 0; + buf += 4; + + headData->hId = hId; + headData->sampRate = mp3SamRateTable[hSrIndex]; + if( hId == 0 ) + headData->sampRate >>= 1; + + headFlags = headData->flags = __AvExtractI4( buf ); // get flags + buf+=4; + + if( headFlags & FRAMES_FLAG ) + { + headData->frames = __AvExtractI4( buf ); + buf+=4; + } + if( headFlags & BYTES_FLAG ) + { + headData->bytes = __AvExtractI4( buf ); + buf+=4; + } + + if( headFlags & TOC_FLAG ) + { + if( headData->toc != NULL ) + { + for( index = 0; index < 100; index++ ) + headData->toc[index] = buf[index]; + } + buf+=100; + } + + headData->vbrScale = -1; + if( headFlags & VBR_SCALE_FLAG ) + { + headData->vbrScale = __AvExtractI4( buf ); + buf+=4; + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("Xing header: sampling-rate:%d, stream-size:%d, frame-number:%d\n", + headData->sampRate, headData->bytes, headData->frames); + #endif + + return 1; // success +} + +static int +__AvGetVBRIHeader( AvVBRIHeadData* headData, unsigned char *buf ) +{ + int hId, hSrIndex; + static int mp3SamRateTable[4] = { 44100, 48000, 32000, 99999 }; + + + // get selected MP3 header data + hId = (buf[1] >> 3) & 1; + hSrIndex = (buf[2] >> 2) & 3; + + buf += (32+4); + + if( buf[0] != 'V' ) return 0; // fail + if( buf[1] != 'B' ) return 0; // header not found + if( buf[2] != 'R' ) return 0; + if( buf[3] != 'I' ) return 0; + buf += 4; + + headData->hId = hId; + headData->sampRate = mp3SamRateTable[hSrIndex]; + if( hId == 0 ) + headData->sampRate >>= 1; + + headData->vID = __AvExtractI2( buf ); // get ver ID + buf+=2; + headData->delay = __AvExtractI2( buf ); + buf+=2; + headData->qualityIndicator = buf[0]; + buf+=2; + headData->bytes = __AvExtractI4( buf ); + buf+=4; + headData->frames= __AvExtractI4( buf ); + buf+=4; + headData->numOfTOC = __AvExtractI2( buf ); + buf+=2; + headData->vbriScale = __AvExtractI2( buf ); + buf+=2; + headData->sizePerTable = __AvExtractI2( buf ); + buf+=2; + headData->framesPerTable = __AvExtractI2( buf ); + buf+=2; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("Vbri header: sampling-rate:%d, stream-size:%d, frame-number:%d\n", + headData->sampRate, headData->bytes, headData->frames); + #endif + + return true; // success +} +static bool +__AvIsValidHeader(AvFileContentInfo* pInfo, unsigned char *buf) +{ + bool bSync = false; + + if (VALID_SYNC(buf)) + { + mp3FrameDataValid[0] = (0xFF) & (mp3FrameMasking[0]); + mp3FrameDataValid[1] = (0xE0 | (buf[AV_MP3HDR_VERSION_OFS] & AV_MP3HDR_VERSION_M) + | (buf[AV_MP3HDR_LAYER_OFS] & AV_MP3HDR_LAYER_M)) & (mp3FrameMasking[1]); + mp3FrameDataValid[2] = (buf[AV_MP3HDR_SAMPLERATE_OFS] & AV_MP3HDR_SAMPLERATE_M) & + (mp3FrameMasking[2]); + mp3FrameDataValid[3] = (buf[AV_MP3HDR_CHANNEL_OFS] & AV_MP3HDR_CHANNEL_M) & + (mp3FrameMasking[3]); + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("*** [%02x][%02x][%02x][%02x] : [%02x][%02x][%02x][%02x]", + buf[0], buf[1], buf[2],buf[3], + mp3FrameDataValid[0], mp3FrameDataValid[1], mp3FrameDataValid[2],mp3FrameDataValid[3]); + #endif + + /* + * MPEG Audio Layer I/II/III frame header + * from : http://www.mp3-tech.org/programmer/frame_header.html * + * + * AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM + * + * A 11 (31-21) Frame sync (all bits must be set) + * B 2 (20,19) MPEG Audio version ID + * C 2 (18,17) Layer description + * D 1 (16) Protection bit + * E 4 (15,12) Bitrate index + * F 2 (11,10) Sampling rate frequency index + * G 1 (9) Padding bit + * H 1 (8) Private bit. This one is only informative. + * I 2 (7,6) Channel Mode + * J 2 (5,4) Mode extension (Only used in Joint stereo) + * K 1 (3) Copyright + * L 1 (2) Original + * M 2 (1,0) Emphasis + * + */ + + /* Simple check for version, layer, bitrate, samplerate */ + if ( (buf[1] & 0x18) != 0x08 && /* 000XX000 : MPEG Audio version ID, XX=01 - reserved => 00001000(0x08) */ + (buf[1] & 0x06) != 0x00 && /* 00000XX0 : Layer description, XX=00 - reserved => 00000000(0x00) */ + (buf[2] & 0xF0) != 0xF0 && /* XXXX0000 : Bitrate index, XX=1111 - bad => 11110000(0xF0) */ + (buf[2] & 0x0C) != 0x0C) /* 0000XX00 : Sampling rate frequency index , XX=11 -reserved => 00001100(0x0C) */ + { + bSync = true; + } + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("=> %s\n", bSync? "Good!":"Bad..."); + #endif + } + + if(bSync == true) + return true; + else + return false; +} + +static bool +__AvParseMp3Header( AvFileContentInfo* pInfo, unsigned char* header ) +{ + unsigned char result; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("### [%02x][%02x][%02x][%02x] ###\n", header[0], header[1], header[2],header[3]); + #endif + + // 1. Check the version of mp3 + result = header[1] & MASK_MPEG; + switch (result) + { + case MASK_MPEG_1: + pInfo->mpegVersion = AV_MPEG_VER_1; + break; + case MASK_MPEG_2: + pInfo->mpegVersion = AV_MPEG_VER_2; + break; + case MASK_MPEG_25: + pInfo->mpegVersion = AV_MPEG_VER_25; + break; + default: + return false; + } + + // 2. Get a layer + result = header[1] & MASK_LAYER; + switch (result) + { + case MASK_LAYER_1: + pInfo->layer = AV_MP3_LAYER_1; + break; + case MASK_LAYER_2: + pInfo->layer = AV_MP3_LAYER_2; + break; + case MASK_LAYER_3: + pInfo->layer = AV_MP3_LAYER_3; + break; + default: + return false; + } + + // 3. bitrate + result = header[2] >> 4; + if ( pInfo->mpegVersion == AV_MPEG_VER_1 ) + pInfo->bitRate = mp3BitRateTable[0][pInfo->layer-1][(int)result] ; + else + pInfo->bitRate = mp3BitRateTable[1][pInfo->layer-1][(int)result] ; + + // 4. samplerate + result = ( header[2] & MASK_SAMPLERATE ) >> 2; + if ( result == 0x03 ) + return false; + else + pInfo->sampleRate = mp3SamRateTable[pInfo->mpegVersion - 1][(int)result]; + + // 5. channel + result = header[3] & MASK_CHANNEL; + switch (result) + { + case MASK_CHANNEL_ST: + pInfo->channelIndex = 0; + pInfo->channels = 2; + break; + case MASK_CHANNEL_JS: + pInfo->channelIndex = 1; + pInfo->channels = 2; + break; + case MASK_CHANNEL_DC: + pInfo->channelIndex = 2; + pInfo->channels = 2; + break; + case MASK_CHANNEL_MN: + pInfo->channelIndex = 3;; + pInfo->channels = 1; + break; + default: + return false; + } + + // 6. padding + result = header[2] & MASK_PADDING; + if ( result == MASK_PADDING ) + pInfo->bPadding = true; + else + pInfo->bPadding = false; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("=> samplerate=%d, bitrate=%d, layer=%d, version=%d, channel=%d, padding=%d", + pInfo->sampleRate, pInfo->bitRate, pInfo->layer, pInfo->mpegVersion, pInfo->channels, pInfo->bPadding ); + #endif + + return true; +} + +static bool +__AvParseXingHeader( AvFileContentInfo* pInfo, unsigned char* buf ) +{ + AvXHeadData data; + memset( &data, 0x00, sizeof(AvXHeadData) ); + + // 1. Xing Header üũ + if((pInfo->mpegVersion == AV_MPEG_VER_1) && (pInfo->channels == 2)) + { + if( buf[36] != 'X' ) return false; + if( buf[37] != 'i' ) return false; + if( buf[38] != 'n' ) return false; + if( buf[39] != 'g' ) return false; + } + else + if((pInfo->mpegVersion == AV_MPEG_VER_2) && (pInfo->channels == 1)) + { + if( buf[13] != 'X' ) return false; + if( buf[14] != 'i' ) return false; + if( buf[15] != 'n' ) return false; + if( buf[16] != 'g' ) return false; + } + else + { + if( buf[21] != 'X' ) return false; + if( buf[22] != 'i' ) return false; + if( buf[23] != 'n' ) return false; + if( buf[24] != 'g' ) return false; + } + + // 2. TOC�� ����ұï¿? + if ( pInfo->pToc ) + data.toc = (unsigned char *)(pInfo->pToc); + + if ( __AvGetXingHeader( &data, (unsigned char *)buf ) == 1 ) // VBR�̴�. + { + if (data.sampRate == 0 || data.bytes == 0 || data.frames == 0) { + debug_error ("invalid Xing header\n"); + return false; + } + + pInfo->sampleRate = data.sampRate; + pInfo->datafileLen = data.bytes; + pInfo->frameNum = data.frames; + pInfo->frameSize = (int) ( (float) data.bytes / (float) data.frames ) ; + pInfo->bVbr = true; + return true; + } + else + return false; +} + +static bool +__AvParseVBRIHeader( AvFileContentInfo* pInfo, unsigned char* buf ) +{ + AvVBRIHeadData data; + memset( &data, 0x00, sizeof(AvVBRIHeadData) ); + + // 1. Xing Header üũ + if((pInfo->mpegVersion == AV_MPEG_VER_1) && (pInfo->channels == 2)) + { + if( buf[36] != 'V' ) return false; + if( buf[37] != 'B' ) return false; + if( buf[38] != 'R' ) return false; + if( buf[39] != 'I' ) return false; + } + else + if((pInfo->mpegVersion == AV_MPEG_VER_2) && (pInfo->channels == 1)) + { + if( buf[36] != 'V' ) return false; + if( buf[37] != 'B' ) return false; + if( buf[38] != 'R' ) return false; + if( buf[39] != 'I' ) return false; + } + else + { + if( buf[36] != 'V' ) return false; + if( buf[37] != 'B' ) return false; + if( buf[38] != 'R' ) return false; + if( buf[39] != 'I' ) return false; + } + + // 2. TOC�� ����ұï¿? + if ( pInfo->pToc ) + data.toc = (unsigned char*)(pInfo->pToc); + + if ( __AvGetVBRIHeader( &data, (unsigned char*)buf ) == 1 ) // VBR�̴�. + { + if (data.sampRate == 0 || data.bytes == 0 || data.frames == 0) { + debug_error ("invalid Vbri header\n"); + return false; + } + + pInfo->sampleRate = data.sampRate; + pInfo->datafileLen = data.bytes; + pInfo->frameNum = data.frames; + pInfo->frameSize = (int) ( (float) data.bytes / (float) data.frames ) ; + pInfo->bVbr = true; + return true; + } + else + return false; +} + +#ifdef __MMFILE_NEW_FRAME_FUNC // from gst +static bool +__AvGetMp3FrameSize( AvFileContentInfo* pInfo ) +{ + unsigned int frameSize = 0; + if ( pInfo == NULL ) + return false; + + frameSize = pInfo->bPadding; + if (pInfo->bitRate == 0) { + if (pInfo->layer == 1) { + frameSize *= 4; + frameSize += 0/* FIXME: possible_free_framelen*/; + pInfo->bitRate = frameSize * pInfo->sampleRate / 48000; + } else { + frameSize += 0/* FIXME: possible_free_framelen*/; + pInfo->bitRate = frameSize * pInfo->sampleRate / + ((pInfo->layer == AV_MP3_LAYER_3 && pInfo->mpegVersion != AV_MPEG_VER_1) ? 72000 : 144000); + } + } else { + /* calculating */ + if (pInfo->layer == 1) { + frameSize = ((12000 * pInfo->bitRate / pInfo->sampleRate) + frameSize) * 4; + } else { + frameSize += ((pInfo->layer == AV_MP3_LAYER_3 + && pInfo->mpegVersion != AV_MPEG_VER_1) ? 72000 : 144000) * pInfo->bitRate / pInfo->sampleRate; + } + } + + pInfo->frameSize = (int)frameSize; + + return true; +} +#endif + + +static bool +__AvGetXingBitrate( AvFileContentInfo* pInfo ) +{ + float br, factor; + int padding; + + if ( pInfo == NULL || pInfo->bVbr == false ) + return false; + + if ( pInfo->bPadding ) + padding = 1; + else + padding = 0; + + if (pInfo->mpegVersion == AV_MPEG_VER_1 ) // MPEG version 1 + { + if (pInfo->layer == AV_MP3_LAYER_1 ) // Layer 1 + factor = 48000.0; + else // Layer 2, 3 + factor = 144000.0; + } + else // MPEG version 2 + { + if (pInfo->layer == AV_MP3_LAYER_1 ) // Layer 1 + factor = 24000.0; + else // Layer 2, 3 + factor = 72000.0; + } + + br = ( pInfo->frameSize - padding ) * pInfo->sampleRate / factor; + + pInfo->bitRate = (int) br; + + return true; +} + +static bool +__AvGetVBRIBitrate( AvFileContentInfo* pInfo ) +{ + float br, factor; + int padding; + + if ( pInfo == NULL || pInfo->bVbr == false ) + return false; + + if ( pInfo->bPadding ) + padding = 1; + else + padding = 0; + + if (pInfo->mpegVersion == AV_MPEG_VER_1 ) // MPEG version 1 + { + if (pInfo->layer == AV_MP3_LAYER_1 ) // Layer 1 + factor = 48000.0; + else // Layer 2, 3 + factor = 144000.0; + } + else // MPEG version 2 + { + if (pInfo->layer == AV_MP3_LAYER_1 ) // Layer 1 + factor = 24000.0; + else // Layer 2, 3 + factor = 72000.0; + } + + br = ( pInfo->frameSize - padding ) * pInfo->sampleRate / factor; + + pInfo->bitRate = (int) br; + + return true; +} + +static int __AvGetLastID3offset (MMFileIOHandle *fp, unsigned int *offset) +{ +#define _MMFILE_MP3_TAGV2_HEADER_LEN 10 +#define _MMFILE_GET_INT_NUMBER(buff) (int)( (((int)(buff)[0]) << 24) | (((int)(buff)[1]) << 16) | (((int)(buff)[2]) << 8) | (((int)(buff)[3]))) + + unsigned char tagHeader[_MMFILE_MP3_TAGV2_HEADER_LEN] = {0,}; + unsigned int tagInfoSize = 0; + unsigned int acc_tagsize = 0; + int tagVersion = 0; + int encSize = 0; + int readed = 0; + + /*init offset*/ + *offset = 0; + + mmfile_seek(fp, 0, MMFILE_SEEK_SET); + +_START_TAG_SEARCH: + + readed = mmfile_read (fp, tagHeader, _MMFILE_MP3_TAGV2_HEADER_LEN); + if (readed != _MMFILE_MP3_TAGV2_HEADER_LEN) { + debug_error ("read error occured.\n"); + return 0; + } + + if (memcmp (tagHeader, "ID3", 3) == 0) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("'ID3' found.\n"); + #endif + } else { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("'ID3' not found.\n"); + #endif + goto search_end; + } + + /**@note weak id3v2 tag checking*/ + if (tagHeader[3] != 0xFF && tagHeader[4] != 0xFF && + (tagHeader[6] & 0x80) == 0 && (tagHeader[7] & 0x80) == 0 && + (tagHeader[8] & 0x80) == 0 && (tagHeader[9] & 0x80) == 0) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("good ID3V2 tag.\n"); + #endif + } else { + debug_warning ("It's bad ID3V2 tag.\n"); + goto search_end; + } + + tagVersion = tagHeader[3]; + + if (tagVersion > 4) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg("Tag version not supported\n"); + #endif + goto search_end; + } + + encSize = _MMFILE_GET_INT_NUMBER(&tagHeader[6]); + tagInfoSize = _MMFILE_MP3_TAGV2_HEADER_LEN; + tagInfoSize += (((encSize & 0x0000007F) >> 0) | ((encSize & 0x00007F00) >> 1) | ((encSize & 0x007F0000) >> 2) | ((encSize & 0x7F000000) >> 3)); + + /**@note unfortunately, some contents has many id3 tag.*/ + acc_tagsize += tagInfoSize; + #ifdef __MMFILE_TEST_MODE__ + debug_msg("tag size: %u, offset: %u\n", tagInfoSize, acc_tagsize); + #endif + + mmfile_seek(fp, acc_tagsize, MMFILE_SEEK_SET); + *offset = acc_tagsize; + goto _START_TAG_SEARCH; + +search_end: + return 1; +} + + + +/* + * This fuction retrieves the start position of header. + * Param _pFile [in] Specifies the file pointer of mp3 file. + * This function returns the start position of header. + */ +static int +__AvFindStartOfMp3Header(MMFileIOHandle *hFile, unsigned char *buf, AvFileContentInfo* pInfo) +{ + int index=0; + long readLen; + unsigned long id3v2TagLen = 0; + unsigned char *pHeader = NULL; + unsigned long preHeaderGap = 0; + unsigned long frameLen = 0; + unsigned long nextFrameOff = 0; /* Offset to the start of the next frame */ + unsigned long nextFrameOffEnd = 0; + unsigned long bufLen = 0; + bool bFoundSync = false; + unsigned long minLen; + + + + if(pInfo->fileLen > (_AV_MP3_HEADER_POSITION_MAX+ pInfo->tagV2Info.tagLen)) + bufLen = _AV_MP3_HEADER_POSITION_MAX; + else + bufLen = pInfo ->fileLen - pInfo->tagV2Info.tagLen; + + if(IS_ID3V2_TAG(buf)) + { + + + if(pInfo->tagV2Info.tagVersion == 0x02) + { + + if(!mm_file_id3tag_parse_v222(pInfo, buf)) + pInfo->tagV2Info.tagLen = 0; + } + else if (pInfo->tagV2Info.tagVersion == 0x03) + { + + if(!mm_file_id3tag_parse_v223(pInfo, buf)) + pInfo->tagV2Info.tagLen = 0; + } + else if (pInfo->tagV2Info.tagVersion == 0x04) + { + + if(!mm_file_id3tag_parse_v224(pInfo, buf)) // currently 2.4 ver pased by 2.3 routine + pInfo->tagV2Info.tagLen = 0; + } + else + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "pInfo->tagV2Info.tagVersion(%d)\n", pInfo->tagV2Info.tagVersion); + #endif + } + + id3v2TagLen = pInfo->tagV2Info.tagLen; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "id3v2TagLen(%d)\n", id3v2TagLen); + #endif + + if(id3v2TagLen) + { + if (mmfile_seek (hFile, id3v2TagLen, SEEK_SET) < 0) { + debug_error ( "seek failed.\n"); + return -1; + } + if ((readLen = mmfile_read (hFile, buf, bufLen)) <= 0) { + debug_error ( "seek failed.\n"); + return -1; + } + } + while(1) + { + if (preHeaderGap == bufLen -2) + break; + if(__AvIsValidHeader(pInfo, buf+preHeaderGap)) + break; + preHeaderGap++; + } + + } + else + { + while(1) + { + if (preHeaderGap == bufLen -2) + break; + if(__AvIsValidHeader(pInfo, buf+preHeaderGap)) + break; + preHeaderGap++; + } + + } + minLen = 4; + + buf += preHeaderGap; + index += preHeaderGap; + while (index <= (bufLen - minLen)) + { + if(buf[0] == 0xff) + { + if(VALID_SYNC(buf)) + { + if(bufLen - index > 256) + { + pHeader = mmfile_malloc (256); + if (pHeader == NULL) + { + debug_error ( "malloc failed.\n"); + return -1; + } + strncpy((char *)pHeader, (char *)buf, 256); + } + else + { + debug_error ( "Header field is not exist\n"); + return -1; + } + if ( __AvParseMp3Header( pInfo, pHeader ) == false) + { + //return -1; + if(pHeader) + _FREE_EX(pHeader); + debug_warning ( "Mp3 parse header failed & index(%d)\n", index); + buf++; + index++; + continue; + } + else + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "This header is valid. index(%d)\n", index); + #endif + } + + if ( __AvParseXingHeader( pInfo, pHeader ) ) + { + __AvGetXingBitrate( pInfo ); + } + else if(__AvParseVBRIHeader( pInfo, pHeader )) + { + __AvGetVBRIBitrate( pInfo ); + } + + if (pInfo->bVbr) + { + if(pHeader) + _FREE_EX(pHeader); + bFoundSync = true; + break; + } + else + { + if(__AvIsValidHeader(pInfo, pHeader)) + { + if(pHeader) + _FREE_EX(pHeader); + + __AvGetMp3FrameSize( pInfo ); + pInfo->datafileLen = pInfo->fileLen - pInfo->headerPos; + frameLen = pInfo->frameSize; + if (frameLen) + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("<<< frameLen=[%d] >>> \n", frameLen); + #endif + + #ifndef __MMFILE_NEW_FRAME_FUNC // FIXME : what purpose to do this? + /* Account for loss of precision in the frame length calculation*/ + frameLen--; + #endif + + /* Check if the remaining buffer size is large enough to + * look for another sync */ + if ((index + frameLen) < (bufLen - (minLen - 1))) + { + nextFrameOff = frameLen; + nextFrameOffEnd = nextFrameOff +MIN(6, bufLen - (index+frameLen) - (minLen - 1)); + + /* Search the next few bytes for the next sync */ + while (nextFrameOff < nextFrameOffEnd) + { + if (VALID_SYNC(buf+nextFrameOff)) + { + if(IS_VALID_FRAME_MP3(buf+nextFrameOff)) + { + bFoundSync = true; + break; + } + } + nextFrameOff++; + } + if (bFoundSync == true) + break; + } + else + { + /* Assume that the first sync is valid, since there is not + * enough data in the buffer to look for the next sync */ + bFoundSync = true; + break; + } + } + + } + else + { + debug_warning ( "Is not vaild header pHeader\n"); + } + } + if(pHeader) + _FREE_EX(pHeader); + } + else + { + debug_warning ( "Mp3 file frist byte is 0xff, but not header sync\n"); + } + } + buf++; + index++; + } + + _FREE_EX (pHeader); + + if (mmfile_seek(hFile, 0, SEEK_SET) < 0) { + debug_error ( "seek error!\n"); + return -1; + } + if(index > (bufLen - minLen)) + { + debug_warning ( "Mp3 file sync is not found : index(%d) bufLen(%d), minLen(%d)\n", index, bufLen, minLen); + return -1; + } + + if(bFoundSync == true) + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Mp3 file found a sync Success!\n"); + #endif + } + else + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Mp3 file found a sync Failed!\n"); + #endif + return -1; + } + + return index+id3v2TagLen; +} + +/* + * This function retrieves the mp3 information. + * Param szFileName [in] Specifies a mp3 file path. + * Param _frame [out] Specifies a struct pointer for mp3 information. + * This function returns true on success, or false on failure. + */ +static int mmf_file_mp3_get_infomation (char *filename, AvFileContentInfo* pInfo ) +{ + MMFileIOHandle *hFile; + unsigned char header[256]; + unsigned long numOfFrames=0; + unsigned long frameSamples=0; + unsigned char *buf = NULL; + unsigned char* v2TagExistCheck = NULL; + unsigned int tempNumFrames = 0; + int readAmount = 0, readedDataLen = 0; + unsigned long long tempduration = 0; + unsigned char TagBuff[MP3TAGINFO_SIZE + TAGV1_SEEK_GAP]; + unsigned char TagV1ID[4] = { 0x54, 0x41, 0x47}; //TAG + int tagHeaderPos = 0; + int ret = 0; + unsigned int head_offset = 0; + debug_fenter(); + + if (pInfo == NULL || filename == NULL) + return -1; + + memset( pInfo, 0x00, sizeof(AvFileContentInfo) ); + + pInfo->tagV2Info.tagLen = 0; + pInfo->headerPos = 0; + pInfo->genre = 148; + + /*open*/ + ret = mmfile_open (&hFile, filename, MMFILE_RDONLY); + if (ret == MMFILE_UTIL_FAIL) + { + debug_error ( "open failed.\n"); + return -1; + } + + mmfile_seek (hFile, 0L, SEEK_END); + pInfo->fileLen = mmfile_tell (hFile); + if (pInfo->fileLen <= 0) + { + debug_error ( "file is too small.\n"); + goto EXCEPTION; + } + mmfile_seek (hFile, 0L, SEEK_SET); + + v2TagExistCheck = mmfile_malloc (MP3_TAGv2_HEADER_LEN); + if (v2TagExistCheck == NULL) { + debug_error ( "malloc failed.\n"); + goto EXCEPTION; + } + + if (mmfile_read (hFile, v2TagExistCheck, MP3_TAGv2_HEADER_LEN) > 0) + { + if(IS_ID3V2_TAG(v2TagExistCheck)) + { + if(!(v2TagExistCheck[3] == 0xFF || v2TagExistCheck[4] == 0xFF ||v2TagExistCheck[6] >= 0x80 || v2TagExistCheck[7] >= 0x80 || v2TagExistCheck[8] >= 0x80 || v2TagExistCheck[9] >= 0x80)) + { + if(!(v2TagExistCheck[3] > 0x04)) + { + pInfo->tagV2Info.tagVersion = v2TagExistCheck[3]; + pInfo->tagV2Info.tagLen = MP3_TAGv2_HEADER_LEN; + pInfo->tagV2Info.tagLen += (unsigned long)v2TagExistCheck[6] << 21 | (unsigned long)v2TagExistCheck[7] << 14 |(unsigned long)v2TagExistCheck[8] << 7 | (unsigned long)v2TagExistCheck[9]; + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "pInfo->tagV2Info.tagLen(%d), pInfo->tagV2Info.tagVersion(%d)\n", pInfo->tagV2Info.tagLen, pInfo->tagV2Info.tagVersion); + #endif + } + else + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "tag is a not supported version(%d)\n", v2TagExistCheck[3]); + #endif + } + } + else + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "tag is a tag data is valid.\n"); + #endif + } + } + else + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "this mp3 file is not included ID3v2 tag info!\n"); + #endif + } + } + else + { + debug_error ( "v2TagExistCheck value read fail!\n"); + if(v2TagExistCheck) + _FREE_EX(v2TagExistCheck); + goto EXCEPTION; + } + + if(v2TagExistCheck) + _FREE_EX(v2TagExistCheck); + + if(!(pInfo->fileLen > pInfo->tagV2Info.tagLen )) + pInfo->tagV2Info.tagLen = 0; + + if (mmfile_seek(hFile, 0L, SEEK_SET) < 0) + goto EXCEPTION; + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "pInfo->fileLen(%lld)\n", pInfo->fileLen); + #endif + + if(pInfo->fileLen > (_AV_MP3_HEADER_POSITION_MAX + pInfo->tagV2Info.tagLen)) + { + readAmount = _AV_MP3_HEADER_POSITION_MAX + pInfo->tagV2Info.tagLen; + buf = mmfile_malloc (readAmount); + if (buf == NULL) { + debug_error ( "malloc failed.\n"); + goto EXCEPTION; + } + + while(readAmount > 0) + { + if(readAmount >= AV_MP3_HEADER_READ_MAX) + { + if ((readedDataLen <= _AV_MP3_HEADER_POSITION_MAX + pInfo->tagV2Info.tagLen) + &&(mmfile_read(hFile, buf+readedDataLen, AV_MP3_HEADER_READ_MAX) <= 0)) + { + if(buf) + _FREE_EX(buf); + + goto EXCEPTION; + } + else + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Reading buf readedDataLen(%d) readAmount (%d)\n", readedDataLen,readAmount); + #endif + } + } + else + { + if ((readedDataLen <= _AV_MP3_HEADER_POSITION_MAX + pInfo->tagV2Info.tagLen) + &&(mmfile_read(hFile, buf+readedDataLen, readAmount) <= 0)) + { + if(buf) + _FREE_EX(buf); + + goto EXCEPTION; + } + else + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "The remained buf readed! readedDataLen(%d) readAmount (%d)\n", readedDataLen,readAmount); + #endif + } + } + + readAmount -= AV_MP3_HEADER_READ_MAX; + readedDataLen += AV_MP3_HEADER_READ_MAX; + + if(readAmount <= 0) + break; + } + } + else + { + buf = mmfile_malloc (pInfo->fileLen); + if (buf == NULL) + { + goto EXCEPTION; + } + + if (mmfile_read(hFile, buf, pInfo->fileLen) <= 0) + { + if(buf) + _FREE_EX(buf); + goto EXCEPTION; + } + } + + + if (__AvGetLastID3offset (hFile, &head_offset)) { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "search start offset: %u\n", head_offset); + #endif + pInfo->tagV2Info.tagLen = head_offset; + } + + pInfo->headerPos = (long) __AvFindStartOfMp3Header(hFile, buf, pInfo); + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Header Pos: %ld\n", pInfo->headerPos); + #endif + + if(buf) + _FREE_EX(buf); + + if (pInfo->headerPos == -1) + goto EXCEPTION; + + if (mmfile_seek(hFile, pInfo->headerPos, SEEK_SET) < 0) + goto EXCEPTION; + + if (mmfile_read (hFile, header, 256) <= 0) + goto EXCEPTION; + + if ( __AvParseMp3Header( pInfo, header ) == false) + goto EXCEPTION; + + if ( __AvParseXingHeader( pInfo, header ) ) + { + __AvGetXingBitrate( pInfo ); + } + else if(__AvParseVBRIHeader( pInfo, header )) + { + __AvGetVBRIBitrate( pInfo ); + } + else + { + __AvGetMp3FrameSize( pInfo ); + pInfo->datafileLen = pInfo->fileLen - pInfo->headerPos; + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Mp3 File FrameSize (%d) pInfo->headerPos(%d)\n", pInfo->frameSize,pInfo->headerPos); + #endif + } + + if (mmfile_seek (hFile, -(MP3TAGINFO_SIZE + TAGV1_SEEK_GAP), SEEK_END) < 0) + goto EXCEPTION; + + + pInfo ->bV1tagFound = false; + + if (mmfile_read (hFile, TagBuff, MP3TAGINFO_SIZE + TAGV1_SEEK_GAP) <= 0) + goto EXCEPTION; + + if ((tagHeaderPos = __AvMemstr(TagBuff, TagV1ID, 3, TAGV1_SEEK_GAP+5)) >= 0) + { + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Mp3 File Tag is existing\n"); + #endif + + pInfo ->bV1tagFound = true; + memcpy(TagBuff, (TagBuff + tagHeaderPos), MP3TAGINFO_SIZE); + + if(!mm_file_id3tag_parse_v110(pInfo, TagBuff)) + goto EXCEPTION; + } + + mm_file_id3tag_restore_content_info (pInfo); + + if(pInfo->bVbr) + numOfFrames = pInfo->frameNum*10; + else + { + numOfFrames = ((pInfo->fileLen + -(pInfo->headerPos + (pInfo ->bV1tagFound ? MP3TAGINFO_SIZE : 0) ) )*10) / pInfo->frameSize; + } + tempNumFrames = (unsigned int)(numOfFrames/10); + + + + if((numOfFrames - tempNumFrames * 10 ) > 5) + numOfFrames = (numOfFrames/10) + 1; + else + numOfFrames = numOfFrames/10; + + + + tempduration = (unsigned long long)(numOfFrames *1000); + + if(pInfo->mpegVersion== 1) + { + if(pInfo->layer== 1) + frameSamples = MPEG_1_SIZE_LAYER_1; + else + frameSamples = MPEG_1_SIZE_LAYER_2_3; + } + else + { + if(pInfo->layer == 1) + frameSamples = MPEG_2_SIZE_LAYER_1; + else + frameSamples = MPEG_2_SIZE_LAYER_2_3; + } + + if(tempduration < (unsigned long long)pInfo->sampleRate) + { + tempduration = (tempduration*frameSamples*10)/pInfo->sampleRate; + tempduration = (tempduration/10); + } + else + tempduration = (tempduration*frameSamples)/pInfo->sampleRate; + + pInfo->duration = tempduration; + + mmfile_close(hFile); + + /*debug print*/ + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "Mp3 File pInfo->duration (%lld) \n", pInfo->duration); + debug_msg ( "** MP3 **\n"); + debug_msg ( "Version : %u\n", pInfo->mpegVersion); + debug_msg ( "Layer : %u\n", pInfo->layer); + debug_msg ( "Channel idx: %u\n", pInfo->channelIndex); + debug_msg ( "Is VBR : %d\n", (pInfo->bVbr == true ? 1 : 0)); + debug_msg ( "Bitrate : %u\n", pInfo->bitRate); + debug_msg ( "SampleRate : %u\n", pInfo->sampleRate); + debug_msg ( "Channels : %u\n", pInfo->channels); + debug_msg ( "**** Info #1 ****\n"); + debug_msg ( "Title : %s\n", pInfo->pTitle); + debug_msg ( "Artist : %s\n", pInfo->pArtist); + debug_msg ( "Album : %s\n", pInfo->pAlbum); + debug_msg ( "Year : %s\n", pInfo->pYear); + debug_msg ( "Comment : %s\n", pInfo->pComment); + debug_msg ( "TrackNum : %s\n", pInfo->pTrackNum); + debug_msg ( "Genre : %s\n", pInfo->pGenre); + debug_msg ( "**** Info #2 ****\n"); + debug_msg ( "Author : %s\n", pInfo->pAuthor); + debug_msg ( "Copyright : %s\n", pInfo->pCopyright); + debug_msg ( "Description : %s\n", pInfo->pDescription); + debug_msg ( "Rating : %s\n", pInfo->pRating); + debug_msg ( "RecDate : %s\n", pInfo->pRecDate); + debug_msg ( "Encoded by : %s\n", pInfo->pEncBy); + debug_msg ( "URL : %s\n", pInfo->pURL); + debug_msg ( "Ori. Artist : %s\n", pInfo->pOriginArtist); + debug_msg ( "Composer : %s\n", pInfo->pComposer); + debug_msg ( "Conductor : %s\n", pInfo->pConductor); + debug_msg ( "Artwork : mime(%s) addr(%p) size(%d)\n", pInfo->imageInfo.imageMIMEType, pInfo->imageInfo.pImageBuf, pInfo->imageInfo.imageLen); + debug_msg ( "UnsyncLyrics : %s\n", pInfo->pUnsyncLyrics); + debug_msg ( "SyncLyrics size : %d\n", pInfo->syncLyricsNum); + + #endif + + return 0; + +EXCEPTION: + debug_error ("Error occured!\n"); + mmfile_close(hFile); + return -1; +} diff --git a/formats/ffmpeg/mm_file_format_tag_id3.c b/formats/ffmpeg/mm_file_format_tag_id3.c new file mode 100755 index 0000000..9a96ca7 --- /dev/null +++ b/formats/ffmpeg/mm_file_format_tag_id3.c @@ -0,0 +1,198 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string.h> +#include <stdlib.h> +#include "mm_debug.h" +#include "mm_file_format_tag_id3.h" + +#define MMFILE_ID3V1TAG_SIZE 128 +#define MMFILE_ID3V2TAG_HEADER_SIZE 10 + +static unsigned int GetSynchsafeInteger (unsigned int value); + +EXPORT_API +int MMFileID3V1TagFind (MMFileIOHandle *fp, unsigned char bAppended, unsigned int startOffset, unsigned int endOffset, tMMFileTags *out) +{ + int ret = 0; + + if (!fp || !out) + { + debug_error ("Invalid input\n"); + return MMFILE_ID3TAG_FAIL; + } + + if (bAppended) + { + unsigned char tagSymbol[3] = {0,}; + + unsigned int offset = endOffset - MMFILE_ID3V1TAG_SIZE; + + mmfile_seek (fp, offset, MMFILE_SEEK_SET); + + ret = mmfile_read (fp, tagSymbol, 3); + if (MMFILE_UTIL_FAIL == ret) + { + debug_error ("read error\n"); + return MMFILE_ID3TAG_FAIL; + } + + if (memcmp ("TAG", tagSymbol, 3) == 0) + { + unsigned char versionBuf[2] = {0,}; + + out->typeOfTag = MMFILE_TAG_ID3V1; + out->startOffset = offset; + out->endOffset = endOffset; + out->bAppendedTag = 1; + out->tagSize = MMFILE_ID3V1TAG_SIZE; + + offset += 125; // byte delimiter offset: ID3V1.1 spec. + mmfile_seek (fp, offset, MMFILE_SEEK_SET); + + ret = mmfile_read (fp, versionBuf, 2); + if (MMFILE_UTIL_FAIL == ret) + { + debug_error ("read error\n"); + return MMFILE_ID3TAG_FAIL; + } + + if (versionBuf[0] == '\0' && versionBuf[1] != '\0') + { + out->version = MMFILE_ID3TAG_V1_1; + } + else + { + out->version = MMFILE_ID3TAG_V1_0; + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("typeOfTag = %d\n", out->typeOfTag); + debug_msg ("startOffset = %d\n", out->startOffset); + debug_msg ("endOffset = %d\n", out->endOffset); + debug_msg ("bAppendedTag = %d\n", out->bAppendedTag); + debug_msg ("tagSize = %d\n", out->tagSize); + debug_msg ("version = %d\n", out->version); + #endif + + return MMFILE_ID3TAG_SUCCESS; + } + } + + return MMFILE_ID3TAG_FAIL; +} + + +EXPORT_API +int MMFileID3V2TagFind (MMFileIOHandle *fp, unsigned char bAppended, unsigned int startOffset, unsigned int endOffset, tMMFileTags *out) +{ + unsigned char *id3v2ID = NULL; + unsigned char header[MMFILE_ID3V2TAG_HEADER_SIZE] = {0,}; + unsigned int offset = 0; + unsigned int index = 0; + int ret = 0; + + if (!fp || !out) + { + debug_error ("Invalid input\n"); + return MMFILE_ID3TAG_FAIL; + } + + if (bAppended) + { + id3v2ID = "3DI"; + offset = endOffset - MMFILE_ID3V2TAG_HEADER_SIZE; + } + else + { + id3v2ID = "ID3"; + offset = startOffset; + } + + mmfile_seek (fp, offset, MMFILE_SEEK_SET); + + ret = mmfile_read (fp, header, MMFILE_ID3V2TAG_HEADER_SIZE); + if (MMFILE_UTIL_FAIL == ret) + { + debug_error ("read error\n"); + return MMFILE_ID3TAG_FAIL; + } + + if (memcmp (id3v2ID, header, 3) == 0) + { + index += 3; + out->typeOfTag = MMFILE_TAG_ID3V2; + out->bAppendedTag = bAppended; + out->startOffset = offset; + + //version check + switch (header[index]) + { + case 0: + out->version = MMFILE_ID3TAG_V2_0; + break; + case 2: + out->version = MMFILE_ID3TAG_V2_2; + break; + case 3: + out->version = MMFILE_ID3TAG_V2_3; + break; + case 4: + out->version = MMFILE_ID3TAG_V2_4; + break; + default: + debug_warning ("unknown version of id3v2\n"); + break; + } + + index += 2; + //check footer + unsigned char footer = (header[index] & 0x10) ? 1 : 0; + + index += 1; + //out->tagSize = GetSynchsafeInteger () + (footer ? 20 : 10) + + if (bAppended) + { +// out->endOffset + } + out->endOffset = endOffset; + } + + + return MMFILE_ID3TAG_FAIL; +} + + +static unsigned int GetSynchsafeInteger (unsigned int value) +{ + int i = 0; + const unsigned int mask = 0x7F000000; + unsigned int ret = 0; + + for (i = 0; i < sizeof(unsigned int); i++) + { + ret = (ret << 7) | ((value & mask) >> 24); + value <<= 8; + } + return ret; +} + diff --git a/formats/ffmpeg/mm_file_format_tags.c b/formats/ffmpeg/mm_file_format_tags.c new file mode 100755 index 0000000..f1c2bf2 --- /dev/null +++ b/formats/ffmpeg/mm_file_format_tags.c @@ -0,0 +1,250 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string.h> +#include <stdlib.h> +#include "mm_debug.h" +#include "mm_file_utils.h" +#include "mm_file_format_tag_id3.h" +#include "mm_file_format_tags.h" + +#define MMFILE_TAGS_NUMBER_OF_ELEMENTS(X) (sizeof X / sizeof X[0]) + +typedef int (*MMFileFindTagFunc)(MMFileIOHandle *fp, unsigned char bAppended, unsigned int startOffset, unsigned int endOffset, tMMFileTags *out); + + +typedef struct mmfiletagsdata +{ + MMFileIOHandle *fp; + unsigned int filesize; + unsigned int startOffset; + unsigned int endOffset; + MMFileList tagList; +} tMMFileTagsData; + + +MMFileFindTagFunc gFindPreprendTagFuncs[] = +{ + MMFileID3V2TagFind, +}; + +MMFileFindTagFunc gFindAppendTagFuncs[] = +{ + MMFileID3V1TagFind, + MMFileID3V2TagFind, +}; + + +int _MMFileFindTags (tMMFileTagsData *privateData, MMFileFindTagFunc FindTag, unsigned char bAppended); +int _MMFileFindPrependTags (tMMFileTagsData *privateData); +int _MMFileFindAppendTags (tMMFileTagsData *privateData); + + + +EXPORT_API +int MMFileOpenTags (MMFileTagsHandle *tagsHandle, const char *uriName) +{ + tMMFileTagsData *privateData = NULL; + int ret = 0; + + privateData = mmfile_malloc (sizeof(tMMFileTagsData)); + if (!privateData) + { + debug_error ("mmfile_malloc: tMMFileTagsData\n"); + return MMFILE_TAGS_FAIL; + } + + *tagsHandle = privateData; + + ret = mmfile_open (&privateData->fp, uriName, MMFILE_RDONLY); + if (MMFILE_UTIL_FAIL == ret) + { + debug_error ("mmfile_open\n"); + ret = MMFILE_TAGS_FAIL; + goto exception; + } + + ret = mmfile_seek (privateData->fp, 0, MMFILE_SEEK_END); + if (MMFILE_UTIL_FAIL == ret) + { + debug_error ("mmfile_seek\n"); + ret = MMFILE_TAGS_FAIL; + goto exception; + } + + privateData->filesize = mmfile_tell (privateData->fp); + + mmfile_seek (privateData->fp, 0, MMFILE_SEEK_SET); + + privateData->startOffset = 0; + privateData->endOffset = privateData->filesize; + + /* Find all of tags */ + while (MMFILE_TAGS_SUCCESS == _MMFileFindPrependTags (privateData)) ; + while (MMFILE_TAGS_SUCCESS == _MMFileFindAppendTags (privateData)) ; + + if (!privateData->tagList) + { + debug_error ("there is no tags\n"); + ret = MMFILE_TAGS_FAIL; + goto exception; + } + + return MMFILE_TAGS_SUCCESS; + +exception: + if (*tagsHandle) + { + MMFileTagsClose (*tagsHandle); + *tagsHandle = NULL; + } + + return ret; +} + +EXPORT_API +int MMFileTagsClose (MMFileTagsHandle tagsHandle) +{ + tMMFileTagsData *privateData = tagsHandle; + + if (privateData) + { + if (privateData->fp) + { + mmfile_close (privateData->fp); + } + + if (privateData->tagList) + { + mmfile_free (privateData->tagList); + } + + mmfile_free (privateData); + } + + return MMFILE_TAGS_SUCCESS; +} + + + +EXPORT_API +int MMFileGetFirstTag (MMFileTagsHandle tagsHandle, tMMFileTags *out) +{ + +} + +EXPORT_API +int MMFileGetNextTag (MMFileTagsHandle tagsHandle, tMMFileTags *out) +{ +} + +int _MMFileFindPrependTags (tMMFileTagsData *privateData) +{ + int i = 0; + int res = 0; + + if (!privateData || !privateData->fp) + { + debug_error ("Invalid argument\n"); + return MMFILE_TAGS_FAIL; + } + + for (i = 0; i < MMFILE_TAGS_NUMBER_OF_ELEMENTS(gFindPreprendTagFuncs); i++) + { + if (gFindPreprendTagFuncs[i]) + { + if (MMFILE_TAGS_SUCCESS == _MMFileFindTags (privateData, gFindPreprendTagFuncs[i], 0)) + { + return MMFILE_TAGS_SUCCESS; + } + } + } + + return MMFILE_TAGS_FAIL; +} + +int _MMFileFindAppendTags (tMMFileTagsData *privateData) +{ + int i = 0; + int res = 0; + + if (!privateData || !privateData->fp) + { + debug_error ("Invalid argument\n"); + return MMFILE_TAGS_FAIL; + } + + for (i = 0; i < MMFILE_TAGS_NUMBER_OF_ELEMENTS(gFindAppendTagFuncs); i++) + { + if (gFindAppendTagFuncs[i]) + { + if (MMFILE_TAGS_SUCCESS == _MMFileFindTags (privateData, gFindAppendTagFuncs[i], 1)) + { + return MMFILE_TAGS_SUCCESS; + } + } + } + + return MMFILE_TAGS_FAIL; +} + +int _MMFileFindTags (tMMFileTagsData *privateData, MMFileFindTagFunc FindTag, unsigned char bAppended) +{ + tMMFileTags *tagData = NULL; + int ret = 0; + + if (!privateData || !privateData->fp) + { + debug_error ("Invalid argument\n"); + return MMFILE_TAGS_FAIL; + } + + tagData = mmfile_malloc (sizeof(tMMFileTags)); + if (!tagData) + { + debug_error ("mmfile_malloc tagData\n"); + return MMFILE_TAGS_FAIL; + } + + ret = FindTag (privateData->fp, bAppended, privateData->startOffset, privateData->endOffset, tagData); + if (ret) + { + if (bAppended) + { + privateData->endOffset = tagData->startOffset; + } + else + { + privateData->startOffset = tagData->endOffset; + } + + /* add tagData into privateData->tagList */ + + //privateData->tagList = mmfile_list_append (privateData->tagList, tagData); + + ret = MMFILE_TAGS_SUCCESS; + } + + mmfile_free(tagData); + + return ret; +} + diff --git a/formats/ffmpeg/mm_file_format_wav.c b/formats/ffmpeg/mm_file_format_wav.c new file mode 100755 index 0000000..38ff860 --- /dev/null +++ b/formats/ffmpeg/mm_file_format_wav.c @@ -0,0 +1,505 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string.h> +#include <stdlib.h> /*malloc*/ + +#include <mm_error.h> +#include "mm_debug.h" + +#include "mm_file_utils.h" +#include "mm_file_format_private.h" +#include "mm_file_format_wav.h" + + +/** + * Wave File Header. + * + * Offset Size Endian Description Value + * 0 4 big Chunk ID "RIFF" (0x52494646) + * 4 4 little Chunk Data Size (file size) - 8 + * 8 4 big Format "WAVE" (0x57415645) + * 12 4 big Sub Chunk1 ID "fmt " (0x666d7420) + * 16 4 little Sub Chunk1 Size + * 20 2 little Audio Format + * 22 2 little Channel number + * 24 4 little Sampling rate + * 28 4 little Byte rate + * 32 2 little Block align + * 34 2 little Bit per sample + */ + + +#define MMF_FILE_WAVE_CHUNK_LEN 12 +#define MMF_FILE_WAVE_SUBCHUNK_LEN 24 +#define MMF_FILE_WAVE_HEADER_LEN (MMF_FILE_WAVE_CHUNK_LEN + MMF_FILE_WAVE_SUBCHUNK_LEN) + +#ifdef __MMFILE_TEST_MODE__ +typedef struct { + short codec; /**< WAVE form Registration Number*/ + char *name; /**< WAVE form wFormatTag ID*/ +} MMF_FILE_WAVE_CODEC_NAME; + +MMF_FILE_WAVE_CODEC_NAME g_audio_cdc_tbl [] = { + { 0x0000, "WAVE_FORMAT_UNKNOWN" }, + { 0x0001, "WAVE_FORMAT_PCM" }, + { 0x0002, "WAVE_FORMAT_ADPCM" }, + { 0x0003, "WAVE_FORMAT_IEEE_FLOAT" }, + { 0x0004, "WAVE_FORMAT_VSELP" }, + { 0x0005, "WAVE_FORMAT_IBM_CVSD" }, + { 0x0006, "WAVE_FORMAT_ALAW" }, + { 0x0007, "WAVE_FORMAT_MULAW" }, + { 0x0010, "WAVE_FORMAT_OKI_ADPCM" }, + { 0x0011, "WAVE_FORMAT_DVI_ADPCM" }, + { 0x0012, "WAVE_FORMAT_MEDIASPACE_ADPCM" }, + { 0x0013, "WAVE_FORMAT_SIERRA_ADPCM" }, + { 0x0014, "WAVE_FORMAT_G723_ADPCM" }, + { 0x0015, "WAVE_FORMAT_DIGISTD" }, + { 0x0016, "WAVE_FORMAT_DIGIFIX" }, + { 0x0017, "WAVE_FORMAT_DIALOGIC_OKI_ADPCM" }, + { 0x0018, "WAVE_FORMAT_MEDIAVISION_ADPCM" }, + { 0x0019, "WAVE_FORMAT_CU_CODEC" }, + { 0x0020, "WAVE_FORMAT_YAMAHA_ADPCM" }, + { 0x0021, "WAVE_FORMAT_SONARC" }, + { 0x0022, "WAVE_FORMAT_DSPGROUP_TRUESPEECH" }, + { 0x0023, "WAVE_FORMAT_ECHOSC1" }, + { 0x0024, "WAVE_FORMAT_AUDIOFILE_AF36" }, + { 0x0025, "WAVE_FORMAT_APTX" }, + { 0x0026, "WAVE_FORMAT_AUDIOFILE_AF10" }, + { 0x0027, "WAVE_FORMAT_PROSODY_1612" }, + { 0x0028, "WAVE_FORMAT_LRC" }, + { 0x0030, "WAVE_FORMAT_DOLBY_AC2" }, + { 0x0031, "WAVE_FORMAT_GSM610" }, + { 0x0032, "WAVE_FORMAT_MSNAUDIO" }, + { 0x0033, "WAVE_FORMAT_ANTEX_ADPCME" }, + { 0x0034, "WAVE_FORMAT_CONTROL_RES_VQLPC" }, + { 0x0035, "WAVE_FORMAT_DIGIREAL" }, + { 0x0036, "WAVE_FORMAT_DIGIADPCM" }, + { 0x0037, "WAVE_FORMAT_CONTROL_RES_CR10" }, + { 0x0038, "WAVE_FORMAT_NMS_VBXADPCM" }, + { 0x0039, "WAVE_FORMAT_ROLAND_RDAC" }, + { 0x003A, "WAVE_FORMAT_ECHOSC3" }, + { 0x003B, "WAVE_FORMAT_ROCKWELL_ADPCM" }, + { 0x003C, "WAVE_FORMAT_ROCKWELL_DIGITALK" }, + { 0x003D, "WAVE_FORMAT_XEBEC" }, + { 0x0040, "WAVE_FORMAT_G721_ADPCM" }, + { 0x0041, "WAVE_FORMAT_G728_CELP" }, + { 0x0042, "WAVE_FORMAT_MSG723" }, + { 0x0050, "WAVE_FORMAT_MPEG" }, + { 0x0052, "WAVE_FORMAT_RT24" }, + { 0x0053, "WAVE_FORMAT_PAC" }, + { 0x0055, "WAVE_FORMAT_MPEGLAYER3" }, + { 0x0059, "WAVE_FORMAT_LUCENT_G723" }, + { 0x0060, "WAVE_FORMAT_CIRRUS" }, + { 0x0061, "WAVE_FORMAT_ESPCM" }, + { 0x0062, "WAVE_FORMAT_VOXWARE" }, + { 0x0063, "WAVE_FORMAT_CANOPUS_ATRAC" }, + { 0x0064, "WAVE_FORMAT_G726_ADPCM" }, + { 0x0065, "WAVE_FORMAT_G722_ADPCM" }, + { 0x0066, "WAVE_FORMAT_DSAT" }, + { 0x0067, "WAVE_FORMAT_DSAT_DISPLAY" }, + { 0x0069, "WAVE_FORMAT_VOXWARE_BYTE_ALIGNED" }, + { 0x0070, "WAVE_FORMAT_VOXWARE_AC8" }, + { 0x0071, "WAVE_FORMAT_VOXWARE_AC10" }, + { 0x0072, "WAVE_FORMAT_VOXWARE_AC16" }, + { 0x0073, "WAVE_FORMAT_VOXWARE_AC20" }, + { 0x0074, "WAVE_FORMAT_VOXWARE_RT24" }, + { 0x0075, "WAVE_FORMAT_VOXWARE_RT29" }, + { 0x0076, "WAVE_FORMAT_VOXWARE_RT29HW" }, + { 0x0077, "WAVE_FORMAT_VOXWARE_VR12" }, + { 0x0078, "WAVE_FORMAT_VOXWARE_VR18" }, + { 0x0079, "WAVE_FORMAT_VOXWARE_TQ40" }, + { 0x0080, "WAVE_FORMAT_SOFTSOUND" }, + { 0x0081, "WAVE_FORMAT_VOXWARE_TQ60" }, + { 0x0082, "WAVE_FORMAT_MSRT24" }, + { 0x0083, "WAVE_FORMAT_G729A" }, + { 0x0084, "WAVE_FORMAT_MVI_MV12" }, + { 0x0085, "WAVE_FORMAT_DF_G726" }, + { 0x0086, "WAVE_FORMAT_DF_GSM610" }, + { 0x0088, "WAVE_FORMAT_ISIAUDIO" }, + { 0x0089, "WAVE_FORMAT_ONLIVE" }, + { 0x0091, "WAVE_FORMAT_SBC24" }, + { 0x0092, "WAVE_FORMAT_DOLBY_AC3_SPDIF" }, + { 0x0097, "WAVE_FORMAT_ZYXEL_ADPCM" }, + { 0x0098, "WAVE_FORMAT_PHILIPS_LPCBB" }, + { 0x0099, "WAVE_FORMAT_PACKED" }, + { 0x0100, "WAVE_FORMAT_RHETOREX_ADPCM" }, + { 0x0101, "WAVE_FORMAT_IRAT" }, + { 0x0111, "WAVE_FORMAT_VIVO_G723" }, + { 0x0112, "WAVE_FORMAT_VIVO_SIREN" }, + { 0x0123, "WAVE_FORMAT_DIGITAL_G723" }, + { 0x0200, "WAVE_FORMAT_CREATIVE_ADPCM" }, + { 0x0202, "WAVE_FORMAT_CREATIVE_FASTSPEECH8" }, + { 0x0203, "WAVE_FORMAT_CREATIVE_FASTSPEECH10" }, + { 0x0220, "WAVE_FORMAT_QUARTERDECK" }, + { 0x0300, "WAVE_FORMAT_FM_TOWNS_SND" }, + { 0x0400, "WAVE_FORMAT_BTV_DIGITAL" }, + { 0x0680, "WAVE_FORMAT_VME_VMPCM" }, + { 0x1000, "WAVE_FORMAT_OLIGSM" }, + { 0x1001, "WAVE_FORMAT_OLIADPCM" }, + { 0x1002, "WAVE_FORMAT_OLICELP" }, + { 0x1003, "WAVE_FORMAT_OLISBC" }, + { 0x1004, "WAVE_FORMAT_OLIOPR" }, + { 0x1100, "WAVE_FORMAT_LH_CODEC" }, + { 0x1400, "WAVE_FORMAT_NORRIS" }, + { 0x1401, "WAVE_FORMAT_ISIAUDIO" }, + { 0x1500, "WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS" }, + { 0x2000, "WAVE_FORMAT_DVM" }, +}; +#endif + +typedef struct { + int size; /**< Chunk size*/ + short format; /**< Wave form Resistration Number*/ + short channel; /**< Number of channels*/ + int sample_rate; /**< Sampling-rate per second*/ + int byte_rate; /**< Byte per second (== Sampling-rate * Channels * Bit per Sample / 8)*/ + short block_align; /**< Block align(== Channels * Bit per Sample / 8)*/ + short bits_per_sample; /**< Bit per sample*/ + +} MM_FILE_WAVE_INFO; + + +/* internal */ +static unsigned char *mmf_file_wave_get_header (char *src); +static int mmf_file_wave_get_info (unsigned char *header, MM_FILE_WAVE_INFO *info); + + +/* mm plugin porting */ +int mmfile_format_read_stream_wav (MMFileFormatContext *formatContext); +int mmfile_format_read_frame_wav (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame); +int mmfile_format_read_tag_wav (MMFileFormatContext *formatContext); +int mmfile_format_close_wav (MMFileFormatContext *formatContext); + + +EXPORT_API +int mmfile_format_open_wav (MMFileFormatContext *formatContext) +{ + int ret = 0; + + if (NULL == formatContext) { + debug_error("formatContext is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + if (formatContext->pre_checked == 0) { + ret = MMFileFormatIsValidWAV (formatContext->uriFileName); + if ( ret == 0 ) + { + debug_error("It is not wav file\n"); + return MMFILE_FORMAT_FAIL; + } + } + + formatContext->ReadStream = mmfile_format_read_stream_wav; + formatContext->ReadFrame = mmfile_format_read_frame_wav; + formatContext->ReadTag = mmfile_format_read_tag_wav; + formatContext->Close = mmfile_format_close_wav; + + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = 1; + + formatContext->privateFormatData = NULL; + + return MMFILE_FORMAT_SUCCESS; +} + + +EXPORT_API +int mmfile_format_read_stream_wav (MMFileFormatContext *formatContext) +{ + unsigned char *header = NULL; + MM_FILE_WAVE_INFO *waveinfo = NULL; + int ret = 0; + + if (formatContext == NULL) { + debug_error("formatContext is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + header = mmf_file_wave_get_header (formatContext->uriFileName); + if (header == NULL) { + debug_error("error: mmf_file_wave_get_header\n"); + goto exception; + } + + waveinfo = mmfile_malloc (sizeof(MM_FILE_WAVE_INFO)); + if (waveinfo == NULL) { + debug_error("error: mmfile_malloc\n"); + goto exception; + } + + ret = mmf_file_wave_get_info (header, waveinfo); + if (ret == -1) { + debug_error("error: mmf_file_wave_get_info\n"); + goto exception; + } + + mmfile_free (header); + + formatContext->privateFormatData = waveinfo; + + formatContext->duration = (int)(((float)(waveinfo->size) / (float)(waveinfo->byte_rate)) * 1000.0F); + formatContext->audioTotalTrackNum = 1; + formatContext->nbStreams = 1; + formatContext->streams[MMFILE_AUDIO_STREAM] = mmfile_malloc (sizeof(MMFileFormatStream)); + + if (!formatContext->streams[MMFILE_AUDIO_STREAM]) { + debug_error("error: mmfile_malloc audio stream for wav\n"); + return MMFILE_FORMAT_FAIL; + } + + formatContext->streams[MMFILE_AUDIO_STREAM]->streamType = MMFILE_AUDIO_STREAM; + + switch (waveinfo->format) { + case 0x0001: + formatContext->streams[MMFILE_AUDIO_STREAM]->codecId = MM_AUDIO_CODEC_PCM; + break; + case 0x0002: + formatContext->streams[MMFILE_AUDIO_STREAM]->codecId = MM_AUDIO_CODEC_MS_ADPCM; + break; + case 0x0006: + formatContext->streams[MMFILE_AUDIO_STREAM]->codecId = MM_AUDIO_CODEC_ALAW; + break; + case 0x0007: + formatContext->streams[MMFILE_AUDIO_STREAM]->codecId = MM_AUDIO_CODEC_MULAW; + break; + case 0x0011: + formatContext->streams[MMFILE_AUDIO_STREAM]->codecId = MM_AUDIO_CODEC_ADPCM; + break; + default: + formatContext->streams[MMFILE_AUDIO_STREAM]->codecId = MM_AUDIO_CODEC_INVALID; + break; + } + + formatContext->streams[MMFILE_AUDIO_STREAM]->bitRate = waveinfo->byte_rate*8; + formatContext->streams[MMFILE_AUDIO_STREAM]->nbChannel = waveinfo->channel; + formatContext->streams[MMFILE_AUDIO_STREAM]->framePerSec = 0; + formatContext->streams[MMFILE_AUDIO_STREAM]->samplePerSec = waveinfo->sample_rate; + + return MMFILE_FORMAT_SUCCESS; + +exception: + mmfile_free (header); + mmfile_free (waveinfo); + + return MMFILE_FORMAT_FAIL; +} + + +EXPORT_API +int mmfile_format_read_frame_wav (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame) +{ + return MMFILE_FORMAT_SUCCESS; +} + + +EXPORT_API +int mmfile_format_read_tag_wav (MMFileFormatContext *formatContext) +{ + return MMFILE_FORMAT_SUCCESS; +} + + +EXPORT_API +int mmfile_format_close_wav (MMFileFormatContext *formatContext) +{ + if (formatContext == NULL) { + debug_error("formatContext is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + if (formatContext->privateFormatData) + mmfile_free (formatContext->privateFormatData); + + return MMFILE_FORMAT_SUCCESS; +} + +static char* +_dump_codec_name (short codec) +{ +#ifdef __MMFILE_TEST_MODE__ + int sz = sizeof (g_audio_cdc_tbl) / sizeof (MMF_FILE_WAVE_CODEC_NAME); + int i; + + for (i = 0; i < sz; i++) { + if (g_audio_cdc_tbl[i].codec == codec) { + return g_audio_cdc_tbl[i].name; + } + } +#endif + return NULL; +} + +static int _get_fmt_subchunk_offset (MMFileIOHandle *fp, long long limit, long long *offset) +{ + long long fmt_offset; + int readed; + int i; + unsigned char buf[4]; + + fmt_offset = mmfile_tell (fp); + if (fmt_offset < 0) + return 0; + + for (i = 0; i < limit; i++) { + mmfile_seek (fp, fmt_offset + i, MMFILE_SEEK_SET); + readed = mmfile_read (fp, buf, 4); + if (readed != 4) { + debug_error ( "failed to read. size = %d\n", readed); + return 0; + } + + if (buf[0] == 'f' && buf[1] == 'm' && buf[2] == 't' && buf[3] == ' ') { + *offset = fmt_offset + i; + return 1; + } + } + + return 0; +} + +static unsigned char * +mmf_file_wave_get_header (char *src) +{ + int readed = 0; + MMFileIOHandle *fp = NULL; + int ret = 0; + long long src_size = 0L; + unsigned char *header = NULL; + long long offset = 0; + long long limit; + + header = mmfile_malloc (MMF_FILE_WAVE_HEADER_LEN); + if (!header) + return NULL; + + /*open*/ + ret = mmfile_open (&fp, src, MMFILE_RDONLY); + if (ret == MMFILE_UTIL_FAIL) { + debug_error ( "open failed.\n"); + goto failed; + } + + + /*get file size*/ + mmfile_seek (fp, 0L, MMFILE_SEEK_END); + src_size = mmfile_tell (fp); + mmfile_seek (fp, 0L, MMFILE_SEEK_SET); + + if (src_size < MMF_FILE_WAVE_HEADER_LEN) { + debug_error ( "header is too small.\n"); + goto failed; + } + + /*read chunk data*/ + readed = mmfile_read (fp, header, MMF_FILE_WAVE_CHUNK_LEN); + if (readed != MMF_FILE_WAVE_CHUNK_LEN) { + debug_error ( "read error. size = %d\n", readed); + goto failed; + } + + /*seach 'fmt ' sub chunk*/ + limit = (src_size - MMF_FILE_WAVE_HEADER_LEN > 10240 ? 10240 : src_size - MMF_FILE_WAVE_HEADER_LEN); + ret = _get_fmt_subchunk_offset (fp, limit, &offset); + if (ret == 0) { + debug_error ( "failed to seach 'fmt ' chunk\n"); + goto failed; + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("fmt offset: %lld\n", offset); + #endif + + mmfile_seek (fp, offset, MMFILE_SEEK_SET); + + /*read sub chunk data*/ + readed = mmfile_read (fp, header + MMF_FILE_WAVE_CHUNK_LEN, MMF_FILE_WAVE_SUBCHUNK_LEN); + if (readed != MMF_FILE_WAVE_SUBCHUNK_LEN) { + debug_error ( "read error. size = %d\n", readed); + goto failed; + } + + mmfile_close (fp); + + return header; + +failed: + if (header) mmfile_free (header); + if (fp) mmfile_close (fp); + + return NULL; +} + +static int +mmf_file_wave_get_info (unsigned char *header, MM_FILE_WAVE_INFO *info) +{ + if (!header || !info) { + return -1; + } + + /*get chunk size*/ + info->size = *((int*)(header + 4)); + + /*get format*/ + info->format = *((short*)(header + 20)); + + /*get channel*/ + info->channel = *((short*)(header + 22)); + + /*get sampling-rate*/ + info->sample_rate = *((int*)(header + 24)); + + /*get byte rate*/ + info->byte_rate = *((int*)(header + 28)); + + /*get byte align*/ + info->block_align = *((short*)(header + 32)); + + /*get bits per sample*/ + info->bits_per_sample = *((short*)(header + 34)); + + info->size = mmfile_io_le_int32 (info->size); + info->format = mmfile_io_le_int16 (info->format); + info->channel = mmfile_io_le_int16 (info->channel); + info->sample_rate = mmfile_io_le_int32 (info->sample_rate); + info->byte_rate = mmfile_io_le_int32 (info->byte_rate); + info->block_align = mmfile_io_le_int16 (info->block_align); + info->bits_per_sample = mmfile_io_le_int16 (info->bits_per_sample); + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ( "----------------------------------------------\n"); + debug_msg ( "chunk size: %d\n", info->size); + debug_msg ( "WAVE form Registration Number: 0x%04X\n", info->format); + debug_msg ( "WAVE form wFormatTag ID: %s\n", _dump_codec_name (info->format)); + debug_msg ( "channel: %d\n", info->channel); + debug_msg ( "sampling-rate: %d\n", info->sample_rate); + debug_msg ( "byte-rate: %d\n", info->byte_rate); + debug_msg ( "byte align: %d\n", info->block_align); + debug_msg ( "bit per sample: %d\n", info->bits_per_sample); + debug_msg ( "----------------------------------------------\n"); + #endif + + return 0; + +} + + diff --git a/formats/ffmpeg/mm_file_formats.c b/formats/ffmpeg/mm_file_formats.c new file mode 100755 index 0000000..bae06fe --- /dev/null +++ b/formats/ffmpeg/mm_file_formats.c @@ -0,0 +1,1049 @@ +/* + * libmm-fileinfo + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Haejeong Kim <backto.kim@samsung.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#ifdef DRM_SUPPORT +#include <drm_client.h> +#endif + + +#include "mm_debug.h" +#include "mm_file_format_private.h" +#include "mm_file_utils.h" + +#define _MMF_FILE_FILEEXT_MAX 128 + +int (*MMFileOpenFunc[MM_FILE_FORMAT_NUM+1]) (MMFileFormatContext *fileContext) = { + mmfile_format_open_ffmpg, /* 3GP */ + mmfile_format_open_ffmpg, /* ASF */ + mmfile_format_open_ffmpg, /* AVI */ + mmfile_format_open_ffmpg, /* MATROSAK */ + mmfile_format_open_ffmpg, /* MP4 */ + mmfile_format_open_ffmpg, /* OGG */ + NULL, /* NUT */ + mmfile_format_open_ffmpg, /* QT */ + NULL, /* REAL */ + mmfile_format_open_amr, /* AMR */ + mmfile_format_open_aac, /* AAC */ + mmfile_format_open_mp3, /* MP3 */ + NULL, /* AIFF */ + NULL, /* AU */ + mmfile_format_open_wav, /* WAV */ + mmfile_format_open_mid, /* MID */ + mmfile_format_open_mmf, /* MMF */ + mmfile_format_open_ffmpg, /* DIVX */ + mmfile_format_open_ffmpg, /* FLV */ + NULL, /* VOB */ + mmfile_format_open_imy, /* IMY */ + mmfile_format_open_ffmpg, /* WMA */ + mmfile_format_open_ffmpg, /* WMV */ + NULL, /* JPG */ + mmfile_format_open_ffmpg, /* FLAC */ + NULL, +}; + +static int _CleanupFrameContext (MMFileFormatContext *formatContext) +{ + if (formatContext) { + formatContext->ReadFrame = NULL; + formatContext->ReadStream = NULL; + formatContext->ReadTag = NULL; + formatContext->Close = NULL; + + if (formatContext->title) mmfile_free(formatContext->title); + if (formatContext->artist) mmfile_free(formatContext->artist); + if (formatContext->author) mmfile_free(formatContext->author); + if (formatContext->composer) mmfile_free(formatContext->composer); + if (formatContext->album) mmfile_free(formatContext->album); + if (formatContext->year) mmfile_free(formatContext->year); + if (formatContext->copyright) mmfile_free(formatContext->copyright); + if (formatContext->comment) mmfile_free(formatContext->comment); + if (formatContext->genre) mmfile_free(formatContext->genre); + if (formatContext->artwork) mmfile_free(formatContext->artwork); + if (formatContext->classification) mmfile_free(formatContext->classification); + if (formatContext->conductor) mmfile_free(formatContext->conductor); + + if (formatContext->privateFormatData) mmfile_free(formatContext->privateFormatData); + if (formatContext->privateCodecData) mmfile_free(formatContext->privateCodecData); + + + if (formatContext->nbStreams > 0) { + int i = 0; + for (i = 0; (i < formatContext->nbStreams) && (i < MAXSTREAMS); i++) { + if (formatContext->streams[i]) mmfile_free(formatContext->streams[i]); + } + } + + if (formatContext->thumbNail) { + if (formatContext->thumbNail->frameData) + mmfile_free (formatContext->thumbNail->frameData); + + if (formatContext->thumbNail->configData) + mmfile_free (formatContext->thumbNail->configData); + + mmfile_free (formatContext->thumbNail); + } + + formatContext->title = NULL; + formatContext->artist = NULL; + formatContext->author = NULL; + formatContext->composer = NULL; + formatContext->album = NULL; + formatContext->copyright = NULL; + formatContext->comment = NULL; + formatContext->genre = NULL; + formatContext->artwork = NULL; + + formatContext->privateFormatData = NULL; + formatContext->privateCodecData = NULL; + formatContext->classification = NULL; + + formatContext->videoTotalTrackNum = 0; + formatContext->audioTotalTrackNum = 0; + + formatContext->nbStreams = 0; + formatContext->streams[MMFILE_AUDIO_STREAM] = NULL; + formatContext->streams[MMFILE_VIDEO_STREAM] = NULL; + formatContext->thumbNail = NULL; + } + return MMFILE_FORMAT_SUCCESS; +} + +static int +_PreprocessFile (MMFileSourceType *fileSrc, char **urifilename, int *formatEnum, int *isdrm) +{ + const char *fileName = NULL; + char extansion_name[_MMF_FILE_FILEEXT_MAX]; + int pos = 0; + int filename_len = 0; + int index = 0, skip_index = 0; + + if (fileSrc->type == MM_FILE_SRC_TYPE_FILE) { + fileName = (const char *)(fileSrc->file.path); + filename_len = strlen (fileName); + pos = filename_len; + + /** + * Get file extension from file's name + */ + while (pos > 0) { + pos--; + if (fileName[pos] == '.') + break; + } + + memset (extansion_name, 0x00, _MMF_FILE_FILEEXT_MAX); + + /*extract metadata for all file. ex)a , a. , a.mp3*/ + if (pos == 0) { + /*even though there is no file extension, extracto metadata*/ + debug_msg ("no file extension"); + } + else if (_MMF_FILE_FILEEXT_MAX > (filename_len - pos - 1)) { + strncpy (extansion_name, fileName + pos +1 , (filename_len - pos - 1)); + extansion_name[filename_len - pos - 1] = '\0'; + } else { + debug_error ("invalid filename. destination length: %d, source length: %d.\n", _MMF_FILE_FILEEXT_MAX, (filename_len - pos - 1)); + return MMFILE_FORMAT_FAIL; /*invalid file name*/ + } + +#ifdef DRM_SUPPORT + /** + * Make URI name with file name + */ + drm_bool_type_e res = DRM_TRUE; + drm_file_type_e file_type = DRM_TYPE_UNDEFINED; + int ret = 0; + bool is_drm = FALSE; + + ret = drm_is_drm_file (fileSrc->file.path, &res); + if (ret == DRM_RETURN_SUCCESS && DRM_TRUE == res) + { + ret = drm_get_file_type(fileSrc->file.path, &file_type); + if((ret == DRM_RETURN_SUCCESS) && ((file_type == DRM_TYPE_OMA_V1) ||(file_type == DRM_TYPE_OMA_V2))) + { + is_drm = TRUE; + } + } + + if (is_drm) + { + *isdrm = MM_FILE_DRM_OMA; + debug_error ("OMA DRM detected. Not Support DRM Content\n"); + return MMFILE_FORMAT_FAIL; /*Not Support DRM Content*/ + } + else +#endif // DRM_SUPPORT + { + *isdrm = MM_FILE_DRM_NONE; +#ifdef __MMFILE_MMAP_MODE__ + *urifilename = mmfile_malloc (MMFILE_MMAP_URI_LEN + filename_len + 1); + if (!*urifilename) { + debug_error ("error: mmfile_malloc uriname\n"); + return MMFILE_FORMAT_FAIL; + } + + memset (*urifilename, 0x00, MMFILE_MMAP_URI_LEN + filename_len + 1); + strncpy (*urifilename, MMFILE_MMAP_URI, MMFILE_MMAP_URI_LEN); + strncat (*urifilename, fileName, filename_len); + (*urifilename)[MMFILE_MMAP_URI_LEN + filename_len] = '\0'; + +#else + *urifilename = mmfile_malloc (MMFILE_FILE_URI_LEN + filename_len + 1); + if (!*urifilename) { + debug_error ("error: mmfile_malloc uriname\n"); + return MMFILE_FORMAT_FAIL; + } + + memset (*urifilename, 0x00, MMFILE_FILE_URI_LEN + filename_len + 1); + strncpy (*urifilename, MMFILE_FILE_URI, MMFILE_FILE_URI_LEN+1); + strncat (*urifilename, fileName, filename_len); + (*urifilename)[MMFILE_FILE_URI_LEN + filename_len] = '\0'; +#endif + } + + /////////////////////////////////////////////////////////////////////// + // Check File format // + /////////////////////////////////////////////////////////////////////// + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("Get codec type of [%s].\n", extansion_name); + #endif + + if (strcasecmp (extansion_name, "mp4") == 0 || + strcasecmp (extansion_name, "mpeg4") == 0 || + strcasecmp (extansion_name, "m4a") == 0 || + strcasecmp (extansion_name, "mpg") == 0 || + strcasecmp (extansion_name, "mpg4") == 0 || + strcasecmp (extansion_name, "m4v") == 0 ) { + + if (MMFileFormatIsValidMP4 (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MP4; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_MP4; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "3gp") == 0) { + if (MMFileFormatIsValidMP4 (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_3GP; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_3GP; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "amr") == 0 || + strcasecmp (extansion_name, "awb") == 0) { + + if (MMFileFormatIsValidAMR (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_AMR; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_AMR; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "wav") == 0) { + if (MMFileFormatIsValidWAV (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_WAV; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_WAV; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "mid") == 0 || + strcasecmp (extansion_name, "midi") == 0 || + strcasecmp (extansion_name, "spm") == 0 ) { + + if (MMFileFormatIsValidMID (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MID; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_MID; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "mp3") == 0) { + if (MMFileFormatIsValidMP3 (*urifilename,5)) { + *formatEnum = MM_FILE_FORMAT_MP3; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_MP3; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "aac") == 0) { + if (MMFileFormatIsValidAAC (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_AAC; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_AAC; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "xmf") == 0 || + strcasecmp (extansion_name, "mxmf") == 0) { + if (MMFileFormatIsValidMID (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MID; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_MID; + goto PROBE_PROPER_FILE_TYPE; + + } else if (!strcasecmp (extansion_name, "mmf") || + !strcasecmp (extansion_name, "ma2")) { + if (MMFileFormatIsValidMMF (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MMF; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_MMF; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "imy") == 0) { + if (MMFileFormatIsValidIMY (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_IMELODY; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_IMELODY; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "avi") == 0) { + if (MMFileFormatIsValidAVI (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_AVI; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_AVI; + goto PROBE_PROPER_FILE_TYPE; + } else if (strcasecmp (extansion_name, "divx") == 0) { + if (MMFileFormatIsValidAVI (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_DIVX; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_DIVX; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "asf") == 0 || + strcasecmp (extansion_name, "asx") == 0 ) { + if (MMFileFormatIsValidASF (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_ASF; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_ASF; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "wma") == 0) { + if (MMFileFormatIsValidWMA (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_WMA; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_WMA; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "wmv") == 0) { + if (MMFileFormatIsValidWMV (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_WMV; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_WMV; + goto PROBE_PROPER_FILE_TYPE; + + } else if (strcasecmp (extansion_name, "ogg") == 0) { + if (MMFileFormatIsValidOGG (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_OGG; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_OGG; + goto PROBE_PROPER_FILE_TYPE; + } else if (strcasecmp (extansion_name, "mkv") == 0 || + strcasecmp (extansion_name, "mka") == 0) { + if (MMFileFormatIsValidMatroska (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MATROSKA; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_MATROSKA; + goto PROBE_PROPER_FILE_TYPE; + } else if (strcasecmp (extansion_name, "mov") == 0) { + if (MMFileFormatIsValidMP4 (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_QT; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_QT; + goto PROBE_PROPER_FILE_TYPE; + } else if (strcasecmp (extansion_name, "flac") == 0) { + if (MMFileFormatIsValidFLAC (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_FLAC; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_FLAC; + goto PROBE_PROPER_FILE_TYPE; + } else if (strcasecmp (extansion_name, "flv") == 0) { + if (MMFileFormatIsValidFLV (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_FLV; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_FLV; + goto PROBE_PROPER_FILE_TYPE; + } else { + debug_warning ("probe file type=%s\n", fileName); + skip_index = -1; + goto PROBE_PROPER_FILE_TYPE; + } + } else if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) { + char tempURIBuffer[MMFILE_URI_MAX_LEN] = {0,}; + + sprintf (tempURIBuffer, "%s%u:%u", MMFILE_MEM_URI, (unsigned int)fileSrc->memory.ptr, fileSrc->memory.size); + *urifilename = mmfile_strdup (tempURIBuffer); + if (!*urifilename) { + debug_error ("error: uri is NULL\n"); + return MMFILE_FORMAT_FAIL; + } + + #ifdef __MMFILE_TEST_MODE__ + debug_msg ("uri: %s\n", *urifilename); + #endif + + switch (fileSrc->memory.format) { + case MM_FILE_FORMAT_3GP: { + if (MMFileFormatIsValidMP4 (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_3GP; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_3GP; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_MP4: { + if (MMFileFormatIsValidMP4 (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MP4; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_MP4; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_AMR: { + if (MMFileFormatIsValidAMR (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_AMR; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_AMR; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_WAV: { + if (MMFileFormatIsValidWAV (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_WAV; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_WAV; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_MID: { + if (MMFileFormatIsValidMID (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MID; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_MID; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_MP3: { + if (MMFileFormatIsValidMP3 (*urifilename,5)) { + *formatEnum = MM_FILE_FORMAT_MP3; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_MP3; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_AAC: { + if (MMFileFormatIsValidAAC (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_AAC; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_AAC; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_MMF: { + if (MMFileFormatIsValidMMF (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MMF; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_MMF; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_IMELODY: { + if (MMFileFormatIsValidIMY (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_IMELODY; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_IMELODY; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_AVI: { + if (MMFileFormatIsValidAVI (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_AVI; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_AVI; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_DIVX: { + if (MMFileFormatIsValidAVI (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_DIVX; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_DIVX; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_ASF: { + if (MMFileFormatIsValidASF (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_ASF; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_ASF; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_WMA: { + if (MMFileFormatIsValidWMA (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_WMA; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_WMA; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_WMV: { + if (MMFileFormatIsValidWMV (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_WMV; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_WMV; + goto PROBE_PROPER_FILE_TYPE; + break; + } + + case MM_FILE_FORMAT_OGG: { + if (MMFileFormatIsValidOGG (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_OGG; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_OGG; + goto PROBE_PROPER_FILE_TYPE; + } + + case MM_FILE_FORMAT_MATROSKA: { + if (MMFileFormatIsValidMatroska (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MATROSKA; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_MATROSKA; + goto PROBE_PROPER_FILE_TYPE; + } + + case MM_FILE_FORMAT_QT: { + if (MMFileFormatIsValidMP4 (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_QT; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_QT; + goto PROBE_PROPER_FILE_TYPE; + } + + case MM_FILE_FORMAT_FLAC: { + if (MMFileFormatIsValidFLAC (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_FLAC; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_FLAC; + goto PROBE_PROPER_FILE_TYPE; + } + + case MM_FILE_FORMAT_FLV: { + if (MMFileFormatIsValidFLV (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_FLV; + return MMFILE_FORMAT_SUCCESS; + } + skip_index = MM_FILE_FORMAT_FLV; + goto PROBE_PROPER_FILE_TYPE; + } + + default: { + debug_warning ("probe fileformat type=%d (%d: autoscan)\n", fileSrc->memory.format, MM_FILE_FORMAT_INVALID); + skip_index = -1; + goto PROBE_PROPER_FILE_TYPE; + break; + } + } + } else { + debug_error ("error: invaild input type[memory|file]\n"); + return MMFILE_FORMAT_FAIL; + } + +PROBE_PROPER_FILE_TYPE: + for (index = 0; index < MM_FILE_FORMAT_NUM; index++) { + if (index == skip_index) + continue; + + debug_msg ("search index = [%d]\n", index); + switch (index) { + case MM_FILE_FORMAT_QT: + case MM_FILE_FORMAT_3GP: + case MM_FILE_FORMAT_MP4: { + if (skip_index == MM_FILE_FORMAT_QT || skip_index == MM_FILE_FORMAT_3GP || skip_index == MM_FILE_FORMAT_MP4) + break; + + if (MMFileFormatIsValidMP4 (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_3GP; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_3GP; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_ASF: + case MM_FILE_FORMAT_WMA: + case MM_FILE_FORMAT_WMV: { + if (skip_index == MM_FILE_FORMAT_ASF || skip_index == MM_FILE_FORMAT_WMA || skip_index == MM_FILE_FORMAT_WMV) + break; + + if (MMFileFormatIsValidASF (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_ASF; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_ASF; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_DIVX: + case MM_FILE_FORMAT_AVI: { + if (skip_index == MM_FILE_FORMAT_DIVX || skip_index == MM_FILE_FORMAT_AVI) + break; + + if (MMFileFormatIsValidAVI(*urifilename)) { + *formatEnum = MM_FILE_FORMAT_AVI; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_AVI; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_OGG: { + if (MMFileFormatIsValidOGG (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_OGG; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_OGG; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_AMR: { + if (MMFileFormatIsValidAMR (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_AMR; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_AMR; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_AAC: { + if (MMFileFormatIsValidAAC (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_AAC; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_AAC; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_MP3: { + if (MMFileFormatIsValidMP3 (*urifilename,50)) { + *formatEnum = MM_FILE_FORMAT_MP3; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_MP3; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_WAV: { + if (MMFileFormatIsValidWAV (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_WAV; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_WAV; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_MID: { + if (MMFileFormatIsValidMID (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MID; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_MID; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_MMF: { + if (MMFileFormatIsValidMMF (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MMF; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_MMF; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_IMELODY: { + if (MMFileFormatIsValidIMY (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_IMELODY; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_IMELODY; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_MATROSKA: { + if (MMFileFormatIsValidMatroska (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_MATROSKA; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_MATROSKA; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_FLAC: { + if (MMFileFormatIsValidFLAC (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_FLAC; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_FLAC; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + case MM_FILE_FORMAT_FLV: { + if (MMFileFormatIsValidFLV (*urifilename)) { + *formatEnum = MM_FILE_FORMAT_FLV; + if (fileSrc->type == MM_FILE_SRC_TYPE_MEMORY) fileSrc->memory.format = MM_FILE_FORMAT_FLV; + return MMFILE_FORMAT_SUCCESS; + } + break; + } + + /* not supported file */ + case MM_FILE_FORMAT_NUT: + case MM_FILE_FORMAT_REAL: + case MM_FILE_FORMAT_AIFF: + case MM_FILE_FORMAT_AU: + case MM_FILE_FORMAT_VOB: + case MM_FILE_FORMAT_JPG: + break; + default: { + debug_error ("error: invaild format enum[%d]\n", index); + break; + } + } + } + + if (index == MM_FILE_FORMAT_NUM) + debug_error("Can't probe file type\n"); + + *formatEnum = -1; + return MMFILE_FORMAT_FAIL; + +} + +static int _mmfile_format_close (MMFileFormatContext *formatContext) +{ + if (NULL == formatContext) { + debug_error ("error: invalid params\n"); + return MMFILE_FORMAT_FAIL; + } + + if (formatContext->Close) { + formatContext->Close(formatContext); + formatContext->Close = NULL; + } + + if (formatContext->ReadFrame) formatContext->ReadFrame = NULL; + if (formatContext->ReadStream) formatContext->ReadStream = NULL; + if (formatContext->ReadTag) formatContext->ReadTag = NULL; + if (formatContext->Close) formatContext->Close = NULL; + + if (formatContext->uriFileName) mmfile_free(formatContext->uriFileName); + if (formatContext->title) mmfile_free(formatContext->title); + if (formatContext->artist) mmfile_free(formatContext->artist); + if (formatContext->author) mmfile_free(formatContext->author); + if (formatContext->copyright) mmfile_free(formatContext->copyright); + if (formatContext->comment) mmfile_free(formatContext->comment); + if (formatContext->album) mmfile_free(formatContext->album); + if (formatContext->year) mmfile_free(formatContext->year); + if (formatContext->genre) mmfile_free(formatContext->genre); + if (formatContext->composer) mmfile_free(formatContext->composer); + if (formatContext->classification) mmfile_free(formatContext->classification); + if (formatContext->artwork) mmfile_free(formatContext->artwork); + if (formatContext->conductor) mmfile_free(formatContext->conductor); + if (formatContext->unsyncLyrics) mmfile_free(formatContext->unsyncLyrics); + if (formatContext->syncLyrics) mm_file_free_synclyrics_list(formatContext->syncLyrics); + if (formatContext->artworkMime) mmfile_free(formatContext->artworkMime); + if (formatContext->recDate) mmfile_free(formatContext->recDate); + + if (formatContext->privateFormatData) mmfile_free(formatContext->privateFormatData); + if (formatContext->privateCodecData) mmfile_free(formatContext->privateCodecData); + + if (formatContext->nbStreams > 0) { + int i = 0; + for (i = 0; i < MAXSTREAMS; i++) + if (formatContext->streams[i]) mmfile_free(formatContext->streams[i]); + } + + if (formatContext->thumbNail) { + if (formatContext->thumbNail->frameData) + mmfile_free (formatContext->thumbNail->frameData); + + if (formatContext->thumbNail->configData) + mmfile_free (formatContext->thumbNail->configData); + + mmfile_free (formatContext->thumbNail); + } + + if (formatContext) + mmfile_free (formatContext); + + return MMFILE_FORMAT_SUCCESS; +} + + +EXPORT_API +int mmfile_format_open (MMFileFormatContext **formatContext, MMFileSourceType *fileSrc) +{ + int index = 0; + int ret = 0; + MMFileFormatContext *formatObject = NULL; + + if (NULL == fileSrc) { + debug_error ("error: invalid params\n"); + return MMFILE_FORMAT_FAIL; + } + + /* create formatContext object */ + formatObject = mmfile_malloc (sizeof (MMFileFormatContext)); + if (NULL == formatObject) { + debug_error ("error: mmfile_malloc fail for formatObject\n"); + *formatContext = NULL; + return MMFILE_FORMAT_FAIL; + } + + memset (formatObject, 0x00, sizeof (MMFileFormatContext)); + + mmfile_register_io_all(); + + /* parsing file extension */ + formatObject->filesrc = fileSrc; + + formatObject->pre_checked = 0; /*not yet format checked.*/ + + /** + * Format detect and validation check. + */ + ret = _PreprocessFile (fileSrc, &formatObject->uriFileName, &formatObject->formatType, &formatObject->isdrm); + if (MMFILE_FORMAT_SUCCESS != ret) { + debug_error ("error: _PreprocessFile fail\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + formatObject->pre_checked = 1; /*already file format checked.*/ + + /** + * Open format function. + */ + if (NULL == MMFileOpenFunc[formatObject->formatType]) { + debug_error ("error: Not implemented \n"); + ret = MMFILE_FORMAT_FAIL; + goto find_valid_handler; + } + + ret = MMFileOpenFunc[formatObject->formatType] (formatObject); + if (MMFILE_FORMAT_FAIL == ret) { + debug_error ("error: Try other formats\n"); + ret = MMFILE_FORMAT_FAIL; + goto find_valid_handler; + } + + *formatContext = formatObject; + return MMFILE_FORMAT_SUCCESS; + +find_valid_handler: + formatObject->pre_checked = 0; /*do check file format*/ + + for (index = 0; index < MM_FILE_FORMAT_NUM+1; index++) { + if (NULL == MMFileOpenFunc[index]) { + debug_error ("error: Not implemented \n"); + ret = MMFILE_FORMAT_FAIL; + continue; + } + + if (formatObject->formatType == index) + continue; + + ret = MMFileOpenFunc[index] (formatObject); + if (MMFILE_FORMAT_FAIL == ret) { + _CleanupFrameContext (formatObject); + continue; + } + + break; + } + + formatObject->formatType = index; + + if (index == MM_FILE_FORMAT_NUM + 1 && MMFILE_FORMAT_FAIL == ret) { + debug_error ("can't find file format handler\n"); + ret = MMFILE_FORMAT_FAIL; + goto exception; + } + + formatObject->formatType = index; + *formatContext = formatObject; + + return MMFILE_FORMAT_SUCCESS; + +exception: + _mmfile_format_close (formatObject); + *formatContext = NULL; + + return ret; +} + +EXPORT_API +int mmfile_format_read_stream (MMFileFormatContext *formatContext) +{ + if (NULL == formatContext || NULL == formatContext->ReadStream) { + debug_error ("error: invalid params\n"); + return MMFILE_FORMAT_FAIL; + } + + return formatContext->ReadStream (formatContext); +} + +EXPORT_API +int mmfile_format_read_frame (MMFileFormatContext *formatContext, unsigned int timestamp, MMFileFormatFrame *frame) +{ + if (NULL == formatContext || NULL == frame || NULL == formatContext->ReadFrame ) { + debug_error ("error: invalid params\n"); + return MMFILE_FORMAT_FAIL; + } + + return formatContext->ReadFrame (formatContext, timestamp, frame); +} + +EXPORT_API +int mmfile_format_read_tag (MMFileFormatContext *formatContext) +{ + if (NULL == formatContext || NULL == formatContext->ReadTag) { + debug_error ("error: invalid params\n"); + return MMFILE_FORMAT_FAIL; + } + + return formatContext->ReadTag (formatContext); +} + + +EXPORT_API +int mmfile_format_close (MMFileFormatContext *formatContext) +{ + if (NULL == formatContext) { + debug_error ("error: invalid params\n"); + return MMFILE_FORMAT_FAIL; + } + + if (formatContext->Close) { + formatContext->Close(formatContext); + formatContext->Close = NULL; + } + + if (formatContext->ReadFrame) formatContext->ReadFrame = NULL; + if (formatContext->ReadStream) formatContext->ReadStream = NULL; + if (formatContext->ReadTag) formatContext->ReadTag = NULL; + if (formatContext->Close) formatContext->Close = NULL; + + if (formatContext->uriFileName) mmfile_free(formatContext->uriFileName); + + if (formatContext->title) mmfile_free(formatContext->title); + if (formatContext->artist) mmfile_free(formatContext->artist); + if (formatContext->author) mmfile_free(formatContext->author); + if (formatContext->copyright) mmfile_free(formatContext->copyright); + if (formatContext->comment) mmfile_free(formatContext->comment); + if (formatContext->album) mmfile_free(formatContext->album); + if (formatContext->year) mmfile_free(formatContext->year); + if (formatContext->genre) mmfile_free(formatContext->genre); + if (formatContext->composer) mmfile_free(formatContext->composer); + if (formatContext->classification) mmfile_free(formatContext->classification); + if (formatContext->artwork) mmfile_free(formatContext->artwork); + if (formatContext->artworkMime) mmfile_free(formatContext->artworkMime); + if (formatContext->tagTrackNum) mmfile_free(formatContext->tagTrackNum); + if (formatContext->rating) mmfile_free(formatContext->rating); + if (formatContext->conductor) mmfile_free(formatContext->conductor); + if (formatContext->unsyncLyrics) mmfile_free(formatContext->unsyncLyrics); + if (formatContext->recDate) mmfile_free(formatContext->recDate); + + if (formatContext->privateFormatData) mmfile_free(formatContext->privateFormatData); + if (formatContext->privateCodecData) mmfile_free(formatContext->privateCodecData); + + if (formatContext->nbStreams > 0) { + int i = 0; + for (i = 0; i < MAXSTREAMS; i++) + if (formatContext->streams[i]) mmfile_free(formatContext->streams[i]); + } + + if (formatContext->thumbNail) { + if (formatContext->thumbNail->frameData) + mmfile_free (formatContext->thumbNail->frameData); + + if (formatContext->thumbNail->configData) + mmfile_free (formatContext->thumbNail->configData); + + mmfile_free (formatContext->thumbNail); + } + + if (formatContext) + mmfile_free (formatContext); + + return MMFILE_FORMAT_SUCCESS; +} + |