first commit
This commit is contained in:
287
project/fm_viewer/fav/AVDecoder.cpp
Normal file
287
project/fm_viewer/fav/AVDecoder.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
/******************************************************************************
|
||||
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 "AVDecoder.h"
|
||||
#include "AVDecoder_p.h"
|
||||
#include "version.h"
|
||||
#include "internal.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace FAV {
|
||||
|
||||
static AVCodec* get_codec(const QString &name, const QString& hwa, AVCodecID cid)
|
||||
{
|
||||
QString fullname(name);
|
||||
if (name.isEmpty()) {
|
||||
if (hwa.isEmpty())
|
||||
return avcodec_find_decoder(cid);
|
||||
fullname = QString("%1_%2").arg(avcodec_get_name(cid)).arg(hwa);
|
||||
}
|
||||
AVCodec *codec = avcodec_find_decoder_by_name(fullname.toUtf8().constData());
|
||||
if (codec)
|
||||
return codec;
|
||||
const AVCodecDescriptor* cd = avcodec_descriptor_get_by_name(fullname.toUtf8().constData());
|
||||
if (cd)
|
||||
return avcodec_find_decoder(cd->id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AVDecoder::AVDecoder(AVDecoderPrivate &d)
|
||||
:DPTR_INIT(&d)
|
||||
{
|
||||
avcodec_register_all(); // avcodec_find_decoder will always be used
|
||||
}
|
||||
|
||||
AVDecoder::~AVDecoder()
|
||||
{
|
||||
setCodecContext(0); // FIXME: will call virtual
|
||||
}
|
||||
|
||||
QString AVDecoder::name() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString AVDecoder::description() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool AVDecoder::open()
|
||||
{
|
||||
DPTR_D(AVDecoder);
|
||||
// codec_ctx can't be null for none-ffmpeg based decoders because we may use it's properties in those decoders
|
||||
if (!d.codec_ctx) {
|
||||
qWarning("FFmpeg codec context not ready");
|
||||
return false;
|
||||
}
|
||||
const QString hwa = property("hwaccel").toString();
|
||||
AVCodec* codec = get_codec(codecName(), hwa, d.codec_ctx->codec_id);
|
||||
if (!codec) { // TODO: can be null for none-ffmpeg based decoders
|
||||
QString es("No codec could be found for '%1'");
|
||||
if (d.codec_name.isEmpty()) {
|
||||
es = es.arg(QLatin1String(avcodec_get_name(d.codec_ctx->codec_id)));
|
||||
if (!hwa.isEmpty())
|
||||
es.append('_').append(hwa);
|
||||
} else {
|
||||
es = es.arg(d.codec_name);
|
||||
}
|
||||
qWarning() << es;
|
||||
AVError::ErrorCode ec(AVError::CodecError);
|
||||
switch (d.codec_ctx->codec_type) {
|
||||
case AVMEDIA_TYPE_VIDEO:
|
||||
ec = AVError::VideoCodecNotFound;
|
||||
break;
|
||||
case AVMEDIA_TYPE_AUDIO:
|
||||
ec = AVError::AudioCodecNotFound;
|
||||
break;
|
||||
case AVMEDIA_TYPE_SUBTITLE:
|
||||
ec = AVError::SubtitleCodecNotFound;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Q_EMIT error(AVError(ec, es));
|
||||
return false;
|
||||
}
|
||||
// hwa extra init can be here
|
||||
if (!d.open()) {
|
||||
d.close();
|
||||
return false;
|
||||
}
|
||||
// CODEC_FLAG_OUTPUT_CORRUPT, CODEC_FLAG2_SHOW_ALL?
|
||||
// TODO: skip for none-ffmpeg based decoders
|
||||
d.applyOptionsForDict();
|
||||
|
||||
av_opt_set_int(d.codec_ctx, "refcounted_frames", d.enableFrameRef(), 0); // why dict may have no effect?
|
||||
|
||||
d.codec_ctx->thread_count = 1;
|
||||
d.codec_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
|
||||
d.codec_ctx->flags2 |= AV_CODEC_FLAG2_CHUNKS;
|
||||
|
||||
// TODO: only open for ff decoders
|
||||
//av_dict_set(&d.dict, "lowres", "1", 0);
|
||||
// dict is used for a specified AVCodec options (priv_class), av_opt_set_xxx(avctx) is only for avctx
|
||||
AV_ENSURE_OK(avcodec_open2(d.codec_ctx, codec, d.options.isEmpty() ? NULL : &d.dict), false);
|
||||
d.is_open = true;
|
||||
static const char* thread_name[] = { "Single", "Frame", "Slice"};
|
||||
qDebug("%s thread type: %s, count: %d", metaObject()->className(), thread_name[d.codec_ctx->active_thread_type], d.codec_ctx->thread_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AVDecoder::close()
|
||||
{
|
||||
if (!isOpen()) {
|
||||
return true;
|
||||
}
|
||||
DPTR_D(AVDecoder);
|
||||
d.is_open = false;
|
||||
// hwa extra finalize can be here
|
||||
flush();
|
||||
d.close();
|
||||
// TODO: reset config?
|
||||
if (d.codec_ctx) {
|
||||
AV_ENSURE_OK(avcodec_close(d.codec_ctx), false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AVDecoder::isOpen() const
|
||||
{
|
||||
return d_func().is_open;
|
||||
}
|
||||
|
||||
void AVDecoder::flush()
|
||||
{
|
||||
if (!isAvailable())
|
||||
return;
|
||||
if (!isOpen())
|
||||
return;
|
||||
avcodec_flush_buffers(d_func().codec_ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* do nothing if equal
|
||||
* close the old one. the codec context can not be shared in more than 1 decoder.
|
||||
*/
|
||||
void AVDecoder::setCodecContext(void *codecCtx)
|
||||
{
|
||||
DPTR_D(AVDecoder);
|
||||
AVCodecContext *ctx = (AVCodecContext*)codecCtx;
|
||||
if (ctx == NULL || d.codec_ctx == ctx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (isOpen())
|
||||
{
|
||||
#if !(OFF_OTHER_DEBUG)
|
||||
qWarning("Can not copy codec properties when it's open");
|
||||
#endif
|
||||
close(); //
|
||||
}
|
||||
d.is_open = false;
|
||||
if (!ctx) {
|
||||
avcodec_free_context(&d.codec_ctx);
|
||||
d.codec_ctx = 0;
|
||||
return;
|
||||
}
|
||||
if (!d.codec_ctx)
|
||||
d.codec_ctx = avcodec_alloc_context3(NULL);
|
||||
// avcodec_alloc_context3(codec) equals to avcodec_alloc_context3(NULL) + avcodec_get_context_defaults3(codec), codec specified private data is initialized
|
||||
if (!d.codec_ctx) {
|
||||
qWarning("avcodec_alloc_context3 failed");
|
||||
return;
|
||||
}
|
||||
AV_ENSURE_OK(avcodec_copy_context(d.codec_ctx, ctx));
|
||||
}
|
||||
|
||||
//TODO: reset other parameters?
|
||||
void* AVDecoder::codecContext() const
|
||||
{
|
||||
return d_func().codec_ctx;
|
||||
}
|
||||
|
||||
void AVDecoder::setCodecName(const QString &name)
|
||||
{
|
||||
DPTR_D(AVDecoder);
|
||||
if (d.codec_name == name)
|
||||
return;
|
||||
d.codec_name = name;
|
||||
Q_EMIT codecNameChanged();
|
||||
}
|
||||
|
||||
QString AVDecoder::codecName() const
|
||||
{
|
||||
DPTR_D(const AVDecoder);
|
||||
return d.codec_name;
|
||||
}
|
||||
|
||||
bool AVDecoder::isAvailable() const
|
||||
{
|
||||
return d_func().codec_ctx != 0;
|
||||
}
|
||||
|
||||
int AVDecoder::undecodedSize() const
|
||||
{
|
||||
return d_func().undecoded_size;
|
||||
}
|
||||
|
||||
void AVDecoder::setOptions(const QVariantHash &dict)
|
||||
{
|
||||
DPTR_D(AVDecoder);
|
||||
d.options = dict;
|
||||
// if dict is empty, can not return here, default options will be set for AVCodecContext
|
||||
// apply to AVCodecContext
|
||||
d.applyOptionsForContext();
|
||||
/* set AVDecoder meta properties.
|
||||
* we do not check whether the property exists thus we can set dynamic properties.
|
||||
*/
|
||||
if (dict.isEmpty())
|
||||
return;
|
||||
if (name() == QLatin1String("avcodec"))
|
||||
return;
|
||||
QVariant opt(dict);
|
||||
if (dict.contains(name()))
|
||||
opt = dict.value(name());
|
||||
else if (dict.contains(name().toLower()))
|
||||
opt = dict.value(name().toLower());
|
||||
Internal::setOptionsForQObject(opt, this);
|
||||
}
|
||||
|
||||
QVariantHash AVDecoder::options() const
|
||||
{
|
||||
return d_func().options;
|
||||
}
|
||||
|
||||
void AVDecoderPrivate::applyOptionsForDict()
|
||||
{
|
||||
if (dict) {
|
||||
av_dict_free(&dict);
|
||||
dict = 0; //aready 0 in av_free
|
||||
}
|
||||
// enable ref if possible
|
||||
av_dict_set(&dict, "refcounted_frames", enableFrameRef() ? "1" : "0", 0);
|
||||
if (options.isEmpty())
|
||||
return;
|
||||
// TODO: use QVariantMap only
|
||||
if (!options.contains(QStringLiteral("avcodec")))
|
||||
return;
|
||||
qDebug("set AVCodecContext dict:");
|
||||
// workaround for VideoDecoderFFmpeg. now it does not call av_opt_set_xxx, so set here in dict
|
||||
// TODO: wrong if opt is empty
|
||||
Internal::setOptionsToDict(options.value(QStringLiteral("avcodec")), &dict);
|
||||
}
|
||||
|
||||
void AVDecoderPrivate::applyOptionsForContext()
|
||||
{
|
||||
if (!codec_ctx)
|
||||
return;
|
||||
if (options.isEmpty()) {
|
||||
// av_opt_set_defaults(codec_ctx); //can't set default values! result maybe unexpected
|
||||
return;
|
||||
}
|
||||
|
||||
if (!options.contains(QStringLiteral("avcodec")))
|
||||
return;
|
||||
// workaround for VideoDecoderFFmpeg. now it does not call av_opt_set_xxx, so set here in dict
|
||||
// TODO: wrong if opt is empty
|
||||
Internal::setOptionsToFFmpegObj(options.value(QStringLiteral("avcodec")), codec_ctx);
|
||||
}
|
||||
} //namespace FAV
|
||||
Reference in New Issue
Block a user