Telegram-iOS/submodules/FFMpegBinding/Sources/FFMpegSWResample.m
2026-04-21 22:04:47 +04:00

154 lines
4.9 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#import <FFMpegBinding/FFMpegSWResample.h>
#import <FFMpegBinding/FFMpegAVFrame.h>
#import "libavformat/avformat.h"
#import "libavcodec/avcodec.h"
#import "libswresample/swresample.h"
@interface FFMpegSWResample () {
int _sourceSampleRate;
FFMpegAVSampleFormat _sourceSampleFormat;
int _destinationChannelCount;
int _destinationSampleRate;
FFMpegAVSampleFormat _destinationSampleFormat;
int _currentSourceChannelCount;
SwrContext *_context;
NSUInteger _ratio;
void *_buffer;
int _bufferSize;
}
@end
@implementation FFMpegSWResample
- (instancetype)initWithSourceChannelCount:(NSInteger)sourceChannelCount sourceSampleRate:(NSInteger)sourceSampleRate sourceSampleFormat:(enum FFMpegAVSampleFormat)sourceSampleFormat destinationChannelCount:(NSInteger)destinationChannelCount destinationSampleRate:(NSInteger)destinationSampleRate destinationSampleFormat:(enum FFMpegAVSampleFormat)destinationSampleFormat {
self = [super init];
if (self != nil) {
_sourceSampleRate = (int)sourceSampleRate;
_sourceSampleFormat = sourceSampleFormat;
_destinationChannelCount = (int)destinationChannelCount;
_destinationSampleRate = (int)destinationSampleRate;
_destinationSampleFormat = destinationSampleFormat;
_currentSourceChannelCount = -1;
}
return self;
}
- (void)dealloc {
if (_context) {
swr_free(&_context);
}
if (_buffer) {
free(_buffer);
}
}
- (void)resetContextForChannelCount:(int)channelCount {
if (_context) {
swr_free(&_context);
_context = NULL;
}
#if LIBAVFORMAT_VERSION_MAJOR >= 59
AVChannelLayout channelLayoutIn;
av_channel_layout_default(&channelLayoutIn, channelCount);
AVChannelLayout channelLayoutOut;
av_channel_layout_default(&channelLayoutOut, _destinationChannelCount);
if (swr_alloc_set_opts2(
&_context,
&channelLayoutOut,
(enum AVSampleFormat)_destinationSampleFormat,
(int)_destinationSampleRate,
&channelLayoutIn,
(enum AVSampleFormat)_sourceSampleFormat,
(int)_sourceSampleRate,
0,
NULL
) != 0) {
return;
}
#else
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
_context = swr_alloc_set_opts(NULL,
av_get_default_channel_layout((int)_destinationChannelCount),
(enum AVSampleFormat)_destinationSampleFormat,
(int)_destinationSampleRate,
av_get_default_channel_layout(channelCount),
(enum AVSampleFormat)_sourceSampleFormat,
(int)_sourceSampleRate,
0,
NULL);
#pragma clang diagnostic pop
#endif
_currentSourceChannelCount = channelCount;
_ratio = MAX(1, _destinationSampleRate / MAX(_sourceSampleRate, 1)) * MAX(1, _destinationChannelCount / channelCount) * 2;
if (_context) {
swr_init(_context);
}
}
- (NSData * _Nullable)resample:(FFMpegAVFrame *)frame {
AVFrame *frameImpl = (AVFrame *)[frame impl];
#if LIBAVFORMAT_VERSION_MAJOR >= 59
int numChannels = frameImpl->ch_layout.nb_channels;
#else
int numChannels = frameImpl->channels;
#endif
if (numChannels != _currentSourceChannelCount) {
[self resetContextForChannelCount:numChannels];
}
if (!_context) {
return nil;
}
int64_t outSamples = (int64_t)frameImpl->nb_samples * (int64_t)_ratio;
// Cap at 8M samples per frame: ~10× larger than any legitimate codec
// frame × resample ratio, bounds the per-frame buffer to ~32 MB (s16 stereo),
// and rejects pathological inputs (e.g. 1 Hz source with a 65 KB FLAC block).
if (outSamples <= 0 || outSamples > 8 * 1024 * 1024) {
return nil;
}
int bufSize = av_samples_get_buffer_size(NULL,
(int)_destinationChannelCount,
(int)outSamples,
(enum AVSampleFormat)_destinationSampleFormat,
1);
if (bufSize <= 0) {
return nil;
}
if (!_buffer || _bufferSize < bufSize) {
void *newBuffer = realloc(_buffer, bufSize);
if (!newBuffer) {
return nil;
}
_buffer = newBuffer;
_bufferSize = bufSize;
}
Byte *outbuf[2] = { _buffer, 0 };
int numFrames = swr_convert(_context,
outbuf,
(int)outSamples,
(const uint8_t **)frameImpl->data,
frameImpl->nb_samples);
if (numFrames <= 0) {
return nil;
}
return [[NSData alloc] initWithBytes:_buffer length:numFrames * _destinationChannelCount * 2];
}
@end