1 /******************************************************************************
2 QtAV: Multimedia framework based on Qt and FFmpeg
3 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
4
5 * This file is part of QtAV
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 ******************************************************************************/
21 #include "QtAV/AVDemuxer.h"
22 #include "QtAV/MediaIO.h"
23 #include "QtAV/private/AVCompat.h"
24 #include <QtCore/QMutex>
25 #include <QtCore/QStringList>
26 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
27 #include <QtCore/QElapsedTimer>
28 #else
29 #include <QtCore/QTime>
30 typedef QTime QElapsedTimer;
31 #endif
32 #include "utils/internal.h"
33 #include "utils/Logger.h"
34
35 namespace QtAV {
36 static const char kFileScheme[] = "file:";
37
38 class AVDemuxer::InterruptHandler : public AVIOInterruptCB
39 {
40 public:
41 enum Action {
42 Unknown = -1,
43 Open,
44 FindStreamInfo,
45 Read
46 };
47 //default network timeout: 30000
InterruptHandler(AVDemuxer * demuxer,int timeout=30000)48 InterruptHandler(AVDemuxer* demuxer, int timeout = 30000)
49 : mStatus(0)
50 , mTimeout(timeout)
51 , mTimeoutAbort(true)
52 , mEmitError(true)
53 //, mLastTime(0)
54 , mAction(Unknown)
55 , mpDemuxer(demuxer)
56 {
57 callback = handleTimeout;
58 opaque = this;
59 }
~InterruptHandler()60 ~InterruptHandler() {
61 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
62 mTimer.invalidate();
63 #else
64 mTimer.stop();
65 #endif
66 }
begin(Action act)67 void begin(Action act) {
68 if (mStatus > 0)
69 mStatus = 0;
70 mEmitError = true;
71 mAction = act;
72 mTimer.start();
73 }
end()74 void end() {
75 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
76 mTimer.invalidate();
77 #else
78 mTimer.stop();
79 #endif
80 switch (mAction) {
81 case Read:
82 //mpDemuxer->setMediaStatus(BufferedMedia);
83 break;
84 default:
85 break;
86 }
87 mAction = Unknown;
88 }
getTimeout() const89 qint64 getTimeout() const { return mTimeout; }
setTimeout(qint64 timeout)90 void setTimeout(qint64 timeout) { mTimeout = timeout; }
setInterruptOnTimeout(bool value)91 bool setInterruptOnTimeout(bool value) {
92 if (mTimeoutAbort == value)
93 return false;
94 mTimeoutAbort = value;
95 if (mTimeoutAbort) {
96 mEmitError = true;
97 }
98 return true;
99 }
isInterruptOnTimeout() const100 bool isInterruptOnTimeout() const {return mTimeoutAbort;}
getStatus() const101 int getStatus() const { return mStatus; }
setStatus(int status)102 void setStatus(int status) { mStatus = status; }
103 /*
104 * metodo per interruzione loop ffmpeg
105 * @param void*obj: classe attuale
106 * @return
107 * >0 Interruzione loop di ffmpeg!
108 */
handleTimeout(void * obj)109 static int handleTimeout(void* obj) {
110 InterruptHandler* handler = static_cast<InterruptHandler*>(obj);
111 if (!handler) {
112 qWarning("InterruptHandler is null");
113 return -1;
114 }
115 //check manual interruption
116 if (handler->getStatus() < 0) {
117 qDebug("User Interrupt: -> quit!");
118 // DO NOT call setMediaStatus() here.
119 /* MUST make sure blocking functions (open, read) return before we change the status
120 * because demuxer may be closed in another thread at the same time if status is not LoadingMedia
121 * use handleError() after blocking functions return is good
122 */
123 // 1: blocking operation will be aborted.
124 return 1;//interrupt
125 }
126 // qApp->processEvents(); //FIXME: qml crash
127 switch (handler->mAction) {
128 case Unknown: //callback is not called between begin()/end()
129 //qWarning("Unknown timeout action");
130 break;
131 case Open:
132 case FindStreamInfo:
133 //qDebug("set loading media for %d from: %d", handler->mAction, handler->mpDemuxer->mediaStatus());
134 handler->mpDemuxer->setMediaStatus(LoadingMedia);
135 break;
136 case Read:
137 //handler->mpDemuxer->setMediaStatus(BufferingMedia);
138 default:
139 break;
140 }
141 if (handler->mTimeout < 0)
142 return 0;
143 if (!handler->mTimer.isValid()) {
144 //qDebug("timer is not valid, start it");
145 handler->mTimer.start();
146 //handler->mLastTime = handler->mTimer.elapsed();
147 return 0;
148 }
149 //use restart
150 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
151 if (!handler->mTimer.hasExpired(handler->mTimeout))
152 #else
153 if (handler->mTimer.elapsed() < handler->mTimeout)
154 #endif
155 return 0;
156 qDebug("status: %d, Timeout expired: %lld/%lld -> quit!", (int)handler->mStatus, handler->mTimer.elapsed(), handler->mTimeout);
157 handler->mTimer.invalidate();
158 if (handler->mStatus == 0) {
159 AVError::ErrorCode ec(AVError::ReadTimedout);
160 if (handler->mAction == Open) {
161 ec = AVError::OpenTimedout;
162 } else if (handler->mAction == FindStreamInfo) {
163 ec = AVError::ParseStreamTimedOut;
164 } else if (handler->mAction == Read) {
165 ec = AVError::ReadTimedout;
166 }
167 handler->mStatus = (int)ec;
168 // maybe changed in other threads
169 //handler->mStatus.testAndSetAcquire(0, ec);
170 }
171 if (handler->mTimeoutAbort)
172 return 1;
173 // emit demuxer error, handleerror
174 if (handler->mEmitError) {
175 handler->mEmitError = false;
176 AVError::ErrorCode ec = AVError::ErrorCode(handler->mStatus); //FIXME: maybe changed in other threads
177 QString es;
178 handler->mpDemuxer->handleError(AVERROR_EXIT, &ec, es);
179 }
180 return 0;
181 }
182 private:
183 int mStatus;
184 qint64 mTimeout;
185 bool mTimeoutAbort;
186 bool mEmitError;
187 //qint64 mLastTime;
188 Action mAction;
189 AVDemuxer *mpDemuxer;
190 QElapsedTimer mTimer;
191 };
192
193 class AVDemuxer::Private
194 {
195 public:
Private()196 Private()
197 : media_status(NoMedia)
198 , seekable(false)
199 , network(false)
200 , has_attached_pic(false)
201 , started(false)
202 , max_pts(0.0)
203 , eof(false)
204 , media_changed(true)
205 , buf_pos(0)
206 , stream(-1)
207 , format_ctx(0)
208 , input_format(0)
209 , input(0)
210 , seek_unit(SeekByTime)
211 , seek_type(AccurateSeek)
212 , dict(0)
213 , interrupt_hanlder(0)
214 {}
~Private()215 ~Private() {
216 delete interrupt_hanlder;
217 if (dict) {
218 av_dict_free(&dict);
219 dict = 0;
220 }
221 if (input) {
222 delete input;
223 input = 0;
224 }
225 }
226 void applyOptionsForDict();
227 void applyOptionsForContext();
resetStreams()228 void resetStreams() {
229 stream = -1;
230 if (media_changed)
231 astream = vstream = sstream = StreamInfo();
232 else
233 astream.avctx = vstream.avctx = sstream.avctx = 0;
234 audio_streams.clear();
235 video_streams.clear();
236 subtitle_streams.clear();
237 }
checkNetwork()238 void checkNetwork() {
239 // FIXME: is there a good way to check network? now use URLContext.flags == URL_PROTOCOL_FLAG_NETWORK
240 // not network: concat cache pipe avdevice crypto?
241 if (!file.isEmpty()
242 && file.contains(QLatin1String(":"))
243 && (file.startsWith(QLatin1String("http")) //http, https, httpproxy
244 || file.startsWith(QLatin1String("rtmp")) //rtmp{,e,s,te,ts}
245 || file.startsWith(QLatin1String("mms")) //mms{,h,t}
246 || file.startsWith(QLatin1String("ffrtmp")) //ffrtmpcrypt, ffrtmphttp
247 || file.startsWith(QLatin1String("rtp:"))
248 || file.startsWith(QLatin1String("rtsp:"))
249 || file.startsWith(QLatin1String("sctp:"))
250 || file.startsWith(QLatin1String("tcp:"))
251 || file.startsWith(QLatin1String("tls:"))
252 || file.startsWith(QLatin1String("udp:"))
253 || file.startsWith(QLatin1String("gopher:"))
254 )) {
255 network = true;
256 }
257 }
checkSeekable()258 bool checkSeekable() {
259 bool s = false;
260 if (!format_ctx)
261 return s;
262 // io.seekable: byte seeking
263 if (input)
264 s |= input->isSeekable();
265 if (format_ctx->pb)
266 s |= !!format_ctx->pb->seekable;
267 // format.read_seek: time seeking. For example, seeking on hls stream steps: find segment, read packet in segment and drop until desired pts got
268 s |= format_ctx->iformat->read_seek || format_ctx->iformat->read_seek2;
269 return s;
270 }
271 // set wanted_xx_stream. call openCodecs() to read new stream frames
272 // stream < 0 is choose best
273 bool setStream(AVDemuxer::StreamType st, int streamValue);
274 //called by loadFile(). if change to a new stream, call it(e.g. in AVPlayer)
275 bool prepareStreams();
276
277 MediaStatus media_status;
278 bool seekable;
279 bool network;
280 bool has_attached_pic;
281 bool started;
282 qreal max_pts; // max pts read
283 bool eof;
284 bool media_changed;
285 mutable qptrdiff buf_pos; // detect eof for dynamic size (growing) stream even if detectDynamicStreamInterval() is not set
286 Packet pkt;
287 int stream;
288 QList<int> audio_streams, video_streams, subtitle_streams;
289 AVFormatContext *format_ctx;
290 //copy the info, not parse the file when constructed, then need member vars
291 QString file;
292 QString file_orig;
293 AVInputFormat *input_format;
294 QString format_forced;
295 MediaIO *input;
296
297 SeekUnit seek_unit;
298 SeekType seek_type;
299
300 AVDictionary *dict;
301 QVariantHash options;
302 typedef struct StreamInfo {
StreamInfoQtAV::AVDemuxer::Private::StreamInfo303 StreamInfo()
304 : stream(-1)
305 , wanted_stream(-1)
306 , index(-1)
307 , wanted_index(-1)
308 , avctx(0)
309 {}
310 // wanted_stream is REQUIRED. e.g. always set -1 to indicate the default stream, -2 to disable
311 int stream, wanted_stream; // -1 default, selected by ff
312 int index, wanted_index; // index in a kind of streams
313 AVCodecContext *avctx;
314 } StreamInfo;
315 StreamInfo astream, vstream, sstream;
316
317 AVDemuxer::InterruptHandler *interrupt_hanlder;
318 QMutex mutex; //TODO: remove if load, read, seek is called in 1 thread
319 };
320
AVDemuxer(QObject * parent)321 AVDemuxer::AVDemuxer(QObject *parent)
322 : QObject(parent)
323 , d(new Private())
324 {
325 // TODO: xxx_register_all already use static var
326 class AVInitializer {
327 public:
328 AVInitializer() {
329 #if !AVCODEC_STATIC_REGISTER
330 avcodec_register_all();
331 #endif
332 #if QTAV_HAVE(AVDEVICE)
333 avdevice_register_all();
334 #endif
335 #if !AVFORMAT_STATIC_REGISTER
336 av_register_all();
337 #endif
338 avformat_network_init();
339 }
340 ~AVInitializer() {
341 avformat_network_deinit();
342 }
343 };
344 static AVInitializer sAVInit;
345 Q_UNUSED(sAVInit);
346 d->interrupt_hanlder = new InterruptHandler(this);
347 }
348
~AVDemuxer()349 AVDemuxer::~AVDemuxer()
350 {
351 unload();
352 }
353
getFFmpegInputFormats(QStringList * formats,QStringList * extensions)354 static void getFFmpegInputFormats(QStringList* formats, QStringList* extensions)
355 {
356 static QStringList exts;
357 static QStringList fmts;
358 if (exts.isEmpty() && fmts.isEmpty()) {
359 QStringList e, f;
360 #if AVFORMAT_STATIC_REGISTER
361 const AVInputFormat *i = NULL;
362 void* it = NULL;
363 while ((i = av_demuxer_iterate(&it))) {
364 #else
365 AVInputFormat *i = NULL;
366 av_register_all(); // MUST register all input/output formats
367 while ((i = av_iformat_next(i))) {
368 #endif
369 if (i->extensions)
370 e << QString::fromLatin1(i->extensions).split(QLatin1Char(','), QString::SkipEmptyParts);
371 if (i->name)
372 f << QString::fromLatin1(i->name).split(QLatin1Char(','), QString::SkipEmptyParts);
373 }
374 foreach (const QString& v, e) {
375 exts.append(v.trimmed());
376 }
377 foreach (const QString& v, f) {
378 fmts.append(v.trimmed());
379 }
380 exts.removeDuplicates();
381 fmts.removeDuplicates();
382 }
383 if (formats)
384 *formats = fmts;
385 if (extensions)
386 *extensions = exts;
387 }
388
389 const QStringList& AVDemuxer::supportedFormats()
390 {
391 static QStringList fmts;
392 if (fmts.isEmpty())
393 getFFmpegInputFormats(&fmts, NULL);
394 return fmts;
395 }
396
397 const QStringList& AVDemuxer::supportedExtensions()
398 {
399 static QStringList exts;
400 if (exts.isEmpty())
401 getFFmpegInputFormats(NULL, &exts);
402 return exts;
403 }
404
405 const QStringList &AVDemuxer::supportedProtocols()
406 {
407 static QStringList protocols;
408 if (!protocols.isEmpty())
409 return protocols;
410 #if QTAV_HAVE(AVDEVICE)
411 protocols << QStringLiteral("avdevice");
412 #endif
413 #if !AVFORMAT_STATIC_REGISTER
414 av_register_all(); // MUST register all input/output formats
415 #endif
416 void* opq = 0;
417 const char* protocol = avio_enum_protocols(&opq, 0);
418 while (protocol) {
419 // static string, no deep copy needed. but QByteArray::fromRawData(data,size) assumes data is not null terminated and we must give a size
420 protocols.append(QString::fromUtf8(protocol));
421 protocol = avio_enum_protocols(&opq, 0);
422 }
423 return protocols;
424 }
425
426 MediaStatus AVDemuxer::mediaStatus() const
427 {
428 return d->media_status;
429 }
430
431 bool AVDemuxer::readFrame()
432 {
433 QMutexLocker lock(&d->mutex);
434 Q_UNUSED(lock);
435 if (!d->format_ctx)
436 return false;
437 d->pkt = Packet();
438 // no lock required because in AVDemuxThread read and seek are in the same thread
439 AVPacket packet;
440 av_init_packet(&packet);
441 d->interrupt_hanlder->begin(InterruptHandler::Read);
442 int ret = av_read_frame(d->format_ctx, &packet); //0: ok, <0: error/end
443 d->interrupt_hanlder->end();
444 // TODO: why return 0 if interrupted by user?
445 if (ret < 0) {
446 //end of file. FIXME: why no d->eof if replaying by seek(0)?
447 // ffplay also check pb && pb->error and exit read thread
448 if (ret == AVERROR_EOF
449 || avio_feof(d->format_ctx->pb)) {
450 if (!d->eof) {
451 if (getInterruptStatus()) {
452 AVError::ErrorCode ec(AVError::ReadError);
453 QString msg(tr("error reading stream data"));
454 handleError(ret, &ec, msg);
455 }
456 if (mediaStatus() != StalledMedia) {
457 d->eof = true;
458 #if 0 // EndOfMedia when demux thread finished
459 d->started = false;
460 setMediaStatus(EndOfMedia);
461 Q_EMIT finished();
462 #endif
463 qDebug("End of file. erreof=%d feof=%d", ret == AVERROR_EOF, avio_feof(d->format_ctx->pb));
464 }
465 }
466 av_packet_unref(&packet); //important!
467 return false;
468 }
469 if (ret == AVERROR(EAGAIN)) {
470 qWarning("demuxer EAGAIN :%s", av_err2str(ret));
471 av_packet_unref(&packet); //important!
472 return false;
473 }
474 AVError::ErrorCode ec(AVError::ReadError);
475 QString msg(tr("error reading stream data"));
476 handleError(ret, &ec, msg);
477 qWarning("[AVDemuxer] error: %s", av_err2str(ret));
478 av_packet_unref(&packet); //important!
479 return false;
480 }
481 d->stream = packet.stream_index;
482 //check whether the 1st frame is alreay got. emit only once
483 if (!d->started) {
484 d->started = true;
485 Q_EMIT started();
486 }
487 if (d->stream != videoStream() && d->stream != audioStream() && d->stream != subtitleStream()) {
488 //qWarning("[AVDemuxer] unknown stream index: %d", stream);
489 av_packet_unref(&packet); //important!
490 return false;
491 }
492 // TODO: v4l2 copy
493 d->pkt = Packet::fromAVPacket(&packet, av_q2d(d->format_ctx->streams[d->stream]->time_base));
494 av_packet_unref(&packet); //important!
495 d->eof = false;
496 if (d->pkt.pts > qreal(duration())/1000.0) {
497 d->max_pts = d->pkt.pts;
498 }
499 return true;
500 }
501
502 Packet AVDemuxer::packet() const
503 {
504 return d->pkt;
505 }
506
507 int AVDemuxer::stream() const
508 {
509 return d->stream;
510 }
511
512 bool AVDemuxer::atEnd() const
513 {
514 if (!d->format_ctx)
515 return false;
516 if (d->format_ctx->pb) {
517 AVIOContext *pb = d->format_ctx->pb;
518 //qDebug("pb->error: %#x, eof: %d, pos: %lld, bufptr: %p", pb->error, pb->eof_reached, pb->pos, pb->buf_ptr);
519 if (d->eof && (qptrdiff)pb->buf_ptr == d->buf_pos)
520 return true;
521 d->buf_pos = (qptrdiff)pb->buf_ptr;
522 return false;
523 }
524 return d->eof;
525 }
526
527 bool AVDemuxer::isSeekable() const
528 {
529 return d->seekable;
530 }
531
532 void AVDemuxer::setSeekUnit(SeekUnit unit)
533 {
534 d->seek_unit = unit;
535 }
536
537 SeekUnit AVDemuxer::seekUnit() const
538 {
539 return d->seek_unit;
540 }
541
542 void AVDemuxer::setSeekType(SeekType target)
543 {
544 d->seek_type = target;
545 }
546
547 SeekType AVDemuxer::seekType() const
548 {
549 return d->seek_type;
550 }
551
552 //TODO: seek by byte
553 bool AVDemuxer::seek(qint64 pos)
554 {
555 if (!isLoaded())
556 return false;
557 //duration: unit is us (10^-6 s, AV_TIME_BASE)
558 qint64 upos = pos*1000LL; // TODO: av_rescale
559 if (upos > startTimeUs() + durationUs() || pos < 0LL) {
560 if (pos >= 0LL && d->input && d->input->isSeekable() && d->input->isVariableSize()) {
561 qDebug("Seek for variable size hack. %lld %.2f. valid range [%lld, %lld]", upos, double(upos)/double(durationUs()), startTimeUs(), startTimeUs()+durationUs());
562 } else if (d->max_pts > qreal(duration())/1000.0) { //FIXME
563 qDebug("Seek (%lld) when video duration is growing %lld=>%lld", pos, duration(), qint64(d->max_pts*1000.0));
564 } else {
565 qWarning("Invalid seek position %lld %.2f. valid range [%lld, %lld]", upos, double(upos)/double(durationUs()), startTimeUs(), startTimeUs()+durationUs());
566 return false;
567 }
568 }
569 d->eof = false;
570 // no lock required because in AVDemuxThread read and seek are in the same thread
571 #if 0
572 //t: unit is s
573 qreal t = q;// * (double)d->format_ctx->duration; //
574 int ret = av_seek_frame(d->format_ctx, -1, (int64_t)(t*AV_TIME_BASE), t > d->pkt.pts ? 0 : AVSEEK_FLAG_BACKWARD);
575 qDebug("[AVDemuxer] seek to %f %f %lld / %lld", q, d->pkt.pts, (int64_t)(t*AV_TIME_BASE), durationUs());
576 #else
577 //TODO: d->pkt.pts may be 0, compute manually.
578
579 bool backward = d->seek_type == AccurateSeek || upos <= (int64_t)(d->pkt.pts*AV_TIME_BASE);
580 //qDebug("[AVDemuxer] seek to %f %f %lld / %lld backward=%d", double(upos)/double(durationUs()), d->pkt.pts, upos, durationUs(), backward);
581 //AVSEEK_FLAG_BACKWARD has no effect? because we know the timestamp
582 // FIXME: back flag is opposite? otherwise seek is bad and may crash?
583 /* If stread->inputdex is (-1), a default
584 * stream is selected, and timestamp is automatically converted
585 * from AV_TIME_BASE units to the stream specific time_base.
586 */
587 int seek_flag = (backward ? AVSEEK_FLAG_BACKWARD : 0);
588 if (d->seek_type == AccurateSeek) {
589 seek_flag = AVSEEK_FLAG_BACKWARD;
590 }
591 if (d->seek_type == AnyFrameSeek) {
592 seek_flag |= AVSEEK_FLAG_ANY;
593 }
594 //qDebug("seek flag: %d", seek_flag);
595 //bool seek_bytes = !!(d->format_ctx->iformat->flags & AVFMT_TS_DISCONT) && strcmp("ogg", d->format_ctx->iformat->name);
596 int ret = av_seek_frame(d->format_ctx, -1, upos, seek_flag);
597 //int ret = avformat_seek_file(d->format_ctx, -1, INT64_MIN, upos, upos, seek_flag);
598 //avformat_seek_file()
599 if (ret < 0 && (seek_flag & AVSEEK_FLAG_BACKWARD)) {
600 // seek to 0?
601 qDebug("av_seek_frame error with flag AVSEEK_FLAG_BACKWARD: %s. try to seek without the flag", av_err2str(ret));
602 seek_flag &= ~AVSEEK_FLAG_BACKWARD;
603 ret = av_seek_frame(d->format_ctx, -1, upos, seek_flag);
604 }
605 //qDebug("av_seek_frame ret: %d", ret);
606 #endif
607 if (ret < 0) {
608 AVError::ErrorCode ec(AVError::SeekError);
609 QString msg(tr("seek error"));
610 handleError(ret, &ec, msg);
611 return false;
612 }
613 // TODO: replay
614 if (upos <= startTime()) {
615 qDebug("************seek to beginning. started = false");
616 d->started = false; //???
617 if (d->astream.avctx)
618 d->astream.avctx->frame_number = 0;
619 if (d->vstream.avctx)
620 d->vstream.avctx->frame_number = 0; //TODO: why frame_number not changed after seek?
621 if (d->sstream.avctx)
622 d->sstream.avctx->frame_number = 0;
623 }
624 return true;
625 }
626
627 bool AVDemuxer::seek(qreal q)
628 {
629 if (duration() <= 0) {
630 qWarning("duration() must be valid for percentage seek");
631 return false;
632 }
633 return seek(qint64(q*(double)duration()));
634 }
635
636 QString AVDemuxer::fileName() const
637 {
638 return d->file_orig;
639 }
640
641 QIODevice* AVDemuxer::ioDevice() const
642 {
643 if (!d->input)
644 return 0;
645 if (d->input->name() != QLatin1String("QIODevice"))
646 return 0;
647 return d->input->property("device").value<QIODevice*>();
648 }
649
650 MediaIO* AVDemuxer::mediaIO() const
651 {
652 return d->input;
653 }
654
655 bool AVDemuxer::setMedia(const QString &fileName)
656 {
657 if (d->input) {
658 delete d->input;
659 d->input = 0;
660 }
661 d->file_orig = fileName;
662 const QString url_old(d->file);
663 d->file = fileName.trimmed();
664 if (d->file.startsWith(QLatin1String("mms:")))
665 d->file.insert(3, QLatin1Char('h'));
666 else if (d->file.startsWith(QLatin1String(kFileScheme)))
667 d->file = Internal::Path::toLocal(d->file);
668 int colon = d->file.indexOf(QLatin1Char(':'));
669 if (colon == 1) {
670 #ifdef Q_OS_WINRT
671 d->file.prepend(QStringLiteral("qfile:"));
672 #endif
673 }
674 d->media_changed = url_old != d->file;
675 if (d->media_changed) {
676 d->format_forced.clear();
677 }
678 // a local file. return here to avoid protocol checking. If path contains ":", protocol checking will fail
679 if (d->file.startsWith(QLatin1Char('/')))
680 return d->media_changed;
681 // use MediaIO to support protocols not supported by ffmpeg
682 colon = d->file.indexOf(QLatin1Char(':'));
683 if (colon >= 0) {
684 #ifdef Q_OS_WIN
685 if (colon == 1 && d->file.at(0).isLetter())
686 return d->media_changed;
687 #endif
688 const QString scheme = colon == 0 ? QStringLiteral("qrc") : d->file.left(colon);
689 // supportedProtocols() is not complete. so try MediaIO 1st, if not found, fallback to libavformat
690 d->input = MediaIO::createForProtocol(scheme);
691 if (d->input) {
692 d->input->setUrl(d->file);
693 }
694 }
695 return d->media_changed;
696 }
697
698 bool AVDemuxer::setMedia(QIODevice* device)
699 {
700 d->file = QString();
701 d->file_orig = QString();
702 if (d->input) {
703 if (d->input->name() != QLatin1String("QIODevice")) {
704 delete d->input;
705 d->input = 0;
706 }
707 }
708 if (!d->input)
709 d->input = MediaIO::create("QIODevice");
710 QIODevice* old_dev = d->input->property("device").value<QIODevice*>();
711 d->media_changed = old_dev != device;
712 if (d->media_changed) {
713 d->format_forced.clear();
714 }
715 d->input->setProperty("device", QVariant::fromValue(device)); //open outside?
716 return d->media_changed;
717 }
718
719 bool AVDemuxer::setMedia(MediaIO *in)
720 {
721 d->media_changed = in != d->input;
722 if (d->media_changed) {
723 d->format_forced.clear();
724 }
725 d->file = QString();
726 d->file_orig = QString();
727 if (!d->input)
728 d->input = in;
729 if (d->input != in) {
730 delete d->input;
731 d->input = in;
732 }
733 return d->media_changed;
734 }
735
736 void AVDemuxer::setFormat(const QString &fmt)
737 {
738 d->format_forced = fmt;
739 }
740
741 QString AVDemuxer::formatForced() const
742 {
743 return d->format_forced;
744 }
745
746 bool AVDemuxer::load()
747 {
748 unload();
749 qDebug("all closed and reseted");
750
751 if (d->file.isEmpty() && !d->input) {
752 setMediaStatus(NoMedia);
753 return false;
754 }
755 QMutexLocker lock(&d->mutex); // TODO: load in AVDemuxThread and remove all locks
756 Q_UNUSED(lock);
757 setMediaStatus(LoadingMedia);
758 d->checkNetwork();
759 #if QTAV_HAVE(AVDEVICE)
760 static const QString avd_scheme(QStringLiteral("avdevice:"));
761 if (d->file.startsWith(avd_scheme)) {
762 QStringList parts = d->file.split(QStringLiteral(":"));
763 int s0 = avd_scheme.size();
764 const int s1 = d->file.indexOf(QChar(':'), s0);
765 if (s1 < 0) {
766 qDebug("invalid avdevice specification");
767 setMediaStatus(InvalidMedia);
768 return false;
769 }
770 if (d->file.at(s0) == QChar('/') && d->file.at(s0+1) == QChar('/')) {
771 // avdevice://avfoundation:device_name
772 s0 += 2;
773 } // else avdevice:video4linux2:file_name
774 d->input_format = av_find_input_format(d->file.mid(s0, s1-s0).toUtf8().constData());
775 d->file = d->file.mid(s1+1);
776 }
777 #endif
778 //alloc av format context
779 if (!d->format_ctx)
780 d->format_ctx = avformat_alloc_context();
781 d->format_ctx->flags |= AVFMT_FLAG_GENPTS;
782 //install interrupt callback
783 d->format_ctx->interrupt_callback = *d->interrupt_hanlder;
784
785 d->applyOptionsForDict();
786 // check special dict keys
787 // d->format_forced can be set from AVFormatContext.format_whitelist
788 if (!d->format_forced.isEmpty()) {
789 d->input_format = av_find_input_format(d->format_forced.toUtf8().constData());
790 qDebug() << "force format: " << d->format_forced;
791 }
792 int ret = 0;
793 // used dict entries will be removed in avformat_open_input
794 d->interrupt_hanlder->begin(InterruptHandler::Open);
795 if (d->input) {
796 if (d->input->accessMode() == MediaIO::Write) {
797 qWarning("wrong MediaIO accessMode. MUST be Read");
798 }
799 d->format_ctx->pb = (AVIOContext*)d->input->avioContext();
800 d->format_ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
801 qDebug("avformat_open_input: d->format_ctx:'%p'..., MediaIO('%s'): %p", d->format_ctx, d->input->name().toUtf8().constData(), d->input);
802 ret = avformat_open_input(&d->format_ctx, "MediaIO", d->input_format, d->options.isEmpty() ? NULL : &d->dict);
803 qDebug("avformat_open_input: (with MediaIO) ret:%d", ret);
804 } else {
805 qDebug("avformat_open_input: d->format_ctx:'%p', url:'%s'...",d->format_ctx, qPrintable(d->file));
806 ret = avformat_open_input(&d->format_ctx, d->file.toUtf8().constData(), d->input_format, d->options.isEmpty() ? NULL : &d->dict);
807 qDebug("avformat_open_input: url:'%s' ret:%d",qPrintable(d->file), ret);
808 }
809 d->interrupt_hanlder->end();
810 if (ret < 0) {
811 // d->format_ctx is 0
812 AVError::ErrorCode ec = AVError::OpenError;
813 QString msg = tr("failed to open media");
814 handleError(ret, &ec, msg);
815 qWarning() << "Can't open media: " << msg;
816 if (mediaStatus() == LoadingMedia) //workaround for timeout but not interrupted
817 setMediaStatus(InvalidMedia);
818 Q_EMIT unloaded(); //context not ready. so will not emit in unload()
819 return false;
820 }
821 //deprecated
822 //if(av_find_stread->inputfo(d->format_ctx)<0) {
823 //TODO: avformat_find_stread->inputfo is too slow, only useful for some video format
824 d->interrupt_hanlder->begin(InterruptHandler::FindStreamInfo);
825 ret = avformat_find_stream_info(d->format_ctx, NULL);
826 d->interrupt_hanlder->end();
827
828 if (ret < 0) {
829 setMediaStatus(InvalidMedia);
830 AVError::ErrorCode ec(AVError::ParseStreamError);
831 QString msg(tr("failed to find stream info"));
832 handleError(ret, &ec, msg);
833 qWarning() << "Can't find stream info: " << msg;
834 // context is ready. unloaded() will be emitted in unload()
835 if (mediaStatus() == LoadingMedia) //workaround for timeout but not interrupted
836 setMediaStatus(InvalidMedia);
837 return false;
838 }
839
840 if (!d->prepareStreams()) {
841 if (mediaStatus() == LoadingMedia)
842 setMediaStatus(InvalidMedia);
843 return false;
844 }
845 d->started = false;
846 setMediaStatus(LoadedMedia);
847 Q_EMIT loaded();
848 const bool was_seekable = d->seekable;
849 d->seekable = d->checkSeekable();
850 if (was_seekable != d->seekable)
851 Q_EMIT seekableChanged();
852 qDebug("avfmtctx.flags: %d, iformat.flags", d->format_ctx->flags, d->format_ctx->iformat->flags);
853 if (getInterruptStatus() < 0) {
854 QString msg;
855 qDebug("AVERROR_EXIT: %d", AVERROR_EXIT);
856 handleError(AVERROR_EXIT, 0, msg);
857 qWarning() << "User interupted: " << msg;
858 return false;
859 }
860 return true;
861 }
862
863 bool AVDemuxer::unload()
864 {
865 QMutexLocker lock(&d->mutex);
866 Q_UNUSED(lock);
867 /*
868 if (d->seekable) {
869 d->seekable = false; //
870 Q_EMIT seekableChanged();
871 }
872 */
873 d->network = false;
874 d->has_attached_pic = false;
875 d->eof = false; // true and set false in load()?
876 d->buf_pos = 0;
877 d->started = false;
878 d->max_pts = 0.0;
879 d->resetStreams();
880 d->interrupt_hanlder->setStatus(0);
881 //av_close_input_file(d->format_ctx); //deprecated
882 if (d->format_ctx) {
883 qDebug("closing d->format_ctx");
884 avformat_close_input(&d->format_ctx); //libavf > 53.10.0
885 d->format_ctx = 0;
886 d->input_format = 0;
887 // no delete. may be used in next load
888 if (d->input)
889 d->input->release();
890 Q_EMIT unloaded();
891 }
892 return true;
893 }
894
895 bool AVDemuxer::isLoaded() const
896 {
897 return d->format_ctx && (d->astream.avctx || d->vstream.avctx || d->sstream.avctx);
898 }
899
900 bool AVDemuxer::hasAttacedPicture() const
901 {
902 return d->has_attached_pic;
903 }
904
905 bool AVDemuxer::setStreamIndex(StreamType st, int index)
906 {
907 QList<int> *streams = 0;
908 Private::StreamInfo *si = 0;
909 if (st == AudioStream) { // TODO: use a struct
910 si = &d->astream;
911 streams = &d->audio_streams;
912 } else if (st == VideoStream) {
913 si = &d->vstream;
914 streams = &d->video_streams;
915 } else if (st == SubtitleStream) {
916 si = &d->sstream;
917 streams = &d->subtitle_streams;
918 }
919 if (!si) {
920 qWarning("stream type %d for index %d not found", st, index);
921 return false;
922 }
923 if (index >= streams->size()) {// || index < 0) { //TODO: disable if <0
924 //si->wanted_stream = -1;
925 qWarning("invalid index %d (valid is 0~%d) for stream type %d.", index, streams->size(), st);
926 return false;
927 }
928 if (index < 0) {
929 qDebug("disable %d stream", st);
930 si->stream = -1;
931 si->wanted_index = -1;
932 si->wanted_stream = -1;
933 return true;
934 }
935 if (!d->setStream(st, streams->at(index)))
936 return false;
937 si->wanted_index = index;
938 return true;
939 }
940
941 AVFormatContext* AVDemuxer::formatContext()
942 {
943 return d->format_ctx;
944 }
945
946 QString AVDemuxer::formatName() const
947 {
948 if (!d->format_ctx)
949 return QString();
950 return QLatin1String(d->format_ctx->iformat->name);
951 }
952
953 QString AVDemuxer::formatLongName() const
954 {
955 if (!d->format_ctx)
956 return QString();
957 return QLatin1String(d->format_ctx->iformat->long_name);
958 }
959
960 // convert to s using AV_TIME_BASE then *1000?
961 qint64 AVDemuxer::startTime() const
962 {
963 return startTimeUs()/1000LL; //TODO: av_rescale
964 }
965
966 qint64 AVDemuxer::duration() const
967 {
968 return durationUs()/1000LL; //time base: AV_TIME_BASE TODO: av_rescale
969 }
970
971 //AVFrameContext use AV_TIME_BASE as time base. AVStream use their own timebase
972 qint64 AVDemuxer::startTimeUs() const
973 {
974 // start time may be not null for network stream
975 if (!d->format_ctx || d->format_ctx->start_time == AV_NOPTS_VALUE)
976 return 0;
977 return d->format_ctx->start_time;
978 }
979
980 qint64 AVDemuxer::durationUs() const
981 {
982 if (!d->format_ctx || d->format_ctx->duration == AV_NOPTS_VALUE)
983 return 0;
984 return d->format_ctx->duration; //time base: AV_TIME_BASE
985 }
986
987 int AVDemuxer::bitRate() const
988 {
989 return d->format_ctx->bit_rate;
990 }
991
992 qreal AVDemuxer::frameRate() const
993 {
994 if (videoStream() < 0)
995 return 0;
996 AVStream *stream = d->format_ctx->streams[videoStream()];
997 return av_q2d(stream->avg_frame_rate);
998 //codecCtx->time_base.den / codecCtx->time_base.num
999 }
1000
1001 qint64 AVDemuxer::frames(int stream) const
1002 {
1003 if (stream == -1) {
1004 stream = videoStream();
1005 if (stream < 0)
1006 stream = audioStream();
1007 if (stream < 0)
1008 return 0;
1009 }
1010 return d->format_ctx->streams[stream]->nb_frames;
1011 }
1012
1013 int AVDemuxer::currentStream(StreamType st) const
1014 {
1015 if (st == AudioStream)
1016 return audioStream();
1017 else if (st == VideoStream)
1018 return videoStream();
1019 else if (st == SubtitleStream)
1020 return subtitleStream();
1021 return -1;
1022 }
1023
1024 QList<int> AVDemuxer::streams(StreamType st) const
1025 {
1026 if (st == AudioStream)
1027 return audioStreams();
1028 else if (st == VideoStream)
1029 return videoStreams();
1030 else if (st == SubtitleStream)
1031 return subtitleStreams();
1032 return QList<int>();
1033 }
1034
1035 int AVDemuxer::audioStream() const
1036 {
1037 return d->astream.stream;
1038 }
1039
1040 QList<int> AVDemuxer::audioStreams() const
1041 {
1042 return d->audio_streams;
1043 }
1044
1045 int AVDemuxer::videoStream() const
1046 {
1047 return d->vstream.stream;
1048 }
1049
1050 QList<int> AVDemuxer::videoStreams() const
1051 {
1052 return d->video_streams;
1053 }
1054
1055 int AVDemuxer::subtitleStream() const
1056 {
1057 return d->sstream.stream;
1058 }
1059
1060 QList<int> AVDemuxer::subtitleStreams() const
1061 {
1062 return d->subtitle_streams;
1063 }
1064
1065 AVCodecContext* AVDemuxer::audioCodecContext(int stream) const
1066 {
1067 if (stream < 0)
1068 return d->astream.avctx;
1069 if (stream > (int)d->format_ctx->nb_streams)
1070 return 0;
1071 AVCodecContext *avctx = d->format_ctx->streams[stream]->codec;
1072 if (avctx->codec_type == AVMEDIA_TYPE_AUDIO)
1073 return avctx;
1074 return 0;
1075 }
1076
1077 AVCodecContext* AVDemuxer::videoCodecContext(int stream) const
1078 {
1079 if (stream < 0)
1080 return d->vstream.avctx;
1081 if (stream > (int)d->format_ctx->nb_streams)
1082 return 0;
1083 AVCodecContext *avctx = d->format_ctx->streams[stream]->codec;
1084 if (avctx->codec_type == AVMEDIA_TYPE_VIDEO)
1085 return avctx;
1086 return 0;
1087 }
1088
1089 AVCodecContext* AVDemuxer::subtitleCodecContext(int stream) const
1090 {
1091 if (stream < 0)
1092 return d->sstream.avctx;
1093 if (stream > (int)d->format_ctx->nb_streams)
1094 return 0;
1095 AVCodecContext *avctx = d->format_ctx->streams[stream]->codec;
1096 if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
1097 return avctx;
1098 return 0;
1099 }
1100
1101 /**
1102 * @brief getInterruptTimeout return the interrupt timeout
1103 * @return
1104 */
1105 qint64 AVDemuxer::getInterruptTimeout() const
1106 {
1107 return d->interrupt_hanlder->getTimeout();
1108 }
1109
1110 /**
1111 * @brief setInterruptTimeout set the interrupt timeout
1112 * @param timeout
1113 * @return
1114 */
1115 void AVDemuxer::setInterruptTimeout(qint64 timeout)
1116 {
1117 d->interrupt_hanlder->setTimeout(timeout);
1118 }
1119
1120 bool AVDemuxer::isInterruptOnTimeout() const
1121 {
1122 return d->interrupt_hanlder->isInterruptOnTimeout();
1123 }
1124
1125 void AVDemuxer::setInterruptOnTimeout(bool value)
1126 {
1127 d->interrupt_hanlder->setInterruptOnTimeout(value);
1128 }
1129
1130 int AVDemuxer::getInterruptStatus() const
1131 {
1132 return d->interrupt_hanlder->getStatus();
1133 }
1134
1135 void AVDemuxer::setInterruptStatus(int interrupt)
1136 {
1137 d->interrupt_hanlder->setStatus(interrupt);
1138 }
1139
1140 void AVDemuxer::setOptions(const QVariantHash &dict)
1141 {
1142 d->options = dict;
1143 d->applyOptionsForContext(); // apply even if avformat context is open
1144 }
1145
1146 QVariantHash AVDemuxer::options() const
1147 {
1148 return d->options;
1149 }
1150
1151 void AVDemuxer::setMediaStatus(MediaStatus status)
1152 {
1153 if (d->media_status == status)
1154 return;
1155
1156 //if (status == NoMedia || status == InvalidMedia)
1157 // Q_EMIT durationChanged(0);
1158
1159 d->media_status = status;
1160
1161 Q_EMIT mediaStatusChanged(d->media_status);
1162 }
1163
1164 void AVDemuxer::Private::applyOptionsForDict()
1165 {
1166 if (dict) {
1167 av_dict_free(&dict);
1168 dict = 0; //aready 0 in av_free
1169 }
1170 if (options.isEmpty())
1171 return;
1172 QVariant opt(options);
1173 if (options.contains(QStringLiteral("avformat")))
1174 opt = options.value(QStringLiteral("avformat"));
1175 Internal::setOptionsToDict(opt, &dict);
1176
1177 if (opt.type() == QVariant::Map) {
1178 QVariantMap avformat_dict(opt.toMap());
1179 if (avformat_dict.contains(QStringLiteral("format_whitelist"))) {
1180 const QString fmts(avformat_dict[QStringLiteral("format_whitelist")].toString());
1181 if (!fmts.contains(QLatin1Char(',')) && !fmts.isEmpty())
1182 format_forced = fmts; // reset when media changed
1183 }
1184 } else if (opt.type() == QVariant::Hash) {
1185 QVariantHash avformat_dict(opt.toHash());
1186 if (avformat_dict.contains(QStringLiteral("format_whitelist"))) {
1187 const QString fmts(avformat_dict[QStringLiteral("format_whitelist")].toString());
1188 if (!fmts.contains(QLatin1Char(',')) && !fmts.isEmpty())
1189 format_forced = fmts; // reset when media changed
1190 }
1191 }
1192 }
1193
1194 void AVDemuxer::Private::applyOptionsForContext()
1195 {
1196 if (!format_ctx)
1197 return;
1198 if (options.isEmpty()) {
1199 //av_opt_set_defaults(format_ctx); //can't set default values! result maybe unexpected
1200 return;
1201 }
1202 QVariant opt(options);
1203 if (options.contains(QStringLiteral("avformat")))
1204 opt = options.value(QStringLiteral("avformat"));
1205 Internal::setOptionsToFFmpegObj(opt, format_ctx);
1206 }
1207
1208 void AVDemuxer::handleError(int averr, AVError::ErrorCode *errorCode, QString &msg)
1209 {
1210 if (averr >= 0)
1211 return;
1212 // d->format_ctx is 0
1213 // TODO: why sometimes AVERROR_EXIT does not work?
1214 bool interrupted = (averr == AVERROR_EXIT) || getInterruptStatus();
1215 QString err_msg(msg);
1216 if (interrupted) { // interrupted by callback, so can not determine whether the media is valid
1217 // insufficient buffering or other interruptions
1218 if (getInterruptStatus() < 0) {
1219 setMediaStatus(StalledMedia);
1220 Q_EMIT userInterrupted();
1221 err_msg += QStringLiteral(" [%1]").arg(tr("interrupted by user"));
1222 } else {
1223 // FIXME: if not interupt on timeout and ffmpeg exits, still LoadingMedia
1224 if (isInterruptOnTimeout())
1225 setMediaStatus(StalledMedia);
1226 // averr is eof for open timeout
1227 err_msg += QStringLiteral(" [%1]").arg(tr("timeout"));
1228 }
1229 } else {
1230 if (mediaStatus() == LoadingMedia)
1231 setMediaStatus(InvalidMedia);
1232 }
1233 msg = err_msg;
1234 if (!errorCode)
1235 return;
1236 AVError::ErrorCode ec(*errorCode);
1237 if (averr == AVERROR_INVALIDDATA) { // leave it if reading
1238 if (*errorCode == AVError::OpenError)
1239 ec = AVError::FormatError;
1240 } else {
1241 // Input/output error etc.
1242 if (d->network)
1243 ec = AVError::NetworkError;
1244 }
1245 AVError err(ec, err_msg, averr);
1246 Q_EMIT error(err);
1247 *errorCode = ec;
1248 }
1249
1250 bool AVDemuxer::Private::setStream(AVDemuxer::StreamType st, int streamValue)
1251 {
1252 if (streamValue < -1)
1253 streamValue = -1;
1254 QList<int> *streams = 0;
1255 Private::StreamInfo *si = 0;
1256 if (st == AudioStream) { // TODO: use a struct
1257 si = &astream;
1258 streams = &audio_streams;
1259 } else if (st == VideoStream) {
1260 si = &vstream;
1261 streams = &video_streams;
1262 } else if (st == SubtitleStream) {
1263 si = &sstream;
1264 streams = &subtitle_streams;
1265 }
1266 if (!si /*|| si->wanted_stream == streamValue*/) { //init -2
1267 qWarning("stream type %d not found", st);
1268 return false;
1269 }
1270 //if (!streams->contains(si->stream)) {
1271 // qWarning("%d is not a valid stream for stream type %d", si->stream, st);
1272 //return false;
1273 //}
1274 bool index_valid = si->wanted_index >= 0 && si->wanted_index < streams->size();
1275 int s = AVERROR_STREAM_NOT_FOUND;
1276 if (streamValue >= 0 || !index_valid) {
1277 // or simply set s to streamValue if value is contained in streams?
1278 s = av_find_best_stream(format_ctx
1279 , st == AudioStream ? AVMEDIA_TYPE_AUDIO
1280 : st == VideoStream ? AVMEDIA_TYPE_VIDEO
1281 : st == SubtitleStream ? AVMEDIA_TYPE_SUBTITLE
1282 : AVMEDIA_TYPE_UNKNOWN
1283 , streamValue, -1, NULL, 0); // streamValue -1 is ok
1284 } else { //index_valid
1285 s = streams->at(si->wanted_index);
1286 }
1287 if (s == AVERROR_STREAM_NOT_FOUND)
1288 return false;
1289 // don't touch wanted index
1290 si->stream = s;
1291 si->wanted_stream = streamValue;
1292 si->avctx = format_ctx->streams[s]->codec;
1293 has_attached_pic = !!(format_ctx->streams[s]->disposition & AV_DISPOSITION_ATTACHED_PIC);
1294 return true;
1295 }
1296
1297 bool AVDemuxer::Private::prepareStreams()
1298 {
1299 has_attached_pic = false;
1300 resetStreams();
1301 if (!format_ctx)
1302 return false;
1303 AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
1304 for (unsigned int i = 0; i < format_ctx->nb_streams; ++i) {
1305 type = format_ctx->streams[i]->codec->codec_type;
1306 if (type == AVMEDIA_TYPE_VIDEO) {
1307 video_streams.push_back(i);
1308 } else if (type == AVMEDIA_TYPE_AUDIO) {
1309 audio_streams.push_back(i);
1310 } else if (type == AVMEDIA_TYPE_SUBTITLE) {
1311 subtitle_streams.push_back(i);
1312 }
1313 }
1314 if (audio_streams.isEmpty() && video_streams.isEmpty() && subtitle_streams.isEmpty())
1315 return false;
1316 setStream(AVDemuxer::AudioStream, -1);
1317 setStream(AVDemuxer::VideoStream, -1);
1318 setStream(AVDemuxer::SubtitleStream, -1);
1319 return true;
1320 }
1321 } //namespace QtAV
1322