395 lines
12 KiB
C++
395 lines
12 KiB
C++
#include "ff_ffplay_def.h"
|
||
#include "easylogging++.h"
|
||
|
||
AVPacket flush_pkt;
|
||
static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt)
|
||
{
|
||
MyAVPacketList *pkt1;
|
||
|
||
if (q->abort_request) //如果已中止,则放入失败
|
||
return -1;
|
||
|
||
pkt1 = (MyAVPacketList *)av_malloc(sizeof(MyAVPacketList)); //分配节点内存
|
||
if (!pkt1) //内存不足,则放入失败
|
||
return -1;
|
||
// 没有做引用计数,那这里也说明av_read_frame不会释放替用户释放buffer。
|
||
pkt1->pkt = *pkt; //拷贝AVPacket(浅拷贝,AVPacket.data等内存并没有拷贝)
|
||
pkt1->next = NULL;
|
||
if (pkt == &flush_pkt)//如果放入的是flush_pkt,需要增加队列的播放序列号,以区分不连续的两段数据
|
||
{
|
||
q->serial++;
|
||
LOG(INFO) << "q->serial = " << q->serial;
|
||
}
|
||
pkt1->serial = q->serial; //用队列序列号标记节点
|
||
/* 队列操作:如果last_pkt为空,说明队列是空的,新增节点为队头;
|
||
* 否则,队列有数据,则让原队尾的next为新增节点。 最后将队尾指向新增节点
|
||
*/
|
||
if (!q->last_pkt)
|
||
q->first_pkt = pkt1;
|
||
else
|
||
q->last_pkt->next = pkt1;
|
||
q->last_pkt = pkt1;
|
||
|
||
//队列属性操作:增加节点数、cache大小、cache总时长, 用来控制队列的大小
|
||
q->nb_packets++;
|
||
q->size += pkt1->pkt.size + sizeof(*pkt1);
|
||
q->duration += pkt1->pkt.duration;
|
||
|
||
/* XXX: should duplicate packet data in DV case */
|
||
//发出信号,表明当前队列中有数据了,通知等待中的读线程可以取数据了
|
||
SDL_CondSignal(q->cond);
|
||
return 0;
|
||
}
|
||
|
||
int packet_queue_put(PacketQueue *q, AVPacket *pkt)
|
||
{
|
||
int ret;
|
||
|
||
SDL_LockMutex(q->mutex);
|
||
ret = packet_queue_put_private(q, pkt);//主要实现
|
||
SDL_UnlockMutex(q->mutex);
|
||
|
||
if (pkt != &flush_pkt && ret < 0)
|
||
av_packet_unref(pkt); //放入失败,释放AVPacket
|
||
|
||
return ret;
|
||
}
|
||
|
||
int packet_queue_put_nullpacket(PacketQueue *q, int stream_index)
|
||
{
|
||
AVPacket pkt1, *pkt = &pkt1;
|
||
av_init_packet(pkt);
|
||
pkt->data = NULL;
|
||
pkt->size = 0;
|
||
pkt->stream_index = stream_index;
|
||
return packet_queue_put(q, pkt);
|
||
}
|
||
|
||
/* packet queue handling */
|
||
int packet_queue_init(PacketQueue *q)
|
||
{
|
||
memset(q, 0, sizeof(PacketQueue));
|
||
q->mutex = SDL_CreateMutex();
|
||
if (!q->mutex) {
|
||
av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
|
||
return AVERROR(ENOMEM);
|
||
}
|
||
q->cond = SDL_CreateCond();
|
||
if (!q->cond) {
|
||
av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
|
||
return AVERROR(ENOMEM);
|
||
}
|
||
q->abort_request = 1;
|
||
return 0;
|
||
}
|
||
|
||
void packet_queue_flush(PacketQueue *q)
|
||
{
|
||
MyAVPacketList *pkt, *pkt1;
|
||
|
||
SDL_LockMutex(q->mutex);
|
||
for (pkt = q->first_pkt; pkt; pkt = pkt1) {
|
||
pkt1 = pkt->next;
|
||
av_packet_unref(&pkt->pkt);
|
||
av_freep(&pkt);
|
||
}
|
||
q->last_pkt = NULL;
|
||
q->first_pkt = NULL;
|
||
q->nb_packets = 0;
|
||
q->size = 0;
|
||
q->duration = 0;
|
||
SDL_UnlockMutex(q->mutex);
|
||
}
|
||
|
||
void packet_queue_destroy(PacketQueue *q)
|
||
{
|
||
packet_queue_flush(q); //先清除所有的节点
|
||
SDL_DestroyMutex(q->mutex);
|
||
SDL_DestroyCond(q->cond);
|
||
}
|
||
|
||
void packet_queue_abort(PacketQueue *q)
|
||
{
|
||
SDL_LockMutex(q->mutex);
|
||
|
||
q->abort_request = 1; // 请求退出
|
||
|
||
SDL_CondSignal(q->cond); //释放一个条件信号
|
||
|
||
SDL_UnlockMutex(q->mutex);
|
||
}
|
||
|
||
void packet_queue_start(PacketQueue *q)
|
||
{
|
||
SDL_LockMutex(q->mutex);
|
||
q->abort_request = 0;
|
||
packet_queue_put_private(q, &flush_pkt); //这里放入了一个flush_pkt
|
||
SDL_UnlockMutex(q->mutex);
|
||
}
|
||
|
||
double packet_queue_cache_duration(PacketQueue *q, AVRational time_base, double packet_duration)
|
||
{
|
||
double pts_duration = 0; // 按队列头部、尾部的pts时长
|
||
double packets_duration = 0;// 按包累积的时长
|
||
|
||
SDL_LockMutex(q->mutex);
|
||
MyAVPacketList *first_pkt = q->first_pkt;
|
||
MyAVPacketList *last_pkt = q->last_pkt;
|
||
int64_t temp_pts = last_pkt->pkt.dts - first_pkt->pkt.dts; // 计算出来差距
|
||
pts_duration = temp_pts * av_q2d(time_base); // 转换成秒
|
||
|
||
packets_duration = packet_duration * q->nb_packets; // packet可能存在多帧音频的情况, 此时这里计算就存在误差
|
||
|
||
SDL_UnlockMutex(q->mutex);
|
||
|
||
// 以时间戳为准,然后根据码率预估持续播放的最大时长,比如以时间戳计算出来超过60秒,则是极有可能是有问题的,说明缓存的数据比较多
|
||
if(pts_duration < 60) {
|
||
return pts_duration;
|
||
} else {
|
||
return packets_duration;
|
||
}
|
||
}
|
||
|
||
|
||
/* return < 0 if aborted, 0 if no packet and > 0 if packet. */
|
||
/**
|
||
* @brief packet_queue_get
|
||
* @param q 队列
|
||
* @param pkt 输出参数,即MyAVPacketList.pkt
|
||
* @param block 调用者是否需要在没节点可取的情况下阻塞等待
|
||
* @param serial 输出参数,即MyAVPacketList.serial
|
||
* @return <0: aborted; =0: no packet; >0: has packet
|
||
*/
|
||
int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block, int *serial)
|
||
{
|
||
MyAVPacketList *pkt1;
|
||
int ret;
|
||
|
||
SDL_LockMutex(q->mutex); // 加锁
|
||
|
||
for (;;) {
|
||
if (q->abort_request) {
|
||
ret = -1;
|
||
break;
|
||
}
|
||
|
||
pkt1 = q->first_pkt; //MyAVPacketList *pkt1; 从队头拿数据
|
||
if (pkt1) { //队列中有数据
|
||
q->first_pkt = pkt1->next; //队头移到第二个节点
|
||
if (!q->first_pkt)
|
||
q->last_pkt = NULL;
|
||
q->nb_packets--; //节点数减1
|
||
q->size -= pkt1->pkt.size + sizeof(*pkt1); //cache大小扣除一个节点
|
||
q->duration -= pkt1->pkt.duration; //总时长扣除一个节点
|
||
//返回AVPacket,这里发生一次AVPacket结构体拷贝,AVPacket的data只拷贝了指针
|
||
*pkt = pkt1->pkt;
|
||
if (serial) //如果需要输出serial,把serial输出
|
||
*serial = pkt1->serial;
|
||
av_free(pkt1); //释放节点内存,只是释放节点,而不是释放AVPacket
|
||
ret = 1;
|
||
break;
|
||
} else if (!block) { //队列中没有数据,且非阻塞调用
|
||
ret = 0;
|
||
break;
|
||
} else { //队列中没有数据,且阻塞调用
|
||
//这里没有break。for循环的另一个作用是在条件变量满足后重复上述代码取出节点
|
||
SDL_CondWait(q->cond, q->mutex);
|
||
}
|
||
}
|
||
SDL_UnlockMutex(q->mutex); // 释放锁
|
||
return ret;
|
||
}
|
||
|
||
|
||
|
||
static void frame_queue_unref_item(Frame *vp)
|
||
{
|
||
av_frame_unref(vp->frame); /* 释放数据 */
|
||
}
|
||
|
||
/* 初始化FrameQueue,视频和音频keep_last设置为1,字幕设置为0 */
|
||
int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
|
||
{
|
||
int i;
|
||
memset(f, 0, sizeof(FrameQueue));
|
||
if (!(f->mutex = SDL_CreateMutex())) {
|
||
av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
|
||
return AVERROR(ENOMEM);
|
||
}
|
||
if (!(f->cond = SDL_CreateCond())) {
|
||
av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
|
||
return AVERROR(ENOMEM);
|
||
}
|
||
f->pktq = pktq;
|
||
f->max_size = FFMIN(max_size, FRAME_QUEUE_SIZE);
|
||
f->keep_last = !!keep_last;
|
||
for (i = 0; i < f->max_size; i++)
|
||
if (!(f->queue[i].frame = av_frame_alloc())) // 分配AVFrame结构体
|
||
return AVERROR(ENOMEM);
|
||
return 0;
|
||
}
|
||
|
||
void frame_queue_destory(FrameQueue *f)
|
||
{
|
||
int i;
|
||
for (i = 0; i < f->max_size; i++) {
|
||
Frame *vp = &f->queue[i];
|
||
// 释放对vp->frame中的数据缓冲区的引用,注意不是释放frame对象本身
|
||
frame_queue_unref_item(vp);
|
||
// 释放vp->frame对象
|
||
av_frame_free(&vp->frame);
|
||
}
|
||
SDL_DestroyMutex(f->mutex);
|
||
SDL_DestroyCond(f->cond);
|
||
}
|
||
|
||
void frame_queue_signal(FrameQueue *f)
|
||
{
|
||
SDL_LockMutex(f->mutex);
|
||
SDL_CondSignal(f->cond);
|
||
SDL_UnlockMutex(f->mutex);
|
||
}
|
||
|
||
/* 获取队列当前Frame, 在调用该函数前先调用frame_queue_nb_remaining确保有frame可读 */
|
||
Frame *frame_queue_peek(FrameQueue *f)
|
||
{
|
||
return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
|
||
}
|
||
|
||
/* 获取当前Frame的下一Frame, 此时要确保queue里面至少有2个Frame */
|
||
// 不管你什么时候调用,返回来肯定不是 NULL
|
||
Frame *frame_queue_peek_next(FrameQueue *f)
|
||
{
|
||
return &f->queue[(f->rindex + f->rindex_shown + 1) % f->max_size];
|
||
}
|
||
|
||
/* 获取last Frame:
|
||
*/
|
||
Frame *frame_queue_peek_last(FrameQueue *f)
|
||
{
|
||
return &f->queue[f->rindex];
|
||
}
|
||
// 获取可写指针
|
||
Frame *frame_queue_peek_writable(FrameQueue *f)
|
||
{
|
||
/* wait until we have space to put a new frame */
|
||
SDL_LockMutex(f->mutex);
|
||
while (f->size >= f->max_size &&
|
||
!f->pktq->abort_request) { /* 检查是否需要退出 */
|
||
SDL_CondWait(f->cond, f->mutex);
|
||
}
|
||
SDL_UnlockMutex(f->mutex);
|
||
|
||
if (f->pktq->abort_request) /* 检查是不是要退出 */
|
||
return NULL;
|
||
|
||
return &f->queue[f->windex];
|
||
}
|
||
// 获取可读
|
||
Frame *frame_queue_peek_readable(FrameQueue *f)
|
||
{
|
||
/* wait until we have a readable a new frame */
|
||
SDL_LockMutex(f->mutex);
|
||
while (f->size <= 0 &&
|
||
!f->pktq->abort_request) {
|
||
SDL_CondWait(f->cond, f->mutex);
|
||
}
|
||
SDL_UnlockMutex(f->mutex);
|
||
|
||
if (f->pktq->abort_request)
|
||
return NULL;
|
||
|
||
return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
|
||
}
|
||
// 更新写指针
|
||
void frame_queue_push(FrameQueue *f)
|
||
{
|
||
if (++f->windex == f->max_size)
|
||
f->windex = 0;
|
||
SDL_LockMutex(f->mutex);
|
||
f->size++;
|
||
SDL_CondSignal(f->cond); // 当_readable在等待时则可以唤醒
|
||
SDL_UnlockMutex(f->mutex);
|
||
}
|
||
|
||
/* 释放当前frame,并更新读索引rindex */
|
||
void frame_queue_next(FrameQueue *f)
|
||
{
|
||
if (f->keep_last && !f->rindex_shown) {
|
||
f->rindex_shown = 1;
|
||
return;
|
||
}
|
||
frame_queue_unref_item(&f->queue[f->rindex]);
|
||
if (++f->rindex == f->max_size)
|
||
f->rindex = 0;
|
||
SDL_LockMutex(f->mutex);
|
||
f->size--;
|
||
SDL_CondSignal(f->cond);
|
||
SDL_UnlockMutex(f->mutex);
|
||
}
|
||
|
||
/* return the number of undisplayed frames in the queue */
|
||
int frame_queue_nb_remaining(FrameQueue *f)
|
||
{
|
||
return f->size - f->rindex_shown;
|
||
}
|
||
|
||
/* return last shown position */
|
||
int64_t frame_queue_last_pos(FrameQueue *f)
|
||
{
|
||
Frame *fp = &f->queue[f->rindex];
|
||
if (f->rindex_shown && fp->serial == f->pktq->serial)
|
||
if(fp)
|
||
return fp->pos;
|
||
else
|
||
return -1;
|
||
}
|
||
|
||
|
||
/**
|
||
* 获取到的实际上是:最后一帧的pts 加上 从处理最后一帧开始到现在的时间,具体参考set_clock_at 和get_clock的代码
|
||
* c->pts_drift=最后一帧的pts-从处理最后一帧时间
|
||
* clock=c->pts_drift+现在的时候
|
||
* get_clock(&is->vidclk) ==is->vidclk.pts, av_gettime_relative() / 1000000.0 -is->vidclk.last_updated +is->vidclk.pts
|
||
*/
|
||
double get_clock(Clock *c)
|
||
{
|
||
if (*c->queue_serial != c->serial)
|
||
return NAN; // 不是同一个播放序列,时钟是无效
|
||
if (c->paused) {
|
||
return c->pts; // 暂停的时候返回的是pts
|
||
} else {
|
||
double time = av_gettime_relative() / 1000000.0;
|
||
return c->pts_drift + time - (time - c->last_updated) * (1.0 - c->speed);
|
||
}
|
||
}
|
||
|
||
void set_clock_at(Clock *c, double pts, int serial,double time)
|
||
{
|
||
c->pts = pts; /* 当前帧的pts */
|
||
c->last_updated = time; /* 最后更新的时间,实际上是当前的一个系统时间 */
|
||
c->pts_drift = c->pts - time; /* 当前帧pts和系统时间的差值,正常播放情况下两者的差值应该是比较固定的,因为两者都是以时间为基准进行线性增长 */
|
||
c->serial = serial;
|
||
}
|
||
|
||
void set_clock(Clock *c, double pts, int serial)
|
||
{
|
||
double time = av_gettime_relative() / 1000000.0;
|
||
set_clock_at(c, pts, serial, time);
|
||
}
|
||
|
||
|
||
void init_clock(Clock *c, int *queue_serial)
|
||
{
|
||
c->speed = 1.0;
|
||
c->paused = 0;
|
||
c->queue_serial = queue_serial;
|
||
set_clock(c, NAN, -1);
|
||
}
|
||
|
||
|
||
void ffp_reset_statistic(FFStatistic *dcc)
|
||
{
|
||
memset(dcc, 0, sizeof(FFStatistic));
|
||
}
|