268 lines
7.7 KiB
C++
268 lines
7.7 KiB
C++
/******************************************************************************
|
|
QtAV: Multimedia framework based on Qt and FFmpeg
|
|
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
|
|
|
|
* This file is part of QtAV
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
******************************************************************************/
|
|
|
|
#include "Packet.h"
|
|
#include "AVCompat.h"
|
|
#include "Logger.h"
|
|
|
|
namespace FAV {
|
|
namespace {
|
|
static const struct RegisterMetaTypes {
|
|
inline RegisterMetaTypes() {
|
|
qRegisterMetaType<FAV::Packet>();
|
|
}
|
|
} _registerMetaTypes;
|
|
} //namespace
|
|
|
|
class PacketPrivate : public QSharedData
|
|
{
|
|
public:
|
|
PacketPrivate()
|
|
: QSharedData()
|
|
, initialized(false)
|
|
{
|
|
av_init_packet(&avpkt);
|
|
}
|
|
PacketPrivate(const PacketPrivate& o)
|
|
: QSharedData(o)
|
|
, initialized(o.initialized)
|
|
{ //used by QSharedDataPointer.detach()
|
|
av_init_packet(&avpkt);
|
|
av_packet_ref(&avpkt, (AVPacket*)&o.avpkt);
|
|
}
|
|
~PacketPrivate() {
|
|
av_packet_unref(&avpkt);
|
|
}
|
|
bool initialized;
|
|
AVPacket avpkt;
|
|
};
|
|
Packet Packet::createEOF()
|
|
{
|
|
Packet pkt;
|
|
pkt.data = QByteArray("eof");
|
|
return pkt;
|
|
}
|
|
bool Packet::isEOF() const
|
|
{
|
|
return data == "eof" && pts < 0.0 && dts < 0.0;
|
|
}
|
|
|
|
Packet Packet::fromAVPacket(const AVPacket *avpkt, double time_base)
|
|
{
|
|
Packet pkt;
|
|
if (fromAVPacket(&pkt, avpkt, time_base))
|
|
return pkt;
|
|
return Packet();
|
|
}
|
|
|
|
// time_base: av_q2d(format_context->streams[stream_idx]->time_base)
|
|
bool Packet::fromAVPacket(Packet* pkt, const AVPacket *avpkt, double time_base)
|
|
{
|
|
if (!pkt || !avpkt)
|
|
return false;
|
|
|
|
pkt->position = avpkt->pos;
|
|
pkt->hasKeyFrame = !!(avpkt->flags & AV_PKT_FLAG_KEY);
|
|
// what about marking avpkt as invalid and do not use isCorrupt?
|
|
pkt->isCorrupt = !!(avpkt->flags & AV_PKT_FLAG_CORRUPT);
|
|
if (pkt->isCorrupt)
|
|
{
|
|
qDebug("currupt packet. pts: %f", pkt->pts);
|
|
}
|
|
|
|
// from av_read_frame: pkt->pts can be AV_NOPTS_VALUE if the video format has B-frames, so it is better to rely on pkt->dts if you do not decompress the payload.
|
|
// old code set pts as dts is valid
|
|
if (avpkt->pts != (qint64)AV_NOPTS_VALUE)
|
|
{
|
|
pkt->pts = avpkt->pts * time_base;
|
|
}
|
|
else if (avpkt->dts != (qint64)AV_NOPTS_VALUE) // is it ok?
|
|
{
|
|
pkt->pts = avpkt->dts * time_base;
|
|
}
|
|
else
|
|
{
|
|
pkt->pts = 0; // TODO: init value
|
|
}
|
|
if (avpkt->dts != (qint64)AV_NOPTS_VALUE) //has B-frames
|
|
{
|
|
pkt->dts = avpkt->dts * time_base;
|
|
}
|
|
else
|
|
{
|
|
pkt->dts = pkt->pts;
|
|
}
|
|
//qDebug("avpacket pts %lld, dts: %lld ", avpkt->pts, avpkt->dts);
|
|
//TODO: pts must >= 0? look at ffplay
|
|
pkt->pts = qMax<qreal>(0, pkt->pts);
|
|
pkt->dts = qMax<qreal>(0, pkt->dts);
|
|
|
|
if (avpkt->duration > 0)
|
|
{
|
|
pkt->duration = avpkt->duration * time_base;
|
|
}
|
|
else
|
|
{
|
|
pkt->duration = 0;
|
|
}
|
|
#if (LIBAVCODEC_VERSION_MAJOR < 57) //FF_API_CONVERGENCE_DURATION since 57
|
|
// subtitle always has a key frame? convergence_duration may be 0
|
|
if (avpkt->convergence_duration > 0
|
|
&& pkt->hasKeyFrame
|
|
#if 0
|
|
&& codec->codec_type == AVMEDIA_TYPE_SUBTITLE
|
|
#endif
|
|
)
|
|
pkt->duration = avpkt->convergence_duration * time_base;
|
|
#endif
|
|
//qDebug("AVPacket.pts=%f, duration=%f, dts=%lld", pkt->pts, pkt->duration, packet.dts);
|
|
pkt->data.clear();
|
|
// TODO: pkt->avpkt. data is not necessary now. see mpv new_demux_packet_from_avpacket
|
|
// copy properties and side data. does not touch data, size and ref
|
|
pkt->d = QSharedDataPointer<PacketPrivate>(new PacketPrivate());
|
|
pkt->d->initialized = true;
|
|
AVPacket *p = &pkt->d->avpkt;
|
|
av_packet_ref(p, (AVPacket*)avpkt); //properties are copied internally
|
|
// add ref without copy, bytearray does not copy either. bytearray options linke remove() is safe. omit FF_INPUT_BUFFER_PADDING_SIZE
|
|
pkt->data = QByteArray::fromRawData((const char*)p->data, p->size);
|
|
// QtAV always use ms (1/1000s) and s. As a result no time_base is required in Packet
|
|
p->pts = pkt->pts * 1000.0;
|
|
p->dts = pkt->dts * 1000.0;
|
|
p->duration = pkt->duration * 1000.0;
|
|
return true;
|
|
}
|
|
|
|
Packet::Packet()
|
|
: hasKeyFrame(false)
|
|
, isCorrupt(false)
|
|
, pts(-1)
|
|
, duration(-1)
|
|
, dts(-1)
|
|
, position(-1)
|
|
#if (PLAY_SYNC_FIX2)
|
|
, _isSeek(false)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
|
|
Packet::Packet(const Packet &other)
|
|
: hasKeyFrame(other.hasKeyFrame)
|
|
, isCorrupt(other.isCorrupt)
|
|
, data(other.data)
|
|
, pts(other.pts)
|
|
, duration(other.duration)
|
|
, dts(other.dts)
|
|
, position(other.position)
|
|
, d(other.d)
|
|
#if (PLAY_SYNC_FIX2)
|
|
,_isSeek(other._isSeek)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
Packet& Packet::operator =(const Packet& other)
|
|
{
|
|
if (this == &other)
|
|
return *this;
|
|
d = other.d;
|
|
hasKeyFrame = other.hasKeyFrame;
|
|
isCorrupt = other.isCorrupt;
|
|
pts = other.pts;
|
|
duration = other.duration;
|
|
dts = other.dts;
|
|
position = other.position;
|
|
data = other.data;
|
|
#if (PLAY_SYNC_FIX2)
|
|
_isSeek = other.isSeek();
|
|
#endif
|
|
return *this;
|
|
}
|
|
|
|
Packet::~Packet()
|
|
{
|
|
}
|
|
|
|
const AVPacket *Packet::asAVPacket() const
|
|
{
|
|
if (d.constData()) //why d->initialized (ref==1) result in detach?
|
|
{
|
|
//d.data() was 0 if d has not been accessed. now only contains avpkt, check d.constData() is engough
|
|
if (d.constData()->initialized)
|
|
{
|
|
d->avpkt.data = (uint8_t*)data.constData();
|
|
d->avpkt.size = data.size();
|
|
return &d->avpkt;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
d = QSharedDataPointer<PacketPrivate>(new PacketPrivate());
|
|
}
|
|
|
|
d->initialized = true;
|
|
AVPacket *p = &d->avpkt;
|
|
p->pts = pts * 1000.0;
|
|
p->dts = dts * 1000.0;
|
|
p->duration = duration * 1000.0;
|
|
p->pos = position;
|
|
if (isCorrupt)
|
|
p->flags |= AV_PKT_FLAG_CORRUPT;
|
|
if (hasKeyFrame)
|
|
p->flags |= AV_PKT_FLAG_KEY;
|
|
if (!data.isEmpty()) {
|
|
p->data = (uint8_t*)data.constData();
|
|
p->size = data.size();
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void Packet::skip(int bytes)
|
|
{
|
|
if (!d.constData()) //not constructed from AVPacket
|
|
{
|
|
d = QSharedDataPointer<PacketPrivate>(new PacketPrivate());
|
|
}
|
|
d->initialized = false;
|
|
data = QByteArray::fromRawData(data.constData() + bytes, data.size() - bytes);
|
|
if (position >= 0)
|
|
{
|
|
position += bytes;
|
|
}
|
|
// TODO: if duration is valid, compute pts/dts and no manually update outside?
|
|
}
|
|
|
|
#ifndef QT_NO_DEBUG_STREAM
|
|
QDebug operator<<(QDebug dbg, const Packet &pkt)
|
|
{
|
|
dbg.nospace() << "FAV::Packet.data " << hex << (qptrdiff)pkt.data.constData() << "+" << dec << pkt.data.size();
|
|
dbg.nospace() << ", dts: " << pkt.dts;
|
|
dbg.nospace() << ", pts: " << pkt.pts;
|
|
dbg.nospace() << ", duration: " << pkt.duration;
|
|
dbg.nospace() << ", position: " << pkt.position;
|
|
dbg.nospace() << ", hasKeyFrame: " << pkt.hasKeyFrame;
|
|
dbg.nospace() << ", isCorrupt: " << pkt.isCorrupt;
|
|
dbg.nospace() << ", eof: " << pkt.isEOF();
|
|
return dbg.space();
|
|
}
|
|
#endif //QT_NO_DEBUG_STREAM
|
|
} //namespace FAV
|