diff -r f484455dd055 -r baa69bd025d9 QTfrontend/util/libav_iteraction.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QTfrontend/util/libav_iteraction.cpp Mon Jun 11 18:15:30 2012 +0400 @@ -0,0 +1,269 @@ +/* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2012 Andrey Korotaev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#define __STDC_CONSTANT_MACROS +extern "C" +{ +#include "libavformat/avformat.h" +} +#include +#include +#include + +#include "libav_iteraction.h" +#include "HWApplication.h" + +struct Codec +{ + CodecID id; + bool isAudio; + QString shortName; // used for identification + QString longName; // used for displaying to user + bool isRecomended; +}; + +struct Format +{ + QString shortName; + QString longName; + bool isRecomended; + // QString extension; + QVector codecs; +}; + +QList codecs; +QMap formats; + +LibavIteraction & LibavIteraction::instance() +{ + static LibavIteraction instance; + return instance; +} + +bool FormatQueryCodec(AVOutputFormat *ofmt, enum CodecID codec_id) +{ +#if LIBAVFORMAT_VERSION_MAJOR >= 54 + return avformat_query_codec(ofmt, codec_id, FF_COMPLIANCE_NORMAL) == 1; +#else + if (ofmt->codec_tag) + return !!av_codec_get_tag(ofmt->codec_tag, codec_id); + return codec_id == ofmt->video_codec || codec_id == ofmt->audio_codec; +#endif +} + +LibavIteraction::LibavIteraction() +{ + // initialize libav and register all codecs and formats + av_register_all(); + + // get list of all codecs + AVCodec* pCodec = NULL; + while (pCodec = av_codec_next(pCodec)) + { +#if LIBAVCODEC_VERSION_MAJOR >= 54 + if (!av_codec_is_encoder(pCodec)) +#else + if (!pCodec->encode) +#endif + continue; + + if (pCodec->capabilities & CODEC_CAP_EXPERIMENTAL) + continue; + + if (pCodec->type != AVMEDIA_TYPE_VIDEO && pCodec->type != AVMEDIA_TYPE_AUDIO) + continue; + + // this encoders seems to be buggy + if (strcmp(pCodec->name, "rv10") == 0 || + strcmp(pCodec->name, "rv20") == 0) + continue; + + // doesn't support stereo sound + if (strcmp(pCodec->name, "real_144") == 0) + continue; +#if LIBAVCODEC_VERSION_MAJOR < 54 + // FIXME: theese doesn't work for some reason + if (strcmp(pCodec->name, "libx264") == 0) + continue; + if (strcmp(pCodec->name, "libxvid") == 0) + continue; +#endif + + if (pCodec->type == AVMEDIA_TYPE_VIDEO) + { + if (pCodec->supported_framerates != NULL) + continue; + + // check if codec supports yuv 4:2:0 format + if (!pCodec->pix_fmts) + continue; + bool yuv420Supported = false; + for (const PixelFormat* pfmt = pCodec->pix_fmts; *pfmt != -1; pfmt++) + if (*pfmt == PIX_FMT_YUV420P) + { + yuv420Supported = true; + break; + } + if (!yuv420Supported) + continue; + } + if (pCodec->type == AVMEDIA_TYPE_AUDIO) + { + // check if codec supports signed 16-bit format + if (!pCodec->sample_fmts) + continue; + bool s16Supported = false; + for (const AVSampleFormat* pfmt = pCodec->sample_fmts; *pfmt != -1; pfmt++) + if (*pfmt == AV_SAMPLE_FMT_S16/* || *pfmt == AV_SAMPLE_FMT_FLT*/) + { + s16Supported = true; + break; + } + if (!s16Supported) + continue; + } + // add codec to list of codecs + codecs.push_back(Codec()); + Codec & codec = codecs.back(); + codec.id = pCodec->id; + codec.isAudio = pCodec->type == AVMEDIA_TYPE_AUDIO; + codec.shortName = pCodec->name; + codec.longName = pCodec->long_name; + + codec.isRecomended = false; + if (strcmp(pCodec->name, "libx264") == 0) + { + codec.longName = "H.264/MPEG-4 Part 10 AVC (x264)"; + codec.isRecomended = true; + } + else if (strcmp(pCodec->name, "libxvid") == 0) + { + codec.longName = "MPEG-4 Part 2 (Xvid)"; + codec.isRecomended = true; + } + else if (strcmp(pCodec->name, "libmp3lame") == 0) + { + codec.longName = "MP3 (MPEG audio layer 3) (LAME)"; + codec.isRecomended = true; + } + else + codec.longName = pCodec->long_name; + + if (strcmp(pCodec->name, "mpeg4") == 0 || strcmp(pCodec->name, "ac3_fixed") == 0) + codec.isRecomended = true; + + // FIXME: remove next line + codec.longName += QString(" (%1)").arg(codec.shortName); + } + + // get list of all formats + AVOutputFormat* pFormat = NULL; + while (pFormat = av_oformat_next(pFormat)) + { + if (!pFormat->extensions) + continue; + + // skip some strange formats to not confuse users + if (strstr(pFormat->long_name, "raw")) + continue; + + Format format; + bool hasVideoCodec = false; + for (QList::iterator codec = codecs.begin(); codec != codecs.end(); ++codec) + { + if (!FormatQueryCodec(pFormat, codec->id)) + continue; + format.codecs.push_back(&*codec); + if (!codec->isAudio) + hasVideoCodec = true; + } + if (!hasVideoCodec) + continue; + format.shortName = pFormat->name; + + QString ext(pFormat->extensions); + ext.truncate(strcspn(pFormat->extensions, ",")); + format.longName = QString("%1 (*.%2)").arg(pFormat->long_name).arg(ext); + + // FIXME: remove next line + format.longName += QString(" (%1)").arg(format.shortName); + + format.isRecomended = strcmp(pFormat->name, "mp4") == 0 || strcmp(pFormat->name, "avi") == 0; + + formats[pFormat->name] = format; + } +} + +void LibavIteraction::FillFormats(QComboBox * pFormats) +{ + // first insert recomended formats + foreach(const Format & format, formats) + if (format.isRecomended) + pFormats->addItem(format.longName, format.shortName); + + // remember where to place separator between recomended and other formats + int sep = pFormats->count(); + + // insert remaining formats + foreach(const Format & format, formats) + if (!format.isRecomended) + pFormats->addItem(format.longName, format.shortName); + + // insert separator if necessary + if (sep != 0 && sep != pFormats->count()) + pFormats->insertSeparator(sep); +} + +void LibavIteraction::FillCodecs(const QVariant & fmt, QComboBox * pVCodecs, QComboBox * pACodecs) +{ + Format & format = formats[fmt.toString()]; + + // first insert recomended codecs + foreach(Codec * codec, format.codecs) + { + if (codec->isRecomended) + { + if (codec->isAudio) + pACodecs->addItem(codec->longName, codec->shortName); + else + pVCodecs->addItem(codec->longName, codec->shortName); + } + } + + // remember where to place separators between recomended and other codecs + int vsep = pVCodecs->count(); + int asep = pACodecs->count(); + + // insert remaining codecs + foreach(Codec * codec, format.codecs) + { + if (!codec->isRecomended) + { + if (codec->isAudio) + pACodecs->addItem(codec->longName, codec->shortName); + else + pVCodecs->addItem(codec->longName, codec->shortName); + } + } + + // insert separators if necessary + if (vsep != 0 && vsep != pVCodecs->count()) + pVCodecs->insertSeparator(vsep); + if (asep != 0 && asep != pACodecs->count()) + pACodecs->insertSeparator(asep); +}