读包操作分析
//链表元素结构体
typedef struct MyAVPacketList {
AVPacket pkt;
struct MyAVPacketList *next;//指向下一个元素
int serial;
} MyAVPacketList;
//包队列,先进FIFO队列
typedef struct PacketQueue {
MyAVPacketList *first_pkt, *last_pkt;//队首,队尾指针
int nb_packets;//包数量,也就是队列元素数量
int size;//占用内存大小
int abort_request;//用户退出请求标志
int serial;//序列,作拖动时用,作为区分前后帧序列
SDL_mutex *mutex;//互斥锁
SDL_cond *cond;//条件锁
} PacketQueue;
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;
pkt1->pkt = *pkt;
pkt1->next = NULL;
if (pkt == &flush_pkt)//入队的包为flush标签包,serial自加,表示另外一个系列
q->serial++;
pkt1->serial = q->serial;
if (!q->last_pkt)//队列为空
q->first_pkt = pkt1;
else
q->last_pkt->next = pkt1;//不为空,加到队尾
q->last_pkt = pkt1;//将队尾设为当前入队包
q->nb_packets++;//数量增加
q->size += pkt1->pkt.size + sizeof(*pkt1);//更新内存使用量
/* XXX: should duplicate packet data in DV case */
SDL_CondSignal(q->cond);//条件变量解锁
return 0;
}
//入队API,调用此函数是线程安全的
static int packet_queue_put(PacketQueue *q, AVPacket *pkt)
{
int ret;
/* duplicate the packet */
if (pkt != &flush_pkt && av_dup_packet(pkt) < 0)
return -1;
SDL_LockMutex(q->mutex);
ret = packet_queue_put_private(q, pkt);
SDL_UnlockMutex(q->mutex);
if (pkt != &flush_pkt && ret < 0)
av_free_packet(pkt);
return ret;
}
由于视频解码线程,音频播放(主线程)同时访问包队列,所以需要加互斥锁。
//将一个空包入队
static 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);
}
//队列初始化
static void packet_queue_init(PacketQueue *q)
{
memset(q, 0, sizeof(PacketQueue));
q->mutex = SDL_CreateMutex();
q->cond = SDL_CreateCond();
q->abort_request = 1;
}
//释放队列空间
static void packet_queue_destroy(PacketQueue *q)
{
packet_queue_flush(q);
SDL_DestroyMutex(q->mutex);
SDL_DestroyCond(q->cond);
}
//用户取消
static void packet_queue_abort(PacketQueue *q)
{
SDL_LockMutex(q->mutex);
q->abort_request = 1;
SDL_CondSignal(q->cond);
SDL_UnlockMutex(q->mutex);
}
//队列开启
static void packet_queue_start(PacketQueue *q)
{
SDL_LockMutex(q->mutex);
q->abort_request = 0;
packet_queue_put_private(q, &flush_pkt);
SDL_UnlockMutex(q->mutex);
}
/* return < 0 if aborted, 0 if no packet and > 0 if packet. */
//出队操作
static 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;
if (pkt1) {
q->first_pkt = pkt1->next;
if (!q->first_pkt)
q->last_pkt = NULL;
q->nb_packets--;
q->size -= pkt1->pkt.size + sizeof(*pkt1);
*pkt = pkt1->pkt;
if (serial)
*serial = pkt1->serial;
av_free(pkt1);
ret = 1;
break;
} else if (!block) {
ret = 0;
break;
} else {//队列为空,阻塞在队列条件变量上
SDL_CondWait(q->cond, q->mutex);
}
}
SDL_UnlockMutex(q->mutex);
return ret;
}
//读取一帧
int av_read_frame(AVFormatContext *s, AVPacket *pkt)
{
const int genpts = s->flags & AVFMT_FLAG_GENPTS;
int eof = 0;
int ret;
AVStream *st;
//如果没有设置生成时间戳标志
if (!genpts) {
//包FIFO队列不为空,从队列中出队,否则从文件中读取
ret = s->packet_buffer ?
read_from_packet_buffer(&s->packet_buffer, &s->packet_buffer_end, pkt) :
read_frame_internal(s, pkt);
if (ret < 0)
return ret;
goto return_packet;
}
//设置了生成时间戳
for (;;) {
//队列头
AVPacketList *pktl = s->packet_buffer;
if (pktl) {
AVPacket *next_pkt = &pktl->pkt;
//如果解码时间戳有效
if (next_pkt->dts != AV_NOPTS_VALUE) {
int wrap_bits = s->streams[next_pkt->stream_index]->pts_wrap_bits;//获取时间戳压缩位数
// last dts seen for this stream. if any of packets following
// current one had no dts, we will set this to AV_NOPTS_VALUE.
int64_t last_dts = next_pkt->dts;
while (pktl && next_pkt->pts == AV_NOPTS_VALUE) {
//时间戳无效
if (pktl->pkt.stream_index == next_pkt->stream_index &&
(av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2LL << (wrap_bits - 1)) < 0)) {
if (av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2LL << (wrap_bits - 1))) { //not b frame
next_pkt->pts = pktl->pkt.dts;
}
if (last_dts != AV_NOPTS_VALUE) {
// Once last dts was set to AV_NOPTS_VALUE, we don't change it.
last_dts = pktl->pkt.dts;
}
}
pktl = pktl->next;
}
if (eof && next_pkt->pts == AV_NOPTS_VALUE && last_dts != AV_NOPTS_VALUE) {
// Fixing the last reference frame had none pts issue (For MXF etc).
// We only do this when
// 1. eof.
// 2. we are not able to resolve a pts value for current packet.
// 3. the packets for this stream at the end of the files had valid dts.
next_pkt->pts = last_dts + next_pkt->duration;
}
pktl = s->packet_buffer;
}
/* read packet from packet buffer, if there is data */
if (!(next_pkt->pts == AV_NOPTS_VALUE &&
next_pkt->dts != AV_NOPTS_VALUE && !eof)) {
ret = read_from_packet_buffer(&s->packet_buffer,
&s->packet_buffer_end, pkt);
goto return_packet;
}
}
ret = read_frame_internal(s, pkt);
if (ret < 0) {
if (pktl && ret != AVERROR(EAGAIN)) {
eof = 1;
continue;
} else
return ret;
}
//拷贝副本,并添加到队列中
if (av_dup_packet(add_to_pktbuf(&s->packet_buffer, pkt,
&s->packet_buffer_end)) < 0)
return AVERROR(ENOMEM);
}
return_packet:
st = s->streams[pkt->stream_index];
if (st->skip_samples) {
uint8_t *p = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
if (p) {
AV_WL32(p, st->skip_samples);
av_log(s, AV_LOG_DEBUG, "demuxer injecting skip %d\n", st->skip_samples);
}
st->skip_samples = 0;
}
if ((s->iformat->flags & AVFMT_GENERIC_INDEX) && pkt->flags & AV_PKT_FLAG_KEY) {
ff_reduce_index(s, st->index);
av_add_index_entry(st, pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME);
}
if (is_relative(pkt->dts))
pkt->dts -= RELATIVE_TS_BASE;
if (is_relative(pkt->pts))
pkt->pts -= RELATIVE_TS_BASE;
return ret;
}
//flush 队列
static void packet_queue_flush(PacketQueue *q)
{
MyAVPacketList *pkt, *pkt1;
SDL_LockMutex(q->mutex);
for (pkt = q->first_pkt; pkt != NULL; pkt = pkt1) {
pkt1 = pkt->next;
av_free_packet(&pkt->pkt);
av_freep(&pkt);
}
q->last_pkt = NULL;
q->first_pkt = NULL;
q->nb_packets = 0;
q->size = 0;
SDL_UnlockMutex(q->mutex);
}
ffmpeg纯属个人喜好而了解,定有许多疏漏之处,欢迎指正!