2

我使用双簧管的 RhytmGame 示例作为指导,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));

调用在这里:

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,readseek参数,以便我可以使用存储中的文件而不是 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

4

1 回答 1

0

这与使用 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;
    }
}
于 2020-01-10T07:15:01.767 回答