前面一章我們講解了解復(fù)用的實現(xiàn)流程,但并沒有詳細(xì)講解解碼器部分的處理,這一章我們將會介紹音頻解碼器以及視頻解碼器的實現(xiàn)。
準(zhǔn)備解碼器
準(zhǔn)備解碼器的流程一般如下:
- 創(chuàng)建解碼上下文
- 從解復(fù)用上下文中復(fù)制參數(shù)到解碼上下文
- 根據(jù)解碼上下文的id查找解碼器,如果在播放器指定了實際的解碼器名稱,則需要根據(jù)指定的解碼器名稱查找解碼器
- 給解碼上下文設(shè)置一些解碼參數(shù),比如lowres、refcounted_frames等解碼參數(shù)
- 打開解碼器
- 如果成功打開解碼器,則根據(jù)類型創(chuàng)建解碼器類,AudioDecoder或者是VideoDecoder。
- 如果不成功,則需要釋放解碼上下文
準(zhǔn)備解碼器的完整代碼如下:
int MediaPlayer::prepareDecoder(int streamIndex) {
AVCodecContext *avctx;
AVCodec *codec;
AVDictionary *opts = NULL;
AVDictionaryEntry *t = NULL;
int ret = 0;
const char *forcedCodecName = NULL;
if (streamIndex < 0 || streamIndex >= pFormatCtx->nb_streams) {
return -1;
}
// 創(chuàng)建解碼上下文
avctx = avcodec_alloc_context3(NULL);
if (!avctx) {
return AVERROR(ENOMEM);
}
do {
// 復(fù)制解碼上下文參數(shù)
ret = avcodec_parameters_to_context(avctx, pFormatCtx->streams[streamIndex]->codecpar);
if (ret < 0) {
break;
}
// 設(shè)置時鐘基準(zhǔn)
av_codec_set_pkt_timebase(avctx, pFormatCtx->streams[streamIndex]->time_base);
// 查找解碼器
codec = avcodec_find_decoder(avctx->codec_id);
// 指定解碼器
switch(avctx->codec_type) {
case AVMEDIA_TYPE_AUDIO: {
forcedCodecName = playerState->audioCodecName;
break;
}
case AVMEDIA_TYPE_VIDEO: {
forcedCodecName = playerState->videoCodecName;
break;
}
}
if (forcedCodecName) {
codec = avcodec_find_decoder_by_name(forcedCodecName);
}
// 判斷是否成功得到解碼器
if (!codec) {
av_log(NULL, AV_LOG_WARNING,
"No codec could be found with id %d\n", avctx->codec_id);
ret = AVERROR(EINVAL);
break;
}
avctx->codec_id = codec->id;
// 設(shè)置一些播放參數(shù)
int stream_lowres = playerState->lowres;
if (stream_lowres > av_codec_get_max_lowres(codec)) {
av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n",
av_codec_get_max_lowres(codec));
stream_lowres = av_codec_get_max_lowres(codec);
}
av_codec_set_lowres(avctx, stream_lowres);
#if FF_API_EMU_EDGE
if (stream_lowres) {
avctx->flags |= CODEC_FLAG_EMU_EDGE;
}
#endif
if (playerState->fast) {
avctx->flags2 |= AV_CODEC_FLAG2_FAST;
}
#if FF_API_EMU_EDGE
if (codec->capabilities & AV_CODEC_CAP_DR1) {
avctx->flags |= CODEC_FLAG_EMU_EDGE;
}
#endif
opts = filterCodecOptions(playerState->codec_opts, avctx->codec_id, pFormatCtx, pFormatCtx->streams[streamIndex], codec);
if (!av_dict_get(opts, "threads", NULL, 0)) {
av_dict_set(&opts, "threads", "auto", 0);
}
if (stream_lowres) {
av_dict_set_int(&opts, "lowres", stream_lowres, 0);
}
if (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
av_dict_set(&opts, "refcounted_frames", "1", 0);
}
// 打開解碼器
if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) {
break;
}
if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
ret = AVERROR_OPTION_NOT_FOUND;
break;
}
// 根據(jù)解碼器類型創(chuàng)建解碼器
pFormatCtx->streams[streamIndex]->discard = AVDISCARD_DEFAULT;
switch (avctx->codec_type) {
case AVMEDIA_TYPE_AUDIO: {
audioDecoder = new AudioDecoder(avctx, pFormatCtx->streams[streamIndex],
streamIndex, playerState);
break;
}
case AVMEDIA_TYPE_VIDEO: {
videoDecoder = new VideoDecoder(pFormatCtx, avctx, pFormatCtx->streams[streamIndex],
streamIndex, playerState);
attachmentRequest = 1;
break;
}
default:{
break;
}
}
} while (false);
// 準(zhǔn)備失敗,則需要釋放創(chuàng)建的解碼上下文
if (ret < 0) {
if (playerCallback != NULL) {
playerCallback->onError(0x01, "failed to open stream!");
}
avcodec_free_context(&avctx);
}
// 釋放參數(shù)
av_dict_free(&opts);
return ret;
}
解碼器類
目前播放器支持音頻解碼和視頻解碼,暫不支持字幕解碼,可以根據(jù)需要自行實現(xiàn)。為了方便使用,我封裝了AudioDecoder和VideoDecoder類。下面介紹解碼器類的實現(xiàn)。
MediaDecoder 解碼器基類
MediaDecoder基類主要封裝一些基本的數(shù)據(jù),比如媒體流、媒體流索引、待解碼的數(shù)據(jù)包隊列以及獲取數(shù)據(jù)包隊列的內(nèi)存大小、隊列緩沖的時長等數(shù)據(jù)。整個解碼器基類的代碼如下:
class MediaDecoder : public Runnable {
public:
MediaDecoder(AVCodecContext *avctx, AVStream *stream, int streamIndex, PlayerState *playerState);
virtual ~MediaDecoder();
virtual void start();
virtual void stop();
virtual void flush();
int pushPacket(AVPacket *pkt);
int pushNullPacket();
int getPacketSize();
int getStreamIndex();
AVStream *getStream();
AVCodecContext *getCodecContext();
int getMemorySize();
int hasEnoughPackets();
virtual void run();
protected:
Mutex mMutex;
Condition mCondition;
PlayerState *playerState;
PacketQueue *packetQueue; // 數(shù)據(jù)包隊列
AVCodecContext *pCodecCtx;
AVStream *pStream;
int streamIndex;
};
MediaDecoder::MediaDecoder(AVCodecContext *avctx, AVStream *stream, int streamIndex, PlayerState *playerState) {
packetQueue = new PacketQueue();
this->pCodecCtx = avctx;
this->pStream = stream;
this->streamIndex = streamIndex;
this->playerState = playerState;
}
MediaDecoder::~MediaDecoder() {
ALOGI("MediaDecoder destructor");
stop();
if (packetQueue) {
packetQueue->flush();
delete packetQueue;
packetQueue = NULL;
}
if (pCodecCtx) {
avcodec_close(pCodecCtx);
avcodec_free_context(&pCodecCtx);
pCodecCtx = NULL;
}
playerState = NULL;
}
void MediaDecoder::start() {
if (packetQueue) {
packetQueue->start();
}
}
void MediaDecoder::stop() {
if (packetQueue) {
packetQueue->abort();
}
}
void MediaDecoder::flush() {
if (packetQueue) {
packetQueue->flush();
}
}
int MediaDecoder::pushPacket(AVPacket *pkt) {
if (packetQueue) {
return packetQueue->pushPacket(pkt);
}
}
int MediaDecoder::pushNullPacket() {
if (packetQueue != NULL) {
return packetQueue->pushNullPacket(streamIndex);
}
return -1;
}
int MediaDecoder::getPacketSize() {
return packetQueue ? packetQueue->getPacketSize() : 0;
}
int MediaDecoder::getStreamIndex() {
return streamIndex;
}
AVStream *MediaDecoder::getStream() {
return pStream;
}
AVCodecContext *MediaDecoder::getCodecContext() {
return pCodecCtx;
}
int MediaDecoder::getMemorySize() {
return packetQueue ? packetQueue->getSize() : 0;
}
int MediaDecoder::hasEnoughPackets() {
Mutex::Autolock lock(mMutex);
return (packetQueue == NULL) || (packetQueue->isAbort())
|| (pStream->disposition & AV_DISPOSITION_ATTACHED_PIC)
|| (packetQueue->getPacketSize() > MIN_FRAMES)
&& (!packetQueue->getDuration()
|| av_q2d(pStream->time_base) * packetQueue->getDuration() > 1.0);
}
void MediaDecoder::run() {
// do nothing
}
MediaDecoder 解碼基類用到了PacketQueue,PacketQueue是一個存放數(shù)據(jù)包的隊列,其代碼如下:
typedef struct PacketList {
AVPacket pkt;
struct PacketList *next;
} PacketList;
/**
* 備注:這里不用std::queue是為了方便計算隊列占用內(nèi)存和隊列的時長,在解碼的時候要用到
*/
class PacketQueue {
public:
PacketQueue();
virtual ~PacketQueue();
// 入隊數(shù)據(jù)包
int pushPacket(AVPacket *pkt);
// 入隊空數(shù)據(jù)包
int pushNullPacket(int stream_index);
// 刷新
void flush();
// 終止
void abort();
// 開始
void start();
// 獲取數(shù)據(jù)包
int getPacket(AVPacket *pkt);
// 獲取數(shù)據(jù)包
int getPacket(AVPacket *pkt, int block);
int getPacketSize();
int getSize();
int64_t getDuration();
int isAbort();
private:
int put(AVPacket *pkt);
private:
Mutex mMutex;
Condition mCondition;
PacketList *first_pkt, *last_pkt;
int nb_packets;
int size;
int64_t duration;
int abort_request;
};
PacketQueue::PacketQueue() {
abort_request = 0;
first_pkt = NULL;
last_pkt = NULL;
nb_packets = 0;
size = 0;
duration = 0;
}
PacketQueue::~PacketQueue() {
abort();
flush();
}
/**
* 入隊數(shù)據(jù)包
* @param pkt
* @return
*/
int PacketQueue::put(AVPacket *pkt) {
PacketList *pkt1;
if (abort_request) {
return -1;
}
pkt1 = (PacketList *) av_malloc(sizeof(PacketList));
if (!pkt1) {
return -1;
}
pkt1->pkt = *pkt;
pkt1->next = NULL;
if (!last_pkt) {
first_pkt = pkt1;
} else {
last_pkt->next = pkt1;
}
last_pkt = pkt1;
nb_packets++;
size += pkt1->pkt.size + sizeof(*pkt1);
duration += pkt1->pkt.duration;
return 0;
}
/**
* 入隊數(shù)據(jù)包
* @param pkt
* @return
*/
int PacketQueue::pushPacket(AVPacket *pkt) {
int ret;
mMutex.lock();
ret = put(pkt);
mCondition.signal();
mMutex.unlock();
if (ret < 0) {
av_packet_unref(pkt);
}
return ret;
}
int PacketQueue::pushNullPacket(int stream_index) {
AVPacket pkt1, *pkt = &pkt1;
av_init_packet(pkt);
pkt->data = NULL;
pkt->size = 0;
pkt->stream_index = stream_index;
return pushPacket(pkt);
}
/**
* 刷新數(shù)據(jù)包
*/
void PacketQueue::flush() {
PacketList *pkt, *pkt1;
mMutex.lock();
for (pkt = first_pkt; pkt; pkt = pkt1) {
pkt1 = pkt->next;
av_packet_unref(&pkt->pkt);
av_freep(&pkt);
}
last_pkt = NULL;
first_pkt = NULL;
nb_packets = 0;
size = 0;
duration = 0;
mCondition.signal();
mMutex.unlock();
}
/**
* 隊列終止
*/
void PacketQueue::abort() {
mMutex.lock();
abort_request = 1;
mCondition.signal();
mMutex.unlock();
}
/**
* 隊列開始
*/
void PacketQueue::start() {
mMutex.lock();
abort_request = 0;
mCondition.signal();
mMutex.unlock();
}
/**
* 取出數(shù)據(jù)包
* @param pkt
* @return
*/
int PacketQueue::getPacket(AVPacket *pkt) {
return getPacket(pkt, 1);
}
/**
* 取出數(shù)據(jù)包
* @param pkt
* @param block
* @return
*/
int PacketQueue::getPacket(AVPacket *pkt, int block) {
PacketList *pkt1;
int ret;
mMutex.lock();
for (;;) {
if (abort_request) {
ret = -1;
break;
}
pkt1 = first_pkt;
if (pkt1) {
first_pkt = pkt1->next;
if (!first_pkt) {
last_pkt = NULL;
}
nb_packets--;
size -= pkt1->pkt.size + sizeof(*pkt1);
duration -= pkt1->pkt.duration;
*pkt = pkt1->pkt;
av_free(pkt1);
ret = 1;
break;
} else if (!block) {
ret = 0;
break;
} else {
mCondition.wait(mMutex);
}
}
mMutex.unlock();
return ret;
}
int PacketQueue::getPacketSize() {
Mutex::Autolock lock(mMutex);
return nb_packets;
}
int PacketQueue::getSize() {
return size;
}
int64_t PacketQueue::getDuration() {
return duration;
}
int PacketQueue::isAbort() {
return abort_request;
}
數(shù)據(jù)包隊列并沒有使用std::queue,這是為了方便得到緩沖的內(nèi)存大小以及緩沖的數(shù)據(jù)包時長。由于每個數(shù)據(jù)包(AVPacket)的數(shù)據(jù)大小不一致,時長也不一致,如果緩沖的數(shù)據(jù)包過小,則會導(dǎo)致播放卡頓等現(xiàn)象,比如后臺播放視頻時,如果緩沖的時長過短,則OpenSLES播放音頻可能會出現(xiàn)卡頓、雜音等現(xiàn)象。
AudioDecoder 音頻解碼器類
音頻解碼器類AudioDecoder 繼承于MediaDecoder,并且封裝了解碼方法decodeAudio(Frame *af)。該方法主要的作用在于將數(shù)據(jù)包解碼得到AVFrame之后,將數(shù)據(jù)復(fù)制到Frame結(jié)構(gòu)體中,并計算幀的pts等數(shù)據(jù)。整個AudioDecoder的代碼如下:
class AudioDecoder : public MediaDecoder {
public:
AudioDecoder(AVCodecContext *avctx, AVStream *stream, int streamIndex, PlayerState *playerState);
virtual ~AudioDecoder();
int getAudioFrame(AVFrame *frame);
private:
AVPacket *packet;
int64_t next_pts;
AVRational next_pts_tb;
};
AudioDecoder::AudioDecoder(AVCodecContext *avctx, AVStream *stream, int streamIndex, PlayerState *playerState)
: MediaDecoder(avctx, stream, streamIndex, playerState) {
packet = av_packet_alloc();
}
AudioDecoder::~AudioDecoder() {
stop();
if (packet) {
av_packet_free(&packet);
av_freep(&packet);
packet = NULL;
}
ALOGI("AudioDecoder destructor");
}
int AudioDecoder::getAudioFrame(AVFrame *frame) {
int got_frame = 0;
int ret = 0;
if (!frame) {
return AVERROR(ENOMEM);
}
av_frame_unref(frame);
do {
if (packetQueue->getPacket(packet) < 0) {
return -1;
}
ret = avcodec_send_packet(pCodecCtx, packet);
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
av_packet_unref(packet);
continue;
}
ret = avcodec_receive_frame(pCodecCtx, frame);
// 釋放數(shù)據(jù)包的引用,防止內(nèi)存泄漏
av_packet_unref(packet);
if (ret < 0) {
av_frame_unref(frame);
got_frame = 0;
continue;
} else {
got_frame = 1;
// 這里要重新計算frame的pts 否則會導(dǎo)致網(wǎng)絡(luò)視頻出現(xiàn)pts 對不上的情況
AVRational tb = (AVRational){1, frame->sample_rate};
if (frame->pts != AV_NOPTS_VALUE) {
frame->pts = av_rescale_q(frame->pts, av_codec_get_pkt_timebase(pCodecCtx), tb);
} else if (next_pts != AV_NOPTS_VALUE) {
frame->pts = av_rescale_q(next_pts, next_pts_tb, tb);
}
if (frame->pts != AV_NOPTS_VALUE) {
next_pts = frame->pts + frame->nb_samples;
next_pts_tb = tb;
}
}
} while (!got_frame);
return got_frame;
}
音頻解碼器這里我們并不需要用一個幀隊列緩存數(shù)據(jù),因為我們默認(rèn)是同步到音頻時鐘的,而音頻流的解碼重采樣等處理比視頻處理要快很多,因此,音頻解碼并不一定要一個音頻幀隊列緩存數(shù)據(jù)。尤其是對于本地音視頻文件,就更不需要了,音頻隊列的數(shù)據(jù)都是連貫的。這里跟ffplay的流程就不同了,ijkplayer 和 ffplay在音頻解碼器都是另開一個解碼線程并且將解碼得到的數(shù)據(jù)存放到解碼隊列中,等待音頻輸出設(shè)備從解碼隊列取出音頻幀做重采樣輸出處理。這里有個注意的地方,那就是解碼得到的音頻幀需要重新計算pts,對于網(wǎng)絡(luò)視頻流來說,解碼得到的pts 有可能不正確,這樣會導(dǎo)致音視頻不同步的情況發(fā)生。
VideoDecoder 視頻解碼器類
視頻解碼器類繼承與MediaDecoder,在調(diào)用開始解碼方法后,打開一個解碼線程,解碼線程不斷從待解碼數(shù)據(jù)包隊列中取出數(shù)據(jù)包解碼,并且將解碼得到的幀放入幀隊列中。
class VideoDecoder : public MediaDecoder {
public:
VideoDecoder(AVFormatContext *pFormatCtx, AVCodecContext *avctx,
AVStream *stream, int streamIndex, PlayerState *playerState);
virtual ~VideoDecoder();
void setMasterClock(MediaClock *masterClock);
void start() override;
void stop() override;
void flush() override;
int getFrameSize();
FrameQueue *getFrameQueue();
void run() override;
private:
// 解碼視頻幀
int decodeVideo();
private:
AVFormatContext *pFormatCtx; // 解復(fù)用上下文
FrameQueue *frameQueue; // 幀隊列
Thread *decodeThread; // 解碼線程
MediaClock *masterClock; // 主時鐘
};
VideoDecoder::VideoDecoder(AVFormatContext *pFormatCtx, AVCodecContext *avctx,
AVStream *stream, int streamIndex, PlayerState *playerState)
: MediaDecoder(avctx, stream, streamIndex, playerState) {
this->pFormatCtx = pFormatCtx;
frameQueue = new FrameQueue(VIDEO_QUEUE_SIZE, 1);
decodeThread = NULL;
masterClock = NULL;
}
VideoDecoder::~VideoDecoder() {
ALOGI("VideoDecoder destructor");
stop();
mMutex.lock();
pFormatCtx = NULL;
if (frameQueue) {
frameQueue->flush();
delete frameQueue;
frameQueue = NULL;
}
masterClock = NULL;
mMutex.unlock();
}
void VideoDecoder::setMasterClock(MediaClock *masterClock) {
Mutex::Autolock lock(mMutex);
this->masterClock = masterClock;
}
void VideoDecoder::start() {
MediaDecoder::start();
if (frameQueue) {
frameQueue->start();
}
if (!decodeThread) {
decodeThread = new Thread(this);
decodeThread->start();
}
}
void VideoDecoder::stop() {
MediaDecoder::stop();
if (frameQueue) {
frameQueue->abort();
}
if (decodeThread) {
decodeThread->join();
delete decodeThread;
decodeThread = NULL;
}
}
void VideoDecoder::flush() {
MediaDecoder::flush();
if (frameQueue) {
frameQueue->flush();
}
}
int VideoDecoder::getFrameSize() {
Mutex::Autolock lock(mMutex);
return frameQueue ? frameQueue->getFrameSize() : 0;
}
FrameQueue *VideoDecoder::getFrameQueue() {
Mutex::Autolock lock(mMutex);
return frameQueue;
}
void VideoDecoder::run() {
decodeVideo();
}
/**
* 解碼視頻數(shù)據(jù)包并放入幀隊列
* @return
*/
int VideoDecoder::decodeVideo() {
AVFrame *frame = av_frame_alloc();
Frame *vp;
int got_picture;
int ret = 0;
AVRational tb = pStream->time_base;
AVRational frame_rate = av_guess_frame_rate(pFormatCtx, pStream, NULL);
if (!frame) {
return AVERROR(ENOMEM);
}
AVPacket *packet = av_packet_alloc();
if (!packet) {
return AVERROR(ENOMEM);
}
for (;;) {
if (playerState->abortRequest) {
ret = -1;
break;
}
if (packetQueue->getPacket(packet) < 0) {
return -1;
}
// 送去解碼
ret = avcodec_send_packet(pCodecCtx, packet);
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
av_packet_unref(packet);
continue;
}
// 得到解碼幀
ret = avcodec_receive_frame(pCodecCtx, frame);
if (ret < 0 && ret != AVERROR_EOF) {
av_frame_unref(frame);
av_packet_unref(packet);
continue;
} else {
got_picture = 1;
// 丟幀處理
if (masterClock != NULL) {
double dpts = NAN;
if (frame->pts != AV_NOPTS_VALUE) {
dpts = av_q2d(pStream->time_base) * frame->pts;
}
// 計算視頻幀的長寬比
frame->sample_aspect_ratio = av_guess_sample_aspect_ratio(pFormatCtx, pStream,
frame);
// 是否需要做舍幀操作
if (playerState->frameDrop > 0 ||
(playerState->frameDrop > 0 && playerState->syncType != AV_SYNC_VIDEO)) {
if (frame->pts != AV_NOPTS_VALUE) {
double diff = dpts - masterClock->getClock();
if (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD &&
diff < 0 && packetQueue->getPacketSize() > 0) {
av_frame_unref(frame);
got_picture = 0;
}
}
}
}
}
if (got_picture) {
// 取出幀
if (!(vp = frameQueue->peekWritable())) {
ret = -1;
break;
}
// 復(fù)制參數(shù)
vp->uploaded = 0;
vp->width = frame->width;
vp->height = frame->height;
vp->format = frame->format;
vp->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
vp->duration = frame_rate.num && frame_rate.den
? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0;
av_frame_move_ref(vp->frame, frame);
// 入隊幀
frameQueue->pushFrame();
}
// 釋放數(shù)據(jù)包和緩沖幀的引用,防止內(nèi)存泄漏
av_frame_unref(frame);
av_packet_unref(packet);
}
av_frame_free(&frame);
av_free(frame);
frame = NULL;
av_packet_free(&packet);
av_free(packet);
packet = NULL;
ALOGD("video decode thread exit!");
return ret;
}
視頻解碼器用到了視頻幀隊列FrameQueue,F(xiàn)rameQueue是一個用數(shù)組實現(xiàn)的環(huán)形隊列,并在創(chuàng)建是指定最大的存放數(shù)據(jù)(小于某個指定值)。其實現(xiàn)代碼如下:
#define FRAME_QUEUE_SIZE 10
typedef struct Frame {
AVFrame *frame;
AVSubtitle sub;
double pts; /* presentation timestamp for the frame */
double duration; /* estimated duration of the frame */
int width;
int height;
int format;
int uploaded;
} Frame;
class FrameQueue {
public:
FrameQueue(int max_size, int keep_last);
virtual ~FrameQueue();
void start();
void abort();
Frame *currentFrame();
Frame *nextFrame();
Frame *lastFrame();
Frame *peekWritable();
void pushFrame();
void popFrame();
void flush();
int getFrameSize();
int getShowIndex() const;
private:
void unrefFrame(Frame *vp);
private:
Mutex mMutex;
Condition mCondition;
int abort_request;
Frame queue[FRAME_QUEUE_SIZE];
int rindex;
int windex;
int size;
int max_size;
int keep_last;
int show_index;
};
FrameQueue::FrameQueue(int max_size, int keep_last) {
memset(queue, 0, sizeof(Frame) * FRAME_QUEUE_SIZE);
this->max_size = FFMIN(max_size, FRAME_QUEUE_SIZE);
this->keep_last = (keep_last != 0);
for (int i = 0; i < this->max_size; ++i) {
queue[i].frame = av_frame_alloc();
}
abort_request = 1;
rindex = 0;
windex = 0;
size = 0;
show_index = 0;
}
FrameQueue::~FrameQueue() {
for (int i = 0; i < max_size; ++i) {
Frame *vp = &queue[i];
unrefFrame(vp);
av_frame_free(&vp->frame);
}
}
void FrameQueue::start() {
mMutex.lock();
abort_request = 0;
mCondition.signal();
mMutex.unlock();
}
void FrameQueue::abort() {
mMutex.lock();
abort_request = 1;
mCondition.signal();
mMutex.unlock();
}
Frame *FrameQueue::currentFrame() {
return &queue[(rindex + show_index) % max_size];
}
Frame *FrameQueue::nextFrame() {
return &queue[(rindex + show_index + 1) % max_size];
}
Frame *FrameQueue::lastFrame() {
return &queue[rindex];
}
Frame *FrameQueue::peekWritable() {
mMutex.lock();
while (size >= max_size && !abort_request) {
mCondition.wait(mMutex);
}
mMutex.unlock();
if (abort_request) {
return NULL;
}
return &queue[windex];
}
void FrameQueue::pushFrame() {
if (++windex == max_size) {
windex = 0;
}
mMutex.lock();
size++;
mCondition.signal();
mMutex.unlock();
}
void FrameQueue::popFrame() {
if (keep_last && !show_index) {
show_index = 1;
return;
}
unrefFrame(&queue[rindex]);
if (++rindex == max_size) {
rindex = 0;
}
mMutex.lock();
size--;
mCondition.signal();
mMutex.unlock();
}
void FrameQueue::flush() {
while (getFrameSize() > 0) {
popFrame();
}
}
int FrameQueue::getFrameSize() {
return size - show_index;
}
void FrameQueue::unrefFrame(Frame *vp) {
av_frame_unref(vp->frame);
avsubtitle_free(&vp->sub);
}
int FrameQueue::getShowIndex() const {
return show_index;
}
至此,我們就創(chuàng)建好了音頻解碼器和視頻解碼器,以及封裝了解碼隊列。
完整代碼請參考本人的播放器項目:CainPlayer