Commit | Line | Data |
---|---|---|
fc11dd05 | 1 | /* |
2 | * Some mp3 related code for Sega/Mega CD. | |
3 | * Uses Libav/FFmpeg libavcodec | |
4 | * (C) notaz, 2013 | |
5 | * | |
6 | * This work is licensed under the terms of MAME license. | |
7 | * See COPYING file in the top-level directory. | |
8 | */ | |
9 | ||
10 | #include <stdio.h> | |
11 | #include <string.h> | |
12 | #include <libavcodec/avcodec.h> | |
986d60fc | 13 | #include <dlfcn.h> |
fc11dd05 | 14 | |
15 | #include <pico/pico_int.h> | |
16 | #include "../libpicofe/lprintf.h" | |
17 | #include "mp3.h" | |
18 | ||
1dfbe19d E |
19 | #if LIBAVCODEC_VERSION_MAJOR < 55 |
20 | #define AVCodecID CodecID | |
21 | #define AV_CODEC_ID_MP3 CODEC_ID_MP3 | |
22 | #endif | |
23 | ||
fc11dd05 | 24 | static AVCodecContext *ctx; |
25 | ||
986d60fc | 26 | /* avoid compile time linking to libavcodec due to huge list of it's deps.. |
27 | * we also use this old API as newer one is not available on pandora */ | |
28 | void (*p_av_init_packet)(AVPacket *pkt); | |
29 | int (*p_avcodec_decode_audio3)(AVCodecContext *avctx, int16_t *samples, | |
30 | int *frame_size_ptr, AVPacket *avpkt); | |
31 | ||
fc11dd05 | 32 | int mp3dec_decode(FILE *f, int *file_pos, int file_len) |
33 | { | |
34 | unsigned char input_buf[2 * 1024]; | |
35 | int frame_size; | |
36 | AVPacket avpkt; | |
37 | int bytes_in; | |
38 | int bytes_out; | |
39 | int offset; | |
40 | int len; | |
41 | ||
986d60fc | 42 | p_av_init_packet(&avpkt); |
fc11dd05 | 43 | |
44 | do | |
45 | { | |
46 | if (*file_pos >= file_len) | |
47 | return 1; // EOF, nothing to do | |
48 | ||
49 | fseek(f, *file_pos, SEEK_SET); | |
50 | bytes_in = fread(input_buf, 1, sizeof(input_buf), f); | |
51 | ||
52 | offset = mp3_find_sync_word(input_buf, bytes_in); | |
53 | if (offset < 0) { | |
54 | lprintf("find_sync_word (%i/%i) err %i\n", | |
55 | *file_pos, file_len, offset); | |
56 | *file_pos = file_len; | |
57 | return 1; // EOF | |
58 | } | |
59 | ||
60 | // to avoid being flooded with "incorrect frame size" errors, | |
61 | // we must calculate and pass exact frame size - lame | |
62 | frame_size = mpeg1_l3_bitrates[input_buf[offset + 2] >> 4]; | |
63 | frame_size = frame_size * 144000 / 44100; | |
64 | frame_size += (input_buf[offset + 2] >> 1) & 1; | |
65 | ||
66 | if (offset > 0 && bytes_in - offset < frame_size) { | |
67 | // underflow | |
68 | *file_pos += offset; | |
69 | continue; | |
70 | } | |
71 | ||
72 | avpkt.data = input_buf + offset; | |
73 | avpkt.size = frame_size; | |
74 | bytes_out = sizeof(cdda_out_buffer); | |
986d60fc | 75 | #if LIBAVCODEC_VERSION_MAJOR < 53 |
76 | // stupidity in v52: enforces this size even when | |
77 | // it doesn't need/use that much at all | |
78 | bytes_out = AVCODEC_MAX_AUDIO_FRAME_SIZE; | |
79 | #endif | |
fc11dd05 | 80 | |
986d60fc | 81 | len = p_avcodec_decode_audio3(ctx, cdda_out_buffer, |
fc11dd05 | 82 | &bytes_out, &avpkt); |
83 | if (len <= 0) { | |
84 | lprintf("mp3 decode err (%i/%i) %i\n", | |
85 | *file_pos, file_len, len); | |
86 | ||
87 | // attempt to skip the offending frame.. | |
88 | *file_pos += offset + 1; | |
89 | continue; | |
90 | } | |
91 | ||
92 | *file_pos += offset + len; | |
93 | } | |
94 | while (0); | |
95 | ||
96 | return 0; | |
97 | } | |
98 | ||
75a30842 | 99 | int mp3dec_start(FILE *f, int fpos_start) |
fc11dd05 | 100 | { |
986d60fc | 101 | void (*avcodec_register_all)(void); |
1dfbe19d | 102 | AVCodec *(*avcodec_find_decoder)(enum AVCodecID id); |
986d60fc | 103 | AVCodecContext *(*avcodec_alloc_context)(void); |
104 | int (*avcodec_open)(AVCodecContext *avctx, AVCodec *codec); | |
105 | void (*av_free)(void *ptr); | |
fc11dd05 | 106 | AVCodec *codec; |
986d60fc | 107 | void *soh; |
fc11dd05 | 108 | int ret; |
109 | ||
110 | if (ctx != NULL) | |
111 | return 0; | |
112 | ||
986d60fc | 113 | // either v52 or v53 should be ok |
114 | soh = dlopen("libavcodec.so.52", RTLD_NOW); | |
115 | if (soh == NULL) | |
116 | soh = dlopen("libavcodec.so.53", RTLD_NOW); | |
117 | if (soh == NULL) { | |
118 | lprintf("mp3dec: load libavcodec.so: %s\n", dlerror()); | |
119 | return -1; | |
120 | } | |
121 | ||
122 | avcodec_register_all = dlsym(soh, "avcodec_register_all"); | |
123 | avcodec_find_decoder = dlsym(soh, "avcodec_find_decoder"); | |
124 | avcodec_alloc_context = dlsym(soh, "avcodec_alloc_context"); | |
125 | avcodec_open = dlsym(soh, "avcodec_open"); | |
126 | av_free = dlsym(soh, "av_free"); | |
127 | p_av_init_packet = dlsym(soh, "av_init_packet"); | |
128 | p_avcodec_decode_audio3 = dlsym(soh, "avcodec_decode_audio3"); | |
129 | ||
130 | if (avcodec_register_all == NULL || avcodec_find_decoder == NULL | |
131 | || avcodec_alloc_context == NULL || avcodec_open == NULL | |
132 | || av_free == NULL | |
133 | || p_av_init_packet == NULL || p_avcodec_decode_audio3 == NULL) | |
134 | { | |
135 | lprintf("mp3dec: missing symbol(s) in libavcodec.so\n"); | |
136 | dlclose(soh); | |
137 | return -1; | |
138 | } | |
139 | ||
fc11dd05 | 140 | // init decoder |
141 | ||
142 | //avcodec_init(); | |
143 | avcodec_register_all(); | |
144 | ||
1dfbe19d | 145 | codec = avcodec_find_decoder(AV_CODEC_ID_MP3); |
fc11dd05 | 146 | if (codec == NULL) { |
147 | lprintf("mp3dec: codec missing\n"); | |
148 | return -1; | |
149 | } | |
150 | ||
151 | ctx = avcodec_alloc_context(); | |
152 | if (ctx == NULL) { | |
153 | lprintf("mp3dec: avcodec_alloc_context failed\n"); | |
154 | return -1; | |
155 | } | |
156 | ||
157 | ret = avcodec_open(ctx, codec); | |
158 | if (ret < 0) { | |
159 | lprintf("mp3dec: avcodec_open failed: %d\n", ret); | |
160 | av_free(ctx); | |
161 | ctx = NULL; | |
162 | return -1; | |
163 | } | |
164 | ||
165 | return 0; | |
166 | } |