File indexing completed on 2025-01-19 03:57:05

0001 /*********************************************************
0002  * Copyright (C) 2021, Val Doroshchuk <valbok@gmail.com> *
0003  *                                                       *
0004  * This file is part of QtAVPlayer.                      *
0005  * Free Qt Media Player based on FFmpeg.                 *
0006  *********************************************************/
0007 
0008 #include "qaviodevice.h"
0009 #include <QMutex>
0010 #include <QWaitCondition>
0011 
0012 extern "C" {
0013 #include <libavformat/avio.h>
0014 #include <libavutil/mem.h>
0015 #include <libavutil/error.h>
0016 }
0017 
0018 QT_BEGIN_NAMESPACE
0019 
0020 struct ReadRequest
0021 {
0022     ReadRequest() = default;
0023     ReadRequest(unsigned char *data, int maxSize): data(data), maxSize(maxSize) { }
0024     int wroteBytes = 0;
0025     unsigned char *data = nullptr;
0026     int maxSize = 0;
0027 };
0028 
0029 class QAVIODevicePrivate
0030 {
0031     Q_DECLARE_PUBLIC(QAVIODevice)
0032 public:
0033     explicit QAVIODevicePrivate(QAVIODevice *q, const QSharedPointer<QIODevice> &device)
0034         : q_ptr(q)
0035         , device(device)
0036         , buffer(static_cast<unsigned char*>(av_malloc(buffer_size)))
0037         , ctx(avio_alloc_context(buffer, static_cast<int>(buffer_size), 0, this, &QAVIODevicePrivate::read, nullptr, !device->isSequential() ? &QAVIODevicePrivate::seek : nullptr))
0038     {
0039         if (!device->isSequential())
0040             ctx->seekable = AVIO_SEEKABLE_NORMAL;
0041     }
0042 
0043     ~QAVIODevicePrivate()
0044     {
0045         av_free(ctx);
0046     }
0047 
0048     void readData()
0049     {
0050         QMutexLocker locker(&mutex);
0051         // if no request or it is being processed
0052         if (readRequest.data == nullptr || readRequest.wroteBytes)
0053             return;
0054 
0055         readRequest.wroteBytes = !device->atEnd() ? device->read((char *)readRequest.data, readRequest.maxSize) : AVERROR_EOF;
0056         // Unblock the decoder thread when there is available bytes
0057         if (readRequest.wroteBytes) {
0058             waitCond.wakeAll();
0059             wakeRead = true;
0060         }
0061     }
0062 
0063     static int read(void *opaque, unsigned char *data, int maxSize)
0064     {
0065         auto d = static_cast<QAVIODevicePrivate *>(opaque);
0066         QMutexLocker locker(&d->mutex);
0067         if (d->aborted)
0068             return ECANCELED;
0069 
0070         d->readRequest = { data, maxSize };
0071         // When decoder thread is the same as current
0072         d->wakeRead = false;
0073         locker.unlock();
0074         // Reading is done on thread where the object is created
0075         QMetaObject::invokeMethod(d->q_ptr, [d] { d->readData(); }, nullptr);
0076         locker.relock();
0077         // Blocks until data is available
0078         if (!d->wakeRead)
0079             d->waitCond.wait(&d->mutex);
0080 
0081         int bytes = d->readRequest.wroteBytes;
0082         d->readRequest = {};
0083         return bytes;
0084     }
0085 
0086     static int64_t seek(void *opaque, int64_t offset, int whence)
0087     {
0088         auto d = static_cast<QAVIODevicePrivate *>(opaque);
0089         QMutexLocker locker(&d->mutex);
0090         if (d->aborted)
0091             return ECANCELED;
0092 
0093         int64_t pos = 0;
0094         bool wake = false;
0095         locker.unlock();
0096         QMetaObject::invokeMethod(d->q_ptr, [&] {
0097             QMutexLocker locker(&d->mutex);
0098             if (whence == AVSEEK_SIZE) {
0099                 pos = d->device->size() > 0 ? d->device->size() : 0;
0100             } else {
0101                 if (whence == SEEK_END)
0102                     offset = d->device->size() - offset;
0103                 else if (whence == SEEK_CUR)
0104                     offset = d->device->pos() + offset;
0105 
0106                 pos = d->device->seek(offset) ? d->device->pos() : -1;
0107             }
0108             d->waitCond.wakeAll();
0109             wake = true;
0110         }, nullptr);
0111 
0112         locker.relock();
0113         if (!wake)
0114             d->waitCond.wait(&d->mutex);
0115 
0116         return pos;
0117     }
0118 
0119     size_t buffer_size = 64 * 1024;
0120     QAVIODevice *q_ptr = nullptr;
0121     QSharedPointer<QIODevice> device;
0122     unsigned char *buffer = nullptr;
0123     AVIOContext *ctx = nullptr;
0124     mutable QMutex mutex;
0125     QWaitCondition waitCond;
0126     bool aborted = false;
0127     bool wakeRead = false;
0128     ReadRequest readRequest;
0129 };
0130 
0131 QAVIODevice::QAVIODevice(const QSharedPointer<QIODevice> &device, QObject *parent)
0132     : QObject(parent)
0133     , d_ptr(new QAVIODevicePrivate(this, device))
0134 {
0135     connect(device.get(), &QIODevice::readyRead, this, [this] {
0136         Q_D(QAVIODevice);
0137         d->readData();
0138     });
0139 }
0140 
0141 QAVIODevice::~QAVIODevice()
0142 {
0143     abort(true);
0144 }
0145 
0146 AVIOContext *QAVIODevice::ctx() const
0147 {
0148     return d_func()->ctx;
0149 }
0150 
0151 void QAVIODevice::abort(bool aborted)
0152 {
0153     Q_D(QAVIODevice);
0154     QMutexLocker locker(&d->mutex);
0155     d->aborted = aborted;
0156     d->waitCond.wakeAll();
0157 }
0158 
0159 void QAVIODevice::setBufferSize(size_t size)
0160 {
0161     Q_D(QAVIODevice);
0162     QMutexLocker locker(&d->mutex);
0163     d->buffer_size = size;
0164 }
0165 
0166 size_t QAVIODevice::bufferSize() const
0167 {
0168     Q_D(const QAVIODevice);
0169     QMutexLocker locker(&d->mutex);
0170     return d->buffer_size;
0171 }
0172 
0173 QT_END_NAMESPACE