diff --git a/src/AVThread_p.h b/src/AVThread_p.h index 9a1842705..b41b46594 100644 --- a/src/AVThread_p.h +++ b/src/AVThread_p.h @@ -24,6 +24,7 @@ #include #include +#include #include #include "QtAV/Packet.h" #include "utils/BlockingQueue.h" diff --git a/src/Packet.cpp b/src/Packet.cpp index 05dc1b08a..a22b60001 100644 --- a/src/Packet.cpp +++ b/src/Packet.cpp @@ -19,19 +19,160 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ -#include +#include "QtAV/Packet.h" +#include "QtAV/private/AVCompat.h" #include "utils/Logger.h" namespace QtAV { const qreal Packet::kEndPts = -0.618; +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); + + if (avpkt->pts != AV_NOPTS_VALUE) + pkt->pts = avpkt->pts * time_base; + else if (avpkt->dts != AV_NOPTS_VALUE) // is it ok? + pkt->pts = avpkt->dts * time_base; + else + pkt->pts = 0; // TODO: init value + if (avpkt->dts != AV_NOPTS_VALUE) //has B-frames + pkt->dts = avpkt->dts * time_base; + else if (avpkt->pts != AV_NOPTS_VALUE) // is it ok? + pkt->dts = avpkt->pts * time_base; + else + pkt->dts = 0; // TODO: init value + //TODO: pts must >= 0? look at ffplay + pkt->pts = qMax(0, pkt->pts); + pkt->dts = qMax(0, pkt->dts); + + + // subtitle always has a key frame? convergence_duration may be 0 + if (avpkt->convergence_duration > 0 // mpv demux_lavf only check this + && pkt->hasKeyFrame +#if 0 + && codec->codec_type == AVMEDIA_TYPE_SUBTITLE +#endif + ) + pkt->duration = avpkt->convergence_duration * time_base; + else if (avpkt->duration > 0) + pkt->duration = avpkt->duration * time_base; + else + pkt->duration = 0; + + //qDebug("AVPacket.pts=%f, duration=%f, dts=%lld", pkt->pts, pkt->duration, packet.dts); +#if NO_PADDING_DATA + pkt->data.clear(); + if (avpkt->data) + pkt->data = QByteArray((const char*)avpkt->data, avpkt->size); +#else + /*! + larger than the actual read bytes because some optimized bitstream readers read 32 or 64 bits at once and could read over the end. + The end of the input buffer avpkt->data should be set to 0 to ensure that no overreading happens for damaged MPEG streams + */ + QByteArray encoded; + if (avpkt->data) { + encoded.reserve(avpkt->size + FF_INPUT_BUFFER_PADDING_SIZE); + encoded.resize(avpkt->size); + // also copy padding data(usually 0) + memcpy(encoded.data(), avpkt->data, encoded.capacity()); // encoded.capacity() is always > 0 even if packet.data, so must check avpkt->data null + } + pkt->data = encoded; +#endif //NO_PADDING_DATA + + // 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 + // libav has no av_copy_packet_side_data() in public api + AVPacket *p = new AVPacket(); + av_init_packet(p); + av_packet_copy_props(p, avpkt); + if (!pkt->data.isEmpty()) { + p->data = (uint8_t*)pkt->data.constData(); + p->size = pkt->data.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; + pkt->avpkt = p; + return true; +} + Packet::Packet() : hasKeyFrame(false) , isCorrupt(false) , pts(0) , duration(0) + , dts(0) + , position(0) + , avpkt(0) +{ +} + +Packet::Packet(const Packet &other) { + if (this == &other) + return; + hasKeyFrame = other.hasKeyFrame; + isCorrupt = other.isCorrupt; + pts = other.pts; + duration = other.duration; + dts = other.dts; + position = other.position; + data = other.data; + avpkt = new AVPacket(); + av_init_packet(avpkt); + av_packet_copy_props(avpkt, other.toAVPacket()); +} + +Packet& Packet::operator =(const Packet& other) +{ + if (this == &other) + return *this; + hasKeyFrame = other.hasKeyFrame; + isCorrupt = other.isCorrupt; + pts = other.pts; + duration = other.duration; + dts = other.dts; + position = other.position; + data = other.data; + if (!avpkt) { + avpkt = new AVPacket(); + av_init_packet(avpkt); + } else { + av_free_packet(avpkt); // free old side data and ref + } + av_packet_copy_props(avpkt, other.toAVPacket()); + return *this; +} + +Packet::~Packet() +{ + if (avpkt) { + // only free side data and ref, does not release data + av_free_packet(avpkt); + + delete avpkt; + avpkt = 0; + } } void Packet::markEnd() @@ -40,4 +181,25 @@ void Packet::markEnd() pts = kEndPts; } +AVPacket* Packet::toAVPacket() const +{ + if (avpkt) + return avpkt; + + avpkt = new AVPacket(); + av_init_packet(avpkt); + avpkt->pts = pts * 1000.0; + avpkt->dts = dts * 1000.0; + avpkt->duration = duration * 1000.0; + if (isCorrupt) + avpkt->flags |= AV_PKT_FLAG_CORRUPT; + if (hasKeyFrame) + avpkt->flags |= AV_PKT_FLAG_KEY; + if (!data.isEmpty()) { + avpkt->data = (uint8_t*)data.constData(); + avpkt->size = data.size(); + } + return avpkt; +} + } //namespace QtAV diff --git a/src/QtAV/Packet.h b/src/QtAV/Packet.h index 1375c9044..0fa34582c 100644 --- a/src/QtAV/Packet.h +++ b/src/QtAV/Packet.h @@ -22,28 +22,44 @@ #ifndef QAV_PACKET_H #define QAV_PACKET_H -#include #include -#include -#include #include -//TODO: init from AVPacket and store ptr? +struct AVPacket; + namespace QtAV { + class Q_AV_EXPORT Packet { public: + //const avpkt? if no use ref + static Packet fromAVPacket(const AVPacket* avpkt, double time_base); + static bool fromAVPacket(Packet *pkt, const AVPacket *avpkt, double time_base); + Packet(); + Packet(const Packet& other); + ~Packet(); + + Packet& operator =(const Packet& other); + inline bool isValid() const; inline bool isEnd() const; void markEnd(); + /// Packet takes the owner ship. time unit is ms + AVPacket* toAVPacket() const; bool hasKeyFrame; bool isCorrupt; QByteArray data; + // time unit is s. qreal pts, duration; + qreal dts; + qint64 position; // position in source file byte stream + private: static const qreal kEndPts; + // TODO: implicity shared. can not use QSharedData + mutable AVPacket *avpkt; }; bool Packet::isValid() const diff --git a/src/VideoFrameExtractor.cpp b/src/VideoFrameExtractor.cpp index 8480872c6..5c0889494 100644 --- a/src/VideoFrameExtractor.cpp +++ b/src/VideoFrameExtractor.cpp @@ -1,5 +1,6 @@ #include "QtAV/VideoFrameExtractor.h" #include +#include #include #include #include