本文参考自:ffmpeg 从内存中读取数据(原创:雷霄骅)
需求
最近做一个项目需要播放一些简单的音频,而这些音频以二进制的形式存储在数据库中。而且需要保证音频的音频播放的实时性。故研究下如何从内存数据中解码音频。
开发
原帖中给的代码已经十分详细了。不过有几个需要注意的点原帖中并未提及。
- 回调函数
filliobuffer
中的第一个参数
回调函数内存读取的回调函数中第一个参数其实是在int fillIiobuffer(void * opaque, uint8_t * buf, int bufsize)
avio_alloc_context
函数中第四个参数设置的。
在回调函数中回将指针传回。这里可以用来标记一些自定义信息。如在我的实际项目代码中用它来传递数据。
由于回调函数写成了一个全局函数,而其他操作封装到了一个类里面。这里需要将用来存储实际未解码内存数据的成员变量以指针的形式传入回调函数中。int fillIiobuffer(void * opaque, uint8_t * buf, int bufsize) { QByteArray* ba = static_cast<QByteArray*>(opaque); if (ba == nullptr || ba->isEmpty()) { return 0; } int realSize = 0; if (bufsize > ba->size()) { realSize = ba->size(); memset(buf,0,bufsize); } else { realSize = bufsize; } if (!memcpy_s(buf, realSize, ba->data(), realSize)) { ba->remove(0, realSize); return realSize; } return 0; }
具体是这样写的。bool XRealTimePlayer::openAudioBuf(QByteArray ba) { m_AudioData = ba; ... AVIOContext *avio = avio_alloc_context(m_iobuffer, 32768, 0, &m_AudioData, fillIiobuffer, nullptr, nullptr); m_AVFormatFromBuf->pb = avio; ... }
- 内存的释放
因为实例需要反复使用,这里就需要进行一些内存释放工作。
经过反复测试发现只有这样做才不会出错,具体为什么,对不对。我也知道,反正目前没有发现内存泄漏。
应该有些人会注意到,我申请的m_ioBuffer并没有释放。而且这个空间还不小。但它就是没有引起内存泄漏,就是这么神奇。if (m_AVFormatFromBuf) { //av_free(m_iobuffer); //注意这里 avio_context_free(&m_AVFormatFromBuf->pb); avformat_close_input(&m_AVFormatFromBuf); avformat_free_context(m_AVFormatFromBuf); } m_iobuffer = (unsigned char *)av_malloc(32768); m_AVFormatFromBuf = avformat_alloc_context(); AVIOContext *avio = avio_alloc_context(m_iobuffer, 32768, 0, &m_AudioData, fillIiobuffer, nullptr, nullptr); m_AVFormatFromBuf->pb = avio;
如果我把它释放了,就是把//av_free(m_iobuffer);
这句注释打开的话。在第三次播放的时候必然堆损坏。不知道为什么。
在源码中对avio_alloc_context
函数第一个参数的注释是这样的。
大概意思就是这个参数可能会被一个新的叫/* * @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(). * /
libavformat.AVIOContext.buffer
的内存空间取代。而且这个libavformat.AVIOContext.buffer
需要进行释放。
因此经过再次测试我们需要释放libavformat.AVIOContext.buffer
这个变量。
所以释放空间的代码需要这么写if (m_AVFormatFromBuf) { //av_free(m_iobuffer); //注意这里 av_free(m_AVFormatFromBuf->pb->buffer); avio_context_free(&m_AVFormatFromBuf->pb); avformat_close_input(&m_AVFormatFromBuf); avformat_free_context(m_AVFormatFromBuf); } m_iobuffer = (unsigned char *)av_malloc(32768); m_AVFormatFromBuf = avformat_alloc_context(); AVIOContext *avio = avio_alloc_context(m_iobuffer, 32768, 0, &m_AudioData, fillIiobuffer, nullptr, nullptr); m_AVFormatFromBuf->pb = avio;