我正在使用双簧管的RhytmGame例子作为一个指南来实现双簧管和mp3解码使用FFmpeg
到我的应用程序。由于我对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));
在这里打电话:
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;
}
}
我尝试过用文件替换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
发布于 2020-01-09 23:15:01
这与使用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;
}
}
https://stackoverflow.com/questions/59640862
复制