我使用Oboe's RhytmGame example作为指导,使用FFmpeg
将双簧管和mp3解码应用到我的应用中。由于我是NDK和C++初学者的新手,所以我仍然难以解决遇到的一些基本概念。
我的问题:上述示例仅使用Android的AssetManager的本机实现处理资产文件夹中的文件。
由于我要访问外部存储上的文件,因此必须更改此设置,但是我不清楚如何执行此操作。
这就是我被困的地方:
我有一个FFmpegExtractor
类,它在FFmpeg's avio.h
中调用此方法:
* Allocate and initialize an AVIOContext for buffered I/O. It must be later
* freed with avio_context_free().
*
* @param buffer Memory block for input/output operations via AVIOContext.
* The buffer must be allocated with av_malloc() and friends.
* It may be freed and replaced with a new buffer by libavformat.
* AVIOContext.buffer holds the buffer currently in use,
* which must be later freed with av_free().
* @param buffer_size The buffer size is very important for performance.
* For protocols with fixed blocksize it should be set to this blocksize.
* For others a typical size is a cache page, e.g. 4kb.
* @param write_flag Set to 1 if the buffer should be writable, 0 otherwise.
* @param opaque An opaque pointer to user-specific data.
* @param read_packet A function for refilling the buffer, may be NULL.
* For stream protocols, must never return 0 but rather
* a proper AVERROR code.
* @param write_packet A function for writing the buffer contents, may be NULL.
* The function may not change the input buffers content.
* @param seek A function for seeking to specified byte position, may be NULL.
*
* @return Allocated AVIOContext or NULL on failure.
*/
AVIOContext *avio_alloc_context(
unsigned char *buffer,
int buffer_size,
int write_flag,
void *opaque,
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
int64_t (*seek)(void *opaque, int64_t offset, int whence));
call 在这里进行:
bool FFMpegExtractor::createAVIOContext(AAsset *asset, uint8_t *buffer, uint32_t bufferSize,
AVIOContext **avioContext) {
constexpr int isBufferWriteable = 0;
*avioContext = avio_alloc_context(
buffer, // internal buffer for FFmpeg to use
bufferSize, // For optimal decoding speed this should be the protocol block size
isBufferWriteable,
asset, // Will be passed to our callback functions as a (void *)
read, // Read callback function
nullptr, // Write callback function (not used)
seek); // Seek callback function
if (*avioContext == nullptr){
LOGE("Failed to create AVIO context");
return false;
} else {
return true;
}
}
我正在寻找替换
asset
,read
和seek
参数,以便可以使用存储中的文件而不是AAsset对象。这是上面传递的
read
回调:int read(void *opaque, uint8_t *buf, int buf_size) {
auto asset = (AAsset *) opaque;
int bytesRead = AAsset_read(asset, buf, (size_t)buf_size);
return bytesRead;
}
这是
seek
回调:int64_t seek(void *opaque, int64_t offset, int whence){
auto asset = (AAsset*)opaque;
// See https://www.ffmpeg.org/doxygen/3.0/avio_8h.html#a427ff2a881637b47ee7d7f9e368be63f
if (whence == AVSEEK_SIZE) return AAsset_getLength(asset);
if (AAsset_seek(asset, offset, whence) == -1){
return -1;
} else {
return 0;
}
}
我试过仅用FILE替换AAsset,但是当然不能这样做。我知道如何打开和读取文件,但是我不清楚这是否在这里,以及如何将AAsset中的方法转换为可返回存储中文件所需值的操作。
谁能指出我正确的方向?
编辑:由于无法容纳,这里是我对@BrianChen的有用评论的答复中提到的代码块:
bool FFMpegExtractor::openAVFormatContext(AVFormatContext *avFormatContext) {
int result = avformat_open_input(&avFormatContext,
"", /* URL is left empty because we're providing our own I/O */
nullptr /* AVInputFormat *fmt */,
nullptr /* AVDictionary **options */
);
不幸的是
avformat_open_input()
产生了Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x20 in tid 23767, pid 23513
最佳答案
与使用AAsset相同。
// wrapper class for file stream
class MediaSource {
public:
MediaSource() {
}
~MediaSource() {
source.close();
}
void open(const string& filePath) {
source.open(filePath, ios::in | ios::binary);
}
int read(uint8_t *buffer, int buf_size) {
// read data to buffer
source.read((char *)buffer, buf_size)
// return how many bytes were read
return source.gcount();
}
int64_t seek(int64_t offset, int whence) {
if (whence == AVSEEK_SIZE) {
// FFmpeg needs file size.
int oldPos = source.tellg();
source.seekg(0,ios::end);
int64_t length = source.tellg();
// seek to old pos
source.seekg(oldPos);
return length;
} else if (whence == SEEK_SET) {
// set pos to offset
source.seekg(offset);
} else if (whence == SEEK_CUR) {
// add offset to pos
source.seekg(offset, ios::cur);
} else {
// do not support other flags, return -1
return -1;
}
// return current pos
return source.tellg();
}
private:
ifstream source;
};
// If FFmpeg needs to read the file, it will call this function.
// We need to fill the buffer with file's data.
int read(void *opaque, uint8_t *buffer, int buf_size) {
MediaSource *source = (MediaSource *)opaque;
return source->read(buffer, buf_size);
}
// If FFmpeg needs to seek in the file, it will call this function.
// We need to change the read pos.
int64_t seek(void *opaque, int64_t offset, int whence) {
MediaSource *source = (MediaSource *)opaque;
return source->seek(offset, whence);
}
// add MediaSource to class FFMpegExtractor
class FFMpegExtractor {
private:
// add this line to declare of class FFMpegExtractor
MediaSource* mSource;
};
FFMpegExtractor::FFMpegExtractor() {
// add this line in constructor, new a instance
mSource = new MediaSource;
}
FFMpegExtractor::~FFMpegExtractor() {
// add this line in destructor, release instance
delete mSource;
}
bool FFMpegExtractor::createAVIOContext(const string& filePath, uint8_t *buffer, uint32_t bufferSize,
AVIOContext **avioContext) {
mSource.open(filePath);
constexpr int isBufferWriteable = 0;
*avioContext = avio_alloc_context(
buffer, // internal buffer for FFmpeg to use
bufferSize, // For optimal decoding speed this should be the protocol block size
isBufferWriteable,
mSource, // Will be passed to our callback functions as a (void *)
read, // Read callback function
nullptr, // Write callback function (not used)
seek); // Seek callback function
if (*avioContext == nullptr){
LOGE("Failed to create AVIO context");
return false;
} else {
return true;
}
}