react-native-sherpa-onnx 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -77
- package/SherpaOnnx.podspec +79 -45
- package/android/build.gradle +8 -2
- package/android/prebuilt-download.gradle +70 -16
- package/android/prebuilt-versions.gradle +14 -6
- package/android/src/main/cpp/CMakeLists.txt +2 -0
- package/android/src/main/cpp/jni/audio/sherpa-onnx-audio-convert-jni.cpp +202 -328
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-detect-jni-common.cpp +22 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-detect-jni-common.h +2 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.cpp +96 -142
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.h +40 -4
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-stt.cpp +774 -316
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-tts.cpp +208 -122
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect.h +92 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-stt-wrapper.cpp +3 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-tts-wrapper.cpp +14 -2
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-stt.cpp +229 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-stt.h +38 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-tts.cpp +144 -0
- package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-tts.h +38 -0
- package/android/src/main/cpp/jni/module/sherpa-onnx-module-jni.cpp +1 -1
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +157 -11
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxPcmCapture.kt +150 -0
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxSttHelper.kt +75 -24
- package/android/src/main/java/com/sherpaonnx/SherpaOnnxTtsHelper.kt +52 -1
- package/ios/SherpaOnnx+PcmLiveStream.mm +288 -0
- package/ios/SherpaOnnx+STT.mm +2 -0
- package/ios/SherpaOnnx+TTS.mm +17 -0
- package/ios/SherpaOnnx.mm +27 -3
- package/ios/SherpaOnnxAudioConvert.h +28 -0
- package/ios/SherpaOnnxAudioConvert.mm +698 -0
- package/ios/archive/sherpa-onnx-archive-helper.mm +12 -0
- package/ios/model_detect/sherpa-onnx-model-detect-helper.h +37 -3
- package/ios/model_detect/sherpa-onnx-model-detect-helper.mm +80 -45
- package/ios/model_detect/sherpa-onnx-model-detect-stt.mm +629 -267
- package/ios/model_detect/sherpa-onnx-model-detect-tts.mm +148 -56
- package/ios/model_detect/sherpa-onnx-model-detect.h +72 -0
- package/ios/model_detect/sherpa-onnx-validate-stt.h +38 -0
- package/ios/model_detect/sherpa-onnx-validate-stt.mm +229 -0
- package/ios/model_detect/sherpa-onnx-validate-tts.h +38 -0
- package/ios/model_detect/sherpa-onnx-validate-tts.mm +144 -0
- package/ios/stt/sherpa-onnx-stt-wrapper.mm +4 -0
- package/lib/module/NativeSherpaOnnx.js.map +1 -1
- package/lib/module/audio/index.js +55 -1
- package/lib/module/audio/index.js.map +1 -1
- package/lib/module/download/ModelDownloadManager.js +14 -0
- package/lib/module/download/ModelDownloadManager.js.map +1 -1
- package/lib/module/index.js +10 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/stt/streaming.js +6 -3
- package/lib/module/stt/streaming.js.map +1 -1
- package/lib/module/tts/index.js +13 -1
- package/lib/module/tts/index.js.map +1 -1
- package/lib/typescript/src/NativeSherpaOnnx.d.ts +32 -3
- package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
- package/lib/typescript/src/audio/index.d.ts +20 -1
- package/lib/typescript/src/audio/index.d.ts.map +1 -1
- package/lib/typescript/src/download/ModelDownloadManager.d.ts +2 -1
- package/lib/typescript/src/download/ModelDownloadManager.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +10 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/stt/streaming.d.ts.map +1 -1
- package/lib/typescript/src/stt/streamingTypes.d.ts +1 -1
- package/lib/typescript/src/stt/streamingTypes.d.ts.map +1 -1
- package/lib/typescript/src/tts/index.d.ts +12 -1
- package/lib/typescript/src/tts/index.d.ts.map +1 -1
- package/package.json +6 -1
- package/scripts/check-model-csvs.sh +72 -0
- package/scripts/setup-ios-framework.sh +272 -191
- package/src/NativeSherpaOnnx.ts +37 -3
- package/src/audio/index.ts +84 -1
- package/src/download/ModelDownloadManager.ts +19 -0
- package/src/index.tsx +15 -0
- package/src/stt/streaming.ts +10 -5
- package/src/stt/streamingTypes.ts +1 -1
- package/src/tts/index.ts +25 -1
- package/third_party/ffmpeg_prebuilt/ANDROID_RELEASE_TAG +1 -1
- package/third_party/libarchive_prebuilt/ANDROID_RELEASE_TAG +1 -1
- package/third_party/libarchive_prebuilt/IOS_RELEASE_TAG +1 -1
- package/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG +1 -1
- package/third_party/sherpa-onnx-prebuilt/IOS_RELEASE_TAG +1 -1
- package/ios/scripts/patch-libarchive-includes.sh +0 -61
- package/ios/scripts/setup-ios-libarchive.sh +0 -98
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
#import "SherpaOnnxAudioConvert.h"
|
|
2
|
+
#import <React/RCTLog.h>
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <sys/stat.h>
|
|
5
|
+
|
|
6
|
+
#ifdef HAVE_FFMPEG
|
|
7
|
+
extern "C" {
|
|
8
|
+
#include <libavcodec/avcodec.h>
|
|
9
|
+
#include <libavformat/avformat.h>
|
|
10
|
+
#include <libavutil/opt.h>
|
|
11
|
+
#include <libavutil/error.h>
|
|
12
|
+
#include <libswresample/swresample.h>
|
|
13
|
+
}
|
|
14
|
+
#include <cstdio>
|
|
15
|
+
#include <vector>
|
|
16
|
+
#endif
|
|
17
|
+
|
|
18
|
+
// Forward declaration — convertToFormat handles all formats including WAV (16 kHz mono).
|
|
19
|
+
static std::string convertToFormat(const char* inputPath, const char* outputPath, const char* formatHint, int outputSampleRateHz);
|
|
20
|
+
|
|
21
|
+
// Convenience: convert any audio to 16 kHz mono WAV via the main convertToFormat pipeline.
|
|
22
|
+
static std::string convertToWav16kMono(const char* inputPath, const char* outputPath) {
|
|
23
|
+
return convertToFormat(inputPath, outputPath, "wav", 16000);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Generic conversion: supports writing WAV/MP3/FLAC depending on output file extension and linked encoders.
|
|
27
|
+
// WAV output is 16 kHz mono PCM (sherpa-onnx). outputSampleRateHz is only used for MP3 (libshine: 32000/44100/48000); 0 = default 44100.
|
|
28
|
+
static std::string convertToFormat(const char* inputPath, const char* outputPath, const char* formatHint, int outputSampleRateHz) {
|
|
29
|
+
#ifdef HAVE_FFMPEG
|
|
30
|
+
std::string fmt(formatHint ? formatHint : "");
|
|
31
|
+
bool isWav = (fmt == "wav" || fmt == "wav16k" || fmt.empty());
|
|
32
|
+
|
|
33
|
+
AVCodecID codec_id = AV_CODEC_ID_NONE;
|
|
34
|
+
if (isWav) codec_id = AV_CODEC_ID_PCM_S16LE;
|
|
35
|
+
else if (fmt == "mp3") codec_id = AV_CODEC_ID_MP3;
|
|
36
|
+
else if (fmt == "flac") codec_id = AV_CODEC_ID_FLAC;
|
|
37
|
+
else if (fmt == "m4a" || fmt == "aac") codec_id = AV_CODEC_ID_AAC;
|
|
38
|
+
else if (fmt == "opus" || fmt == "oggm" || fmt == "ogg" || fmt == "webm" || fmt == "mkv") codec_id = AV_CODEC_ID_OPUS;
|
|
39
|
+
else codec_id = AV_CODEC_ID_PCM_S16LE;
|
|
40
|
+
|
|
41
|
+
struct stat stIn = {};
|
|
42
|
+
long inputSizeBytes = (stat(inputPath, &stIn) == 0 && S_ISREG(stIn.st_mode)) ? (long)stIn.st_size : -1;
|
|
43
|
+
RCTLogInfo(@"[SherpaOnnxAudioConvert] convertToFormat: input=%s size=%ld format=%s output=%s",
|
|
44
|
+
inputPath ? inputPath : "(null)", inputSizeBytes, formatHint ? formatHint : "", outputPath ? outputPath : "(null)");
|
|
45
|
+
|
|
46
|
+
// Open input
|
|
47
|
+
AVFormatContext* inFmt = nullptr;
|
|
48
|
+
if (avformat_open_input(&inFmt, inputPath, nullptr, nullptr) < 0) {
|
|
49
|
+
RCTLogError(@"[SherpaOnnxAudioConvert] Failed to open input file: inputPath=%s", inputPath ? inputPath : "(null)");
|
|
50
|
+
return std::string("Failed to open input file");
|
|
51
|
+
}
|
|
52
|
+
if (avformat_find_stream_info(inFmt, nullptr) < 0) {
|
|
53
|
+
avformat_close_input(&inFmt);
|
|
54
|
+
return std::string("Failed to find stream info");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
int audioStreamIndex = -1;
|
|
58
|
+
for (unsigned i = 0; i < inFmt->nb_streams; ++i) {
|
|
59
|
+
if (inFmt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
|
60
|
+
audioStreamIndex = i;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (audioStreamIndex < 0) {
|
|
65
|
+
avformat_close_input(&inFmt);
|
|
66
|
+
return std::string("No audio stream found in input");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
AVStream* inStream = inFmt->streams[audioStreamIndex];
|
|
70
|
+
const AVCodec* decoder = avcodec_find_decoder(inStream->codecpar->codec_id);
|
|
71
|
+
if (!decoder) {
|
|
72
|
+
avformat_close_input(&inFmt);
|
|
73
|
+
return std::string("Unsupported input codec");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
AVCodecContext* decCtx = avcodec_alloc_context3(decoder);
|
|
77
|
+
if (!decCtx) {
|
|
78
|
+
avformat_close_input(&inFmt);
|
|
79
|
+
return std::string("Failed to allocate decoder context");
|
|
80
|
+
}
|
|
81
|
+
if (avcodec_parameters_to_context(decCtx, inStream->codecpar) < 0) {
|
|
82
|
+
avcodec_free_context(&decCtx);
|
|
83
|
+
avformat_close_input(&inFmt);
|
|
84
|
+
return std::string("Failed to copy codec parameters");
|
|
85
|
+
}
|
|
86
|
+
if (avcodec_open2(decCtx, decoder, nullptr) < 0) {
|
|
87
|
+
avcodec_free_context(&decCtx);
|
|
88
|
+
avformat_close_input(&inFmt);
|
|
89
|
+
return std::string("Failed to open decoder");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
SwrContext* swr = nullptr;
|
|
93
|
+
|
|
94
|
+
AVFormatContext* outFmt = nullptr;
|
|
95
|
+
if (avformat_alloc_output_context2(&outFmt, nullptr, nullptr, outputPath) < 0 || !outFmt) {
|
|
96
|
+
swr_free(&swr);
|
|
97
|
+
avcodec_free_context(&decCtx);
|
|
98
|
+
avformat_close_input(&inFmt);
|
|
99
|
+
return std::string("Failed to allocate output context");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const AVCodec* encoder = nullptr;
|
|
103
|
+
if (codec_id == AV_CODEC_ID_MP3) {
|
|
104
|
+
encoder = avcodec_find_encoder_by_name("libshine");
|
|
105
|
+
if (!encoder) {
|
|
106
|
+
avformat_free_context(outFmt);
|
|
107
|
+
swr_free(&swr);
|
|
108
|
+
avcodec_free_context(&decCtx);
|
|
109
|
+
avformat_close_input(&inFmt);
|
|
110
|
+
return std::string("libshine encoder not available in this build");
|
|
111
|
+
}
|
|
112
|
+
} else if (codec_id == AV_CODEC_ID_OPUS) {
|
|
113
|
+
encoder = avcodec_find_encoder_by_name("libopus");
|
|
114
|
+
if (!encoder) {
|
|
115
|
+
avformat_free_context(outFmt);
|
|
116
|
+
swr_free(&swr);
|
|
117
|
+
avcodec_free_context(&decCtx);
|
|
118
|
+
avformat_close_input(&inFmt);
|
|
119
|
+
return std::string("libopus encoder not available in this build");
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
encoder = avcodec_find_encoder(codec_id);
|
|
123
|
+
if (!encoder) {
|
|
124
|
+
avformat_free_context(outFmt);
|
|
125
|
+
swr_free(&swr);
|
|
126
|
+
avcodec_free_context(&decCtx);
|
|
127
|
+
avformat_close_input(&inFmt);
|
|
128
|
+
return std::string("Requested encoder not available in this build");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
AVStream* outStream = avformat_new_stream(outFmt, nullptr);
|
|
133
|
+
if (!outStream) {
|
|
134
|
+
avformat_free_context(outFmt);
|
|
135
|
+
swr_free(&swr);
|
|
136
|
+
avcodec_free_context(&decCtx);
|
|
137
|
+
avformat_close_input(&inFmt);
|
|
138
|
+
return std::string("Failed to create output stream");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
AVCodecContext* encCtx = avcodec_alloc_context3(encoder);
|
|
142
|
+
if (!encCtx) {
|
|
143
|
+
avformat_free_context(outFmt);
|
|
144
|
+
swr_free(&swr);
|
|
145
|
+
avcodec_free_context(&decCtx);
|
|
146
|
+
avformat_close_input(&inFmt);
|
|
147
|
+
return std::string("Failed to allocate encoder context");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Set channel layout: prefer input stream layout, otherwise decoder layout.
|
|
151
|
+
if (inStream->codecpar->ch_layout.nb_channels) {
|
|
152
|
+
if (av_channel_layout_copy(&encCtx->ch_layout, &inStream->codecpar->ch_layout) < 0) {
|
|
153
|
+
avcodec_free_context(&encCtx);
|
|
154
|
+
avformat_free_context(outFmt);
|
|
155
|
+
swr_free(&swr);
|
|
156
|
+
avcodec_free_context(&decCtx);
|
|
157
|
+
avformat_close_input(&inFmt);
|
|
158
|
+
return std::string("Failed to copy input channel layout to encoder");
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
if (av_channel_layout_copy(&encCtx->ch_layout, &decCtx->ch_layout) < 0) {
|
|
162
|
+
avcodec_free_context(&encCtx);
|
|
163
|
+
avformat_free_context(outFmt);
|
|
164
|
+
swr_free(&swr);
|
|
165
|
+
avcodec_free_context(&decCtx);
|
|
166
|
+
avformat_close_input(&inFmt);
|
|
167
|
+
return std::string("Failed to set encoder channel layout");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (codec_id == AV_CODEC_ID_MP3) {
|
|
172
|
+
if (encCtx->ch_layout.nb_channels <= 0) {
|
|
173
|
+
int nb_channels = 1;
|
|
174
|
+
if (inStream->codecpar && inStream->codecpar->ch_layout.nb_channels > 0) {
|
|
175
|
+
nb_channels = inStream->codecpar->ch_layout.nb_channels;
|
|
176
|
+
} else if (decCtx && decCtx->ch_layout.nb_channels > 0) {
|
|
177
|
+
nb_channels = decCtx->ch_layout.nb_channels;
|
|
178
|
+
}
|
|
179
|
+
av_channel_layout_default(&encCtx->ch_layout, nb_channels);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
encCtx->sample_rate = inStream->codecpar->sample_rate ? inStream->codecpar->sample_rate : decCtx->sample_rate;
|
|
184
|
+
|
|
185
|
+
if (isWav) {
|
|
186
|
+
encCtx->sample_rate = 16000;
|
|
187
|
+
encCtx->sample_fmt = AV_SAMPLE_FMT_S16;
|
|
188
|
+
av_channel_layout_uninit(&encCtx->ch_layout);
|
|
189
|
+
AVChannelLayout mono = AV_CHANNEL_LAYOUT_MONO;
|
|
190
|
+
av_channel_layout_copy(&encCtx->ch_layout, &mono);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
AVSampleFormat chosen_fmt = AV_SAMPLE_FMT_NONE;
|
|
194
|
+
const void *fmt_configs = nullptr;
|
|
195
|
+
int fmt_num = 0;
|
|
196
|
+
avcodec_get_supported_config(encCtx, encoder, AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, &fmt_configs, &fmt_num);
|
|
197
|
+
|
|
198
|
+
const void *sr_configs = nullptr;
|
|
199
|
+
int sr_num = 0;
|
|
200
|
+
avcodec_get_supported_config(encCtx, encoder, AV_CODEC_CONFIG_SAMPLE_RATE, 0, &sr_configs, &sr_num);
|
|
201
|
+
|
|
202
|
+
const void *chl_configs = nullptr;
|
|
203
|
+
int chl_num = 0;
|
|
204
|
+
avcodec_get_supported_config(encCtx, encoder, AV_CODEC_CONFIG_CHANNEL_LAYOUT, 0, &chl_configs, &chl_num);
|
|
205
|
+
|
|
206
|
+
if (fmt_configs && fmt_num > 0) {
|
|
207
|
+
const AVSampleFormat *fmts = (const AVSampleFormat *)fmt_configs;
|
|
208
|
+
for (int i = 0; i < fmt_num; ++i) if (fmts[i] == AV_SAMPLE_FMT_S16) { chosen_fmt = AV_SAMPLE_FMT_S16; break; }
|
|
209
|
+
if (chosen_fmt == AV_SAMPLE_FMT_NONE && codec_id == AV_CODEC_ID_MP3) {
|
|
210
|
+
for (int i = 0; i < fmt_num; ++i) if (fmts[i] == AV_SAMPLE_FMT_S16P) { chosen_fmt = AV_SAMPLE_FMT_S16P; break; }
|
|
211
|
+
}
|
|
212
|
+
if (chosen_fmt == AV_SAMPLE_FMT_NONE) {
|
|
213
|
+
for (int i = 0; i < fmt_num; ++i) if (fmts[i] == decCtx->sample_fmt) { chosen_fmt = decCtx->sample_fmt; break; }
|
|
214
|
+
}
|
|
215
|
+
if (chosen_fmt == AV_SAMPLE_FMT_NONE && fmt_num > 0) chosen_fmt = fmts[0];
|
|
216
|
+
} else {
|
|
217
|
+
// If not MP3, try to use S16 (standard). If AAC, it might prefer FLTP, which `chosen_fmt = fmts[0]` captures below.
|
|
218
|
+
chosen_fmt = (codec_id == AV_CODEC_ID_MP3) ? AV_SAMPLE_FMT_S16P : AV_SAMPLE_FMT_S16;
|
|
219
|
+
}
|
|
220
|
+
encCtx->sample_fmt = chosen_fmt;
|
|
221
|
+
|
|
222
|
+
if (sr_configs && sr_num > 0) {
|
|
223
|
+
const int *srs = (const int*)sr_configs;
|
|
224
|
+
int pick_sr = 0;
|
|
225
|
+
for (int i = 0; i < sr_num; ++i) {
|
|
226
|
+
if (srs[i] == encCtx->sample_rate) { pick_sr = srs[i]; break; }
|
|
227
|
+
}
|
|
228
|
+
if (pick_sr == 0) pick_sr = srs[0];
|
|
229
|
+
encCtx->sample_rate = pick_sr;
|
|
230
|
+
}
|
|
231
|
+
if (codec_id == AV_CODEC_ID_MP3) {
|
|
232
|
+
int want = (outputSampleRateHz == 32000 || outputSampleRateHz == 44100 || outputSampleRateHz == 48000) ? outputSampleRateHz : 44100;
|
|
233
|
+
if (encCtx->sample_rate != want) encCtx->sample_rate = want;
|
|
234
|
+
}
|
|
235
|
+
if (codec_id == AV_CODEC_ID_OPUS) {
|
|
236
|
+
int want = (outputSampleRateHz == 8000 || outputSampleRateHz == 12000 || outputSampleRateHz == 16000 || outputSampleRateHz == 24000 || outputSampleRateHz == 48000) ? outputSampleRateHz : 48000;
|
|
237
|
+
if (encCtx->sample_rate != want) encCtx->sample_rate = want;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (chl_configs && chl_num > 0) {
|
|
241
|
+
const AVChannelLayout *layouts = (const AVChannelLayout *)chl_configs;
|
|
242
|
+
int pick_nb = 0;
|
|
243
|
+
for (int i = 0; i < chl_num; ++i) {
|
|
244
|
+
const AVChannelLayout *l = &layouts[i];
|
|
245
|
+
if (l->nb_channels == encCtx->ch_layout.nb_channels) { pick_nb = l->nb_channels; break; }
|
|
246
|
+
}
|
|
247
|
+
if (pick_nb == 0) pick_nb = layouts[0].nb_channels > 0 ? layouts[0].nb_channels : 1;
|
|
248
|
+
if (encCtx->ch_layout.nb_channels != pick_nb) av_channel_layout_default(&encCtx->ch_layout, pick_nb);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (codec_id == AV_CODEC_ID_MP3) {
|
|
252
|
+
int want_ch = (encCtx->ch_layout.nb_channels == 2) ? 2 : 1;
|
|
253
|
+
av_channel_layout_uninit(&encCtx->ch_layout);
|
|
254
|
+
if (want_ch == 2) {
|
|
255
|
+
AVChannelLayout stereo = AV_CHANNEL_LAYOUT_STEREO;
|
|
256
|
+
if (av_channel_layout_copy(&encCtx->ch_layout, &stereo) < 0)
|
|
257
|
+
av_channel_layout_default(&encCtx->ch_layout, 2);
|
|
258
|
+
} else {
|
|
259
|
+
AVChannelLayout mono = AV_CHANNEL_LAYOUT_MONO;
|
|
260
|
+
if (av_channel_layout_copy(&encCtx->ch_layout, &mono) < 0)
|
|
261
|
+
av_channel_layout_default(&encCtx->ch_layout, 1);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (codec_id == AV_CODEC_ID_MP3 || codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_OPUS) encCtx->bit_rate = 128000;
|
|
266
|
+
else encCtx->bit_rate = 0;
|
|
267
|
+
|
|
268
|
+
if (outFmt->oformat->flags & AVFMT_GLOBALHEADER) encCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
|
269
|
+
|
|
270
|
+
if (encCtx->sample_rate > 0) {
|
|
271
|
+
encCtx->time_base = AVRational{1, encCtx->sample_rate};
|
|
272
|
+
if (outStream) {
|
|
273
|
+
outStream->time_base = encCtx->time_base;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
AVDictionary *enc_opts = nullptr;
|
|
278
|
+
int nb_ch = encCtx->ch_layout.nb_channels;
|
|
279
|
+
if (nb_ch <= 0) nb_ch = 1;
|
|
280
|
+
char tmpbuf[64];
|
|
281
|
+
if (codec_id != AV_CODEC_ID_MP3) {
|
|
282
|
+
snprintf(tmpbuf, sizeof(tmpbuf), "%d", nb_ch);
|
|
283
|
+
av_dict_set(&enc_opts, "channels", tmpbuf, 0);
|
|
284
|
+
snprintf(tmpbuf, sizeof(tmpbuf), "%d", encCtx->sample_rate);
|
|
285
|
+
av_dict_set(&enc_opts, "sample_rate", tmpbuf, 0);
|
|
286
|
+
if (encCtx->bit_rate > 0) {
|
|
287
|
+
snprintf(tmpbuf, sizeof(tmpbuf), "%d", (int)encCtx->bit_rate);
|
|
288
|
+
av_dict_set(&enc_opts, "bit_rate", tmpbuf, 0);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
int ret = avcodec_open2(encCtx, encoder, &enc_opts);
|
|
293
|
+
av_dict_free(&enc_opts);
|
|
294
|
+
enc_opts = nullptr;
|
|
295
|
+
if (ret < 0) {
|
|
296
|
+
char errbuf[256];
|
|
297
|
+
av_strerror(ret, errbuf, sizeof(errbuf));
|
|
298
|
+
|
|
299
|
+
if (codec_id == AV_CODEC_ID_MP3) {
|
|
300
|
+
std::string msg = std::string("Failed to open encoder: ") + errbuf;
|
|
301
|
+
avcodec_free_context(&encCtx);
|
|
302
|
+
avformat_free_context(outFmt);
|
|
303
|
+
swr_free(&swr);
|
|
304
|
+
avcodec_free_context(&decCtx);
|
|
305
|
+
avformat_close_input(&inFmt);
|
|
306
|
+
return msg;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
RCTLogWarn(@"[SherpaOnnxAudioConvert] avcodec_open2 failed for encoder %s: %s. Trying alternatives.", encoder->name, errbuf);
|
|
310
|
+
|
|
311
|
+
const AVSampleFormat *fmts = fmt_configs ? (const AVSampleFormat*)fmt_configs : nullptr;
|
|
312
|
+
if (fmts && fmt_num > 0) {
|
|
313
|
+
for (int i = 0; i < fmt_num && ret < 0; ++i) {
|
|
314
|
+
encCtx->sample_fmt = fmts[i];
|
|
315
|
+
AVDictionary *try_opts = nullptr;
|
|
316
|
+
snprintf(tmpbuf, sizeof(tmpbuf), "%d", encCtx->ch_layout.nb_channels > 0 ? encCtx->ch_layout.nb_channels : 1);
|
|
317
|
+
av_dict_set(&try_opts, "channels", tmpbuf, 0);
|
|
318
|
+
snprintf(tmpbuf, sizeof(tmpbuf), "%d", encCtx->sample_rate);
|
|
319
|
+
av_dict_set(&try_opts, "sample_rate", tmpbuf, 0);
|
|
320
|
+
if (encCtx->bit_rate > 0) { snprintf(tmpbuf, sizeof(tmpbuf), "%d", (int)encCtx->bit_rate); av_dict_set(&try_opts, "bit_rate", tmpbuf, 0); }
|
|
321
|
+
const char *sfname = av_get_sample_fmt_name(encCtx->sample_fmt);
|
|
322
|
+
if (sfname) av_dict_set(&try_opts, "sample_fmt", sfname, 0);
|
|
323
|
+
int r = avcodec_open2(encCtx, encoder, &try_opts);
|
|
324
|
+
if (r >= 0) {
|
|
325
|
+
if (try_opts) av_dict_free(&try_opts);
|
|
326
|
+
ret = r;
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
if (try_opts) av_dict_free(&try_opts);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (ret < 0) {
|
|
334
|
+
// AAC encoders typically require FLTP; try that specifically.
|
|
335
|
+
AVSampleFormat fallbacks[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_FLTP };
|
|
336
|
+
for (int fi = 0; fi < 3 && ret < 0; ++fi) {
|
|
337
|
+
encCtx->sample_fmt = fallbacks[fi];
|
|
338
|
+
AVDictionary *try_opts = nullptr;
|
|
339
|
+
snprintf(tmpbuf, sizeof(tmpbuf), "%d", encCtx->ch_layout.nb_channels > 0 ? encCtx->ch_layout.nb_channels : 1);
|
|
340
|
+
av_dict_set(&try_opts, "channels", tmpbuf, 0);
|
|
341
|
+
snprintf(tmpbuf, sizeof(tmpbuf), "%d", encCtx->sample_rate);
|
|
342
|
+
av_dict_set(&try_opts, "sample_rate", tmpbuf, 0);
|
|
343
|
+
if (encCtx->bit_rate > 0) { snprintf(tmpbuf, sizeof(tmpbuf), "%d", (int)encCtx->bit_rate); av_dict_set(&try_opts, "bit_rate", tmpbuf, 0); }
|
|
344
|
+
const char *sfname = av_get_sample_fmt_name(encCtx->sample_fmt);
|
|
345
|
+
if (sfname) av_dict_set(&try_opts, "sample_fmt", sfname, 0);
|
|
346
|
+
int r = avcodec_open2(encCtx, encoder, &try_opts);
|
|
347
|
+
if (r >= 0) {
|
|
348
|
+
if (try_opts) av_dict_free(&try_opts);
|
|
349
|
+
ret = r;
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
if (try_opts) av_dict_free(&try_opts);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (ret < 0) {
|
|
357
|
+
char eb[256]; av_strerror(ret, eb, sizeof(eb));
|
|
358
|
+
std::string msg = std::string("Failed to open encoder: ") + eb;
|
|
359
|
+
avcodec_free_context(&encCtx);
|
|
360
|
+
avformat_free_context(outFmt);
|
|
361
|
+
swr_free(&swr);
|
|
362
|
+
avcodec_free_context(&decCtx);
|
|
363
|
+
avformat_close_input(&inFmt);
|
|
364
|
+
return msg;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (avcodec_parameters_from_context(outStream->codecpar, encCtx) < 0) {
|
|
369
|
+
avcodec_free_context(&encCtx);
|
|
370
|
+
avformat_free_context(outFmt);
|
|
371
|
+
swr_free(&swr);
|
|
372
|
+
avcodec_free_context(&decCtx);
|
|
373
|
+
avformat_close_input(&inFmt);
|
|
374
|
+
return std::string("Failed to set output stream parameters");
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (!(outFmt->oformat->flags & AVFMT_NOFILE)) {
|
|
378
|
+
if (avio_open(&outFmt->pb, outputPath, AVIO_FLAG_WRITE) < 0) {
|
|
379
|
+
avcodec_free_context(&encCtx);
|
|
380
|
+
avformat_free_context(outFmt);
|
|
381
|
+
swr_free(&swr);
|
|
382
|
+
avcodec_free_context(&decCtx);
|
|
383
|
+
avformat_close_input(&inFmt);
|
|
384
|
+
return std::string("Failed to open output file for writing");
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (avformat_write_header(outFmt, nullptr) < 0) {
|
|
389
|
+
if (!(outFmt->oformat->flags & AVFMT_NOFILE)) avio_closep(&outFmt->pb);
|
|
390
|
+
avcodec_free_context(&encCtx);
|
|
391
|
+
avformat_free_context(outFmt);
|
|
392
|
+
swr_free(&swr);
|
|
393
|
+
avcodec_free_context(&decCtx);
|
|
394
|
+
avformat_close_input(&inFmt);
|
|
395
|
+
return std::string("Failed to write output header");
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
AVPacket* pkt = av_packet_alloc();
|
|
399
|
+
AVFrame* frame = av_frame_alloc();
|
|
400
|
+
AVFrame* resampled = av_frame_alloc();
|
|
401
|
+
resampled->format = encCtx->sample_fmt;
|
|
402
|
+
resampled->sample_rate = encCtx->sample_rate;
|
|
403
|
+
if (av_channel_layout_copy(&resampled->ch_layout, &encCtx->ch_layout) < 0) {
|
|
404
|
+
av_frame_free(&frame);
|
|
405
|
+
av_frame_free(&resampled);
|
|
406
|
+
av_packet_free(&pkt);
|
|
407
|
+
avcodec_free_context(&encCtx);
|
|
408
|
+
avformat_free_context(outFmt);
|
|
409
|
+
avcodec_free_context(&decCtx);
|
|
410
|
+
avformat_close_input(&inFmt);
|
|
411
|
+
return std::string("Failed to set resampled channel layout");
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
AVChannelLayout in_ch_layout2{};
|
|
415
|
+
if (inStream->codecpar->ch_layout.nb_channels) {
|
|
416
|
+
if (av_channel_layout_copy(&in_ch_layout2, &inStream->codecpar->ch_layout) < 0) {
|
|
417
|
+
av_channel_layout_uninit(&resampled->ch_layout);
|
|
418
|
+
av_frame_free(&frame);
|
|
419
|
+
av_frame_free(&resampled);
|
|
420
|
+
av_packet_free(&pkt);
|
|
421
|
+
avcodec_free_context(&encCtx);
|
|
422
|
+
avformat_free_context(outFmt);
|
|
423
|
+
swr_free(&swr);
|
|
424
|
+
avcodec_free_context(&decCtx);
|
|
425
|
+
avformat_close_input(&inFmt);
|
|
426
|
+
return std::string("Failed to copy input channel layout");
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
429
|
+
if (av_channel_layout_copy(&in_ch_layout2, &decCtx->ch_layout) < 0) {
|
|
430
|
+
av_channel_layout_uninit(&resampled->ch_layout);
|
|
431
|
+
av_frame_free(&frame);
|
|
432
|
+
av_frame_free(&resampled);
|
|
433
|
+
av_packet_free(&pkt);
|
|
434
|
+
avcodec_free_context(&encCtx);
|
|
435
|
+
avformat_free_context(outFmt);
|
|
436
|
+
swr_free(&swr);
|
|
437
|
+
avcodec_free_context(&decCtx);
|
|
438
|
+
avformat_close_input(&inFmt);
|
|
439
|
+
return std::string("Failed to init input channel layout");
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (swr_alloc_set_opts2(&swr,
|
|
443
|
+
&encCtx->ch_layout, encCtx->sample_fmt, encCtx->sample_rate,
|
|
444
|
+
&in_ch_layout2, (AVSampleFormat)decCtx->sample_fmt, decCtx->sample_rate,
|
|
445
|
+
0, nullptr) < 0 || !swr) {
|
|
446
|
+
av_channel_layout_uninit(&in_ch_layout2);
|
|
447
|
+
if (swr) swr_free(&swr);
|
|
448
|
+
av_channel_layout_uninit(&resampled->ch_layout);
|
|
449
|
+
av_frame_free(&frame);
|
|
450
|
+
av_frame_free(&resampled);
|
|
451
|
+
av_packet_free(&pkt);
|
|
452
|
+
avcodec_free_context(&encCtx);
|
|
453
|
+
avformat_free_context(outFmt);
|
|
454
|
+
avcodec_free_context(&decCtx);
|
|
455
|
+
avformat_close_input(&inFmt);
|
|
456
|
+
return std::string("Failed to initialize resampler");
|
|
457
|
+
}
|
|
458
|
+
{
|
|
459
|
+
int initRet = swr_init(swr);
|
|
460
|
+
if (initRet < 0) {
|
|
461
|
+
char errbuf[256];
|
|
462
|
+
av_strerror(initRet, errbuf, sizeof(errbuf));
|
|
463
|
+
RCTLogError(@"[SherpaOnnxAudioConvert] swr_init failed: %s", errbuf);
|
|
464
|
+
av_channel_layout_uninit(&in_ch_layout2);
|
|
465
|
+
swr_free(&swr);
|
|
466
|
+
av_channel_layout_uninit(&resampled->ch_layout);
|
|
467
|
+
av_frame_free(&frame);
|
|
468
|
+
av_frame_free(&resampled);
|
|
469
|
+
av_packet_free(&pkt);
|
|
470
|
+
avcodec_free_context(&encCtx);
|
|
471
|
+
avformat_free_context(outFmt);
|
|
472
|
+
avcodec_free_context(&decCtx);
|
|
473
|
+
avformat_close_input(&inFmt);
|
|
474
|
+
return std::string("Failed to initialize resampler (swr_init)");
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
av_channel_layout_uninit(&in_ch_layout2);
|
|
478
|
+
|
|
479
|
+
int totalDecodedFrames = 0;
|
|
480
|
+
int totalFramesSent = 0;
|
|
481
|
+
int totalPacketsFromEncoder = 0;
|
|
482
|
+
int flushPackets = 0;
|
|
483
|
+
int64_t encoder_pts = 0;
|
|
484
|
+
|
|
485
|
+
const int default_frame_size = 1024;
|
|
486
|
+
const int enc_frame_size =
|
|
487
|
+
(codec_id == AV_CODEC_ID_MP3) ? 1152 :
|
|
488
|
+
(encCtx->frame_size > 0 ? encCtx->frame_size : default_frame_size);
|
|
489
|
+
int out_ch2 = encCtx->ch_layout.nb_channels;
|
|
490
|
+
if (out_ch2 <= 0) out_ch2 = 1;
|
|
491
|
+
int bytes_per_sample = av_get_bytes_per_sample(encCtx->sample_fmt);
|
|
492
|
+
|
|
493
|
+
std::vector<uint8_t> accumBuf;
|
|
494
|
+
size_t accumReadOffset = 0;
|
|
495
|
+
const int bytesPerFrame = bytes_per_sample * out_ch2;
|
|
496
|
+
int accumSamples = 0;
|
|
497
|
+
|
|
498
|
+
const size_t kCompactThreshold = 256 * 1024;
|
|
499
|
+
|
|
500
|
+
auto maybeCompact = [&]() {
|
|
501
|
+
if (accumReadOffset == 0) return;
|
|
502
|
+
if (accumReadOffset < kCompactThreshold && accumReadOffset * 2 < accumBuf.size()) return;
|
|
503
|
+
size_t valid = accumBuf.size() - accumReadOffset;
|
|
504
|
+
if (valid > 0) memmove(accumBuf.data(), accumBuf.data() + accumReadOffset, valid);
|
|
505
|
+
accumBuf.resize(valid);
|
|
506
|
+
accumReadOffset = 0;
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
auto flushAccumFrames = [&](bool sendPartial) {
|
|
510
|
+
int needed = enc_frame_size;
|
|
511
|
+
if (needed <= 0) return;
|
|
512
|
+
|
|
513
|
+
while (accumSamples >= needed || (sendPartial && accumSamples > 0)) {
|
|
514
|
+
int toSend = (accumSamples >= needed) ? needed : accumSamples;
|
|
515
|
+
AVFrame* ef = av_frame_alloc();
|
|
516
|
+
if (!ef) break;
|
|
517
|
+
ef->format = encCtx->sample_fmt;
|
|
518
|
+
ef->sample_rate = encCtx->sample_rate;
|
|
519
|
+
if (av_channel_layout_copy(&ef->ch_layout, &encCtx->ch_layout) < 0) { av_frame_free(&ef); break; }
|
|
520
|
+
ef->nb_samples = toSend;
|
|
521
|
+
if (av_frame_get_buffer(ef, 0) < 0) { av_channel_layout_uninit(&ef->ch_layout); av_frame_free(&ef); break; }
|
|
522
|
+
int copyBytes = toSend * bytesPerFrame;
|
|
523
|
+
memcpy(ef->data[0], accumBuf.data() + accumReadOffset, copyBytes);
|
|
524
|
+
ef->pts = encoder_pts;
|
|
525
|
+
encoder_pts += toSend;
|
|
526
|
+
|
|
527
|
+
accumReadOffset += (size_t)copyBytes;
|
|
528
|
+
accumSamples -= toSend;
|
|
529
|
+
|
|
530
|
+
for (;;) {
|
|
531
|
+
int ret = avcodec_send_frame(encCtx, ef);
|
|
532
|
+
if (ret == 0) break;
|
|
533
|
+
if (ret == AVERROR(EAGAIN)) {
|
|
534
|
+
AVPacket* op = av_packet_alloc();
|
|
535
|
+
while (avcodec_receive_packet(encCtx, op) == 0) {
|
|
536
|
+
op->stream_index = outStream->index;
|
|
537
|
+
av_packet_rescale_ts(op, encCtx->time_base, outStream->time_base);
|
|
538
|
+
av_interleaved_write_frame(outFmt, op);
|
|
539
|
+
av_packet_unref(op);
|
|
540
|
+
totalPacketsFromEncoder++;
|
|
541
|
+
}
|
|
542
|
+
av_packet_free(&op);
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
RCTLogWarn(@"[SherpaOnnxAudioConvert] send_frame ret=%d frame=%d pts=%lld nb=%d", ret, totalFramesSent, (long long)ef->pts, toSend);
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
AVPacket* op = av_packet_alloc();
|
|
549
|
+
while (avcodec_receive_packet(encCtx, op) == 0) {
|
|
550
|
+
op->stream_index = outStream->index;
|
|
551
|
+
av_packet_rescale_ts(op, encCtx->time_base, outStream->time_base);
|
|
552
|
+
av_interleaved_write_frame(outFmt, op);
|
|
553
|
+
av_packet_unref(op);
|
|
554
|
+
totalPacketsFromEncoder++;
|
|
555
|
+
}
|
|
556
|
+
av_packet_free(&op);
|
|
557
|
+
|
|
558
|
+
av_channel_layout_uninit(&ef->ch_layout);
|
|
559
|
+
av_frame_free(&ef);
|
|
560
|
+
totalFramesSent++;
|
|
561
|
+
|
|
562
|
+
if (!sendPartial && accumSamples < needed) break;
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
while (av_read_frame(inFmt, pkt) >= 0) {
|
|
567
|
+
if (pkt->stream_index == audioStreamIndex) {
|
|
568
|
+
if (avcodec_send_packet(decCtx, pkt) == 0) {
|
|
569
|
+
while (avcodec_receive_frame(decCtx, frame) == 0) {
|
|
570
|
+
totalDecodedFrames++;
|
|
571
|
+
int in_sr2 = inStream->codecpar->sample_rate ? inStream->codecpar->sample_rate : decCtx->sample_rate;
|
|
572
|
+
int64_t out_nb_samples = av_rescale_rnd(swr_get_delay(swr, in_sr2) + frame->nb_samples, encCtx->sample_rate, in_sr2, AV_ROUND_UP);
|
|
573
|
+
uint8_t** outData = nullptr;
|
|
574
|
+
if (av_samples_alloc_array_and_samples(&outData, nullptr, out_ch2, (int)out_nb_samples, encCtx->sample_fmt, 0) < 0) {
|
|
575
|
+
av_packet_unref(pkt);
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
int converted = swr_convert(swr, outData, (int)out_nb_samples, (const uint8_t**)frame->data, frame->nb_samples);
|
|
579
|
+
if (converted <= 0) {
|
|
580
|
+
av_freep(&outData[0]);
|
|
581
|
+
av_freep(&outData);
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
int newBytes = converted * bytes_per_sample * out_ch2;
|
|
587
|
+
maybeCompact();
|
|
588
|
+
size_t oldSize = accumBuf.size();
|
|
589
|
+
accumBuf.resize(oldSize + (size_t)newBytes);
|
|
590
|
+
memcpy(accumBuf.data() + oldSize, outData[0], (size_t)newBytes);
|
|
591
|
+
accumSamples += converted;
|
|
592
|
+
|
|
593
|
+
av_freep(&outData[0]);
|
|
594
|
+
av_freep(&outData);
|
|
595
|
+
av_frame_unref(frame);
|
|
596
|
+
|
|
597
|
+
flushAccumFrames(false);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
av_packet_unref(pkt);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
{
|
|
605
|
+
uint8_t** tailData = nullptr;
|
|
606
|
+
int tailCap = swr_get_delay(swr, encCtx->sample_rate) + 256;
|
|
607
|
+
if (tailCap > 0 && av_samples_alloc_array_and_samples(&tailData, nullptr, out_ch2, tailCap, encCtx->sample_fmt, 0) >= 0) {
|
|
608
|
+
int tailConverted = swr_convert(swr, tailData, tailCap, nullptr, 0);
|
|
609
|
+
if (tailConverted > 0) {
|
|
610
|
+
int tailBytes = tailConverted * bytes_per_sample * out_ch2;
|
|
611
|
+
maybeCompact();
|
|
612
|
+
size_t oldSize = accumBuf.size();
|
|
613
|
+
accumBuf.resize(oldSize + (size_t)tailBytes);
|
|
614
|
+
memcpy(accumBuf.data() + oldSize, tailData[0], (size_t)tailBytes);
|
|
615
|
+
accumSamples += tailConverted;
|
|
616
|
+
}
|
|
617
|
+
av_freep(&tailData[0]);
|
|
618
|
+
av_freep(&tailData);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
flushAccumFrames(true);
|
|
622
|
+
|
|
623
|
+
(void)totalDecodedFrames; (void)totalPacketsFromEncoder;
|
|
624
|
+
|
|
625
|
+
avcodec_send_frame(encCtx, nullptr);
|
|
626
|
+
AVPacket* outPkt2 = av_packet_alloc();
|
|
627
|
+
while (avcodec_receive_packet(encCtx, outPkt2) == 0) {
|
|
628
|
+
flushPackets++;
|
|
629
|
+
outPkt2->stream_index = outStream->index;
|
|
630
|
+
av_packet_rescale_ts(outPkt2, encCtx->time_base, outStream->time_base);
|
|
631
|
+
av_interleaved_write_frame(outFmt, outPkt2);
|
|
632
|
+
av_packet_unref(outPkt2);
|
|
633
|
+
}
|
|
634
|
+
av_packet_free(&outPkt2);
|
|
635
|
+
(void)flushPackets;
|
|
636
|
+
|
|
637
|
+
av_write_trailer(outFmt);
|
|
638
|
+
if (!(outFmt->oformat->flags & AVFMT_NOFILE)) avio_closep(&outFmt->pb);
|
|
639
|
+
|
|
640
|
+
struct stat stOut = {};
|
|
641
|
+
long outputSizeBytes = (stat(outputPath, &stOut) == 0 && S_ISREG(stOut.st_mode)) ? (long)stOut.st_size : -1;
|
|
642
|
+
RCTLogInfo(@"[SherpaOnnxAudioConvert] done outputPath=%s size=%ld", outputPath ? outputPath : "(null)", outputSizeBytes);
|
|
643
|
+
|
|
644
|
+
av_packet_free(&pkt);
|
|
645
|
+
av_frame_free(&frame);
|
|
646
|
+
av_channel_layout_uninit(&resampled->ch_layout);
|
|
647
|
+
av_frame_free(&resampled);
|
|
648
|
+
|
|
649
|
+
swr_free(&swr);
|
|
650
|
+
avcodec_free_context(&encCtx);
|
|
651
|
+
avformat_free_context(outFmt);
|
|
652
|
+
avcodec_free_context(&decCtx);
|
|
653
|
+
avformat_close_input(&inFmt);
|
|
654
|
+
|
|
655
|
+
return std::string("");
|
|
656
|
+
#else
|
|
657
|
+
(void)inputPath; (void)outputPath; (void)formatHint;
|
|
658
|
+
return std::string("FFmpeg not available. Build prebuilts with third_party/ffmpeg_prebuilt/build_ffmpeg_ios.sh.");
|
|
659
|
+
#endif
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
@implementation SherpaOnnxAudioConvert
|
|
663
|
+
|
|
664
|
+
+ (BOOL)convertAudioToWav16k:(NSString *)inputPath
|
|
665
|
+
outputPath:(NSString *)outputPath
|
|
666
|
+
error:(NSError **)error
|
|
667
|
+
{
|
|
668
|
+
std::string err = convertToWav16kMono(inputPath.UTF8String, outputPath.UTF8String);
|
|
669
|
+
if (!err.empty()) {
|
|
670
|
+
if (error) {
|
|
671
|
+
*error = [NSError errorWithDomain:@"SherpaOnnxAudioConvert"
|
|
672
|
+
code:-1
|
|
673
|
+
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithUTF8String:err.c_str()]}];
|
|
674
|
+
}
|
|
675
|
+
return NO;
|
|
676
|
+
}
|
|
677
|
+
return YES;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
+ (BOOL)convertAudioToFormat:(NSString *)inputPath
|
|
681
|
+
outputPath:(NSString *)outputPath
|
|
682
|
+
format:(NSString *)format
|
|
683
|
+
outputSampleRateHz:(int)outputSampleRateHz
|
|
684
|
+
error:(NSError **)error
|
|
685
|
+
{
|
|
686
|
+
std::string err = convertToFormat(inputPath.UTF8String, outputPath.UTF8String, format.UTF8String, outputSampleRateHz);
|
|
687
|
+
if (!err.empty()) {
|
|
688
|
+
if (error) {
|
|
689
|
+
*error = [NSError errorWithDomain:@"SherpaOnnxAudioConvert"
|
|
690
|
+
code:-1
|
|
691
|
+
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithUTF8String:err.c_str()]}];
|
|
692
|
+
}
|
|
693
|
+
return NO;
|
|
694
|
+
}
|
|
695
|
+
return YES;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
@end
|