QTfrontend/util/libav_iteraction.cpp
author Stepan777 <stepik-777@mail.ru>
Mon, 11 Jun 2012 18:15:30 +0400
changeset 7235 baa69bd025d9
child 7280 fd707afbc3a2
permissions -rw-r--r--
1. Implement new page in frontend with options for video recording. 2. Store temoprary files in different directory

/*
 * Hedgewars, a free turn based strategy game
 * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
 *
 * 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 <QVector>
#include <QList>
#include <QMessageBox>

#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<Codec*> codecs;
};

QList<Codec> codecs;
QMap<QString,Format> 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<Codec>::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);
}