diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..a9bb44a35 --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +CVS +.#* + +.hg +.hgignore + +bin +obj +TestResults +*.pbxuser +*.perspectivev3 +.DS_Store + +*.old +*.log +*.out +*.cache + +*.deb + +*.git.old* +Makefile* +*.*pro*.user* +*.[oa] +*.so* +*.dll +*.lib +*.exp +*.exe +*.out + +#vc files +*.def +*.dep +*.idb +*.layout +*.manifest +*.ncb +*.obj +*.pdb +*.suo +*.user +*.tlh +*.tli + + +#intel compiler +*.ilk + +#dirs +.moc +.rcc +.obj +/bin* +/lib* +Debug +Release +*.Debug +*.Release + +*.fuse* + +#qt +*.prl +*moc_* +*.moc +*qrc_res.cpp + diff --git a/QtAV.pro b/QtAV.pro new file mode 100644 index 000000000..dbd21d672 --- /dev/null +++ b/QtAV.pro @@ -0,0 +1,11 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = libqtav test + +libqtav.file = src/libQtAV.pro +test.file = test/tst_QtAV.pro +test.depends += libqtav + +OTHER_FILES += README + + diff --git a/README.md b/README.md deleted file mode 100644 index 4ef27c0f7..000000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -QtAV -==== - -A media library based on Qt and FFmpeg \ No newline at end of file diff --git a/common.pri b/common.pri new file mode 100644 index 000000000..c557fb58b --- /dev/null +++ b/common.pri @@ -0,0 +1,164 @@ +# qmake common template pri file +# Copyright (C) 2011 Wang Bin +# Shanghai, China. +# +# 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; either version 2 of the License, or +# (at your option) any later version. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +isEmpty(COMMON_PRI_INCLUDED): { #begin COMMON_PRI_INCLUDED + +CONFIG += profile +#profiling, -pg is not supported for msvc +debug:!*msvc*:profile { + QMAKE_CXXFLAGS_DEBUG += -pg + QMAKE_LFLAGS_DEBUG += -pg + QMAKE_CXXFLAGS_DEBUG = $$unique(QMAKE_CXXFLAGS_DEBUG) + QMAKE_LFLAGS_DEBUG = $$unique(QMAKE_LFLAGS_DEBUG) +} + +#$$[TARGET_PLATFORM] +#$$[QT_ARCH] #windows symbian windowsce arm +_OS = +_ARCH = +_EXTRA = + +unix { + _OS = _unix + *linux*: _OS = _linux + *maemo* { + _OS = _maemo + *maemo5*:_OS = _maemo5 + *maemo6*:_OS = _maemo6 + } + *meego*: _OS = _meego + !isEmpty(MEEGO_EDITION): _OS = _$$MEEGO_EDITION +} else:wince* { + _OS = _wince +} else:win32 { #true for wince + _OS = _win32 +} else:macx { + _OS = _macx +} + +#*arm*: _ARCH = $${_ARCH}_arm +contains(QT_ARCH, arm.*) { + _ARCH = $${_ARCH}_$${QT_ARCH} +} +*64: _ARCH = $${_ARCH}_x64 +*llvm*: _EXTRA = _llvm +#*msvc*: + +win32-msvc* { + #Don't warn about sprintf, fopen etc being 'unsafe' + DEFINES += _CRT_SECURE_NO_WARNINGS +} + +#################################functions######################################### +defineReplace(cleanPath) { + win32:1 ~= s|\\\\|/|g + contains(1, ^/.*):pfx = / + else:pfx = + segs = $$split(1, /) + out = + for(seg, segs) { + equals(seg, ..):out = $$member(out, 0, -2) + else:!equals(seg, .):out += $$seg + } + return($$join(out, /, $$pfx)) +} + +#Acts like qtLibraryTarget. From qtcreator.pri +defineReplace(qtLibName) { + #TEMPLATE += fakelib + #LIB_FULLNAME = $$qtLibraryTarget($$1) + #TEMPLATE -= fakelib + unset(LIBRARY_NAME) + LIBRARY_NAME = $$1 + CONFIG(debug, debug|release) { + !debug_and_release|build_pass { + mac:RET = $$member(LIBRARY_NAME, 0)_debug + else:win32:RET = $$member(LIBRARY_NAME, 0)d + } + } + isEmpty(RET):RET = $$LIBRARY_NAME + !win32: return($$RET) + + isEmpty(2): VERSION_EXT = $$VERSION + else: VERSION_EXT = $$2 + !isEmpty(VERSION_EXT) { + VERSION_EXT = $$section(VERSION_EXT, ., 0, 0) + isEqual(VERSION_EXT, 0):unset(VERSION_EXT) + } + RET = $${RET}$${VERSION_EXT} + unset(VERSION_EXT) + return($$RET) +} + + +#fakelib +defineReplace(qtStaticLib) { + unset(LIB_FULLNAME) + LIB_FULLNAME = $$qtLibName($$1, $$2) + *msvc*: LIB_FULLNAME = $$member(LIB_FULLNAME, 0).lib + else: LIB_FULLNAME = lib$$member(LIB_FULLNAME, 0).a + return($$LIB_FULLNAME) +} + +defineReplace(qtSharedLib) { + unset(LIB_FULLNAME) + LIB_FULLNAME = $$qtLibName($$1, $$2) + win32: LIB_FULLNAME = $$member(LIB_FULLNAME, 0).dll + else { + macx|ios: LIB_FULLNAME = lib$$member(LIB_FULLNAME, 0).$${QMAKE_EXTENSION_SHLIB} #default_post.prf + else: LIB_FULLNAME = lib$$member(LIB_FULLNAME, 0).so + } + return($$LIB_FULLNAME) +} + +defineReplace(qtLongName) { + unset(LONG_NAME) + LONG_NAME = $$1$${_OS}$${_ARCH}$${_EXTRA} + return($$LONG_NAME) +} + +##############################paths#################################### +#TRANSLATIONS += i18n/$${TARGET}_zh-cn.ts i18n/$${TARGET}_zh_CN.ts + +#for Qt2, Qt3 which does not have QT_VERSION. Qt4: $$[QT_VERSION] +MOC_DIR = $$BUILD_DIR/.moc/$${QT_VERSION} +RCC_DIR = $$BUILD_DIR/.rcc/$${QT_VERSION} +UI_DIR = $$BUILD_DIR/.ui/$${QT_VERSION} +#obj is platform dependent +OBJECTS_DIR = $$qtLongName($$BUILD_DIR/.obj/$$TARGET) + +isEqual(TEMPLATE, app) { + DESTDIR = $$BUILD_DIR/bin + TARGET = $$qtLongName($$TARGET) + EXE_EXT = + win32: EXE_EXT = .exe + CONFIG(release, debug|release): !isEmpty(QMAKE_STRIP): QMAKE_POST_LINK = -$$QMAKE_STRIP $$DESTDIR/$${TARGET}$${EXE_EXT} #.exe in win +} +else: DESTDIR = $$qtLongName($$BUILD_DIR/lib) + +!build_pass:message(target: $$DESTDIR/$$TARGET) + +COMMON_PRI_INCLUDED = 1 + +} #end COMMON_PRI_INCLUDED + #before target name changed +#TRANSLATIONS += i18n/$${TARGET}_zh-cn.ts #i18n/$${TARGET}_zh_CN.ts + + + diff --git a/src/AVDecoder.cpp b/src/AVDecoder.cpp new file mode 100644 index 000000000..8299551cb --- /dev/null +++ b/src/AVDecoder.cpp @@ -0,0 +1,44 @@ +#include + +namespace QtAV { +AVDecoder::AVDecoder() + :codec_ctx(0),frame_(0) +{ +} + +AVDecoder::~AVDecoder() +{ +} + +void AVDecoder::setCodecContext(AVCodecContext *codecCtx) +{ + codec_ctx = codecCtx; +} + +AVCodecContext* AVDecoder::codecContext() const +{ + return codec_ctx; +} + +void AVDecoder::setFrame(AVFrame *frame) +{ + frame_ = frame; +} + +AVFrame* AVDecoder::frame() const +{ + return frame_; +} + +bool AVDecoder::decode(const QByteArray &encoded) +{ + decoded = encoded; + return true; +} + +QByteArray AVDecoder::data() const +{ + return decoded; +} + +} diff --git a/src/AVDemuxThread.cpp b/src/AVDemuxThread.cpp new file mode 100644 index 000000000..7f7be06e4 --- /dev/null +++ b/src/AVDemuxThread.cpp @@ -0,0 +1,15 @@ +#include + +namespace QtAV { +AVDemuxThread::AVDemuxThread(QObject *parent) : + QThread(parent) +{ +} + + +void AVDemuxThread::run() +{ + //demuxer->read() + //enqueue() +} +} diff --git a/src/AVDemuxer.cpp b/src/AVDemuxer.cpp new file mode 100644 index 000000000..42d357fb1 --- /dev/null +++ b/src/AVDemuxer.cpp @@ -0,0 +1,62 @@ +#include +#ifdef __cplusplus +extern "C" { +#include +#include +} +#endif //__cplusplus + +#include + +namespace QtAV { +AVDemuxer::AVDemuxer() + :formatCtx(0) +{ +} + + +bool AVDemuxer::readPacket(QAVPacket *packet, int stream) +{ + AVPacket pkt; + int ret = av_read_frame(formatCtx, &pkt); + + if (ret == AVERROR(EAGAIN)) + return true; + else if (ret) + return false; + /* idx = pkt.stream_index; + if (idx >= _streams.count()) + { + loadStreams(); + if ( idx >= _streams.count() ) + return false; + } +*/ + packet->data = QByteArray((const char *)pkt.data, pkt.size); +#if 0 + AVStream *&stream = ( AVStream *& )_streams[ idx ]; + +//TODO: seek by byte? + if (pkt.dts == AV_NOPTS_VALUE && pkt.pts != AV_NOPTS_VALUE) + packet->pts = pkt.pts; + else if (pkt.dts != AV_NOPTS_VALUE) + packet->pts = pkt.dts; + if (packet->pts < 0.) + packet->pts = 0.; + packet->pts *= av_q2d(stream->time_base); + if (packet->pts && packet->pts - start_time >= 0.0 ) + packet->pts -= start_time; + + if (stream->codec->codec_type == AVMEDIA_TYPE_SUBTITLE + && ( pkt.flags & AV_PKT_FLAG_KEY ) + && pkt.convergence_duration != AV_NOPTS_VALUE) + packet->duration = pkt.convergence_duration * av_q2d(stream->time_base); + else if (pkt.duration > 0) + packet->duration = pkt.duration * av_q2d(stream->time_base); + else + packet->duration = 0.; +#endif + return true; +} + +} diff --git a/src/AVInfo.cpp b/src/AVInfo.cpp new file mode 100644 index 000000000..b57401728 --- /dev/null +++ b/src/AVInfo.cpp @@ -0,0 +1,349 @@ +/****************************************************************************** + qavinfo.cpp: description + Copyright (C) 2012 Wang Bin + + 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; either version 2 of the License, or + (at your option) any later version. + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +******************************************************************************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus +#include +#ifdef __cplusplus +} +#endif //__cplusplus +#include + +namespace QtAV { +AVInfo::AVInfo(const QString& fileName) + :_is_input(true),_format_context(0),a_codec_context(0),v_codec_context(0) + ,_file_name(fileName) +{ + av_register_all(); + if (!_file_name.isEmpty()) + loadFile(_file_name); +} + +AVInfo::~AVInfo() +{ + avcodec_close(a_codec_context); + avcodec_close(v_codec_context); + //av_close_input_file(_format_context); //deprecated + avformat_close_input(&_format_context); //libavf > 53.10.0 +} + +bool AVInfo::loadFile(const QString &fileName) +{ + _file_name = fileName; + //deprecated + // Open an input stream and read the header. The codecs are not opened. + //if(av_open_input_file(&_format_context, _file_name.toLocal8Bit().constData(), NULL, 0, NULL)) { + if (avformat_open_input(&_format_context, qPrintable(_file_name), NULL, NULL)) { + //if (avformat_open_input(&formatCtx, qPrintable(filename), NULL, NULL)) { + qDebug("Can't open video"); + return false; + } + _format_context->flags |= AVFMT_FLAG_GENPTS; + //deprecated + //if(av_find_stream_info(_format_context)<0) { + if (avformat_find_stream_info(_format_context, NULL)<0) { + qDebug("Can't find stream info"); + return false; + } + + //a_codec_context = _format_context->streams[audioStream()]->codec; + //v_codec_context = _format_context->streams[videoStream()]->codec; + findAVCodec(); + + AVCodec *aCodec = avcodec_find_decoder(a_codec_context->codec_id); + if(aCodec) { + if(avcodec_open2(a_codec_context, aCodec, NULL)<0) { + qDebug("open audio codec failed"); + } + } else { + qDebug("Unsupported audio codec. id=%d.", v_codec_context->codec_id); + } + AVCodec *vCodec = avcodec_find_decoder(v_codec_context->codec_id); + if(!vCodec) { + qDebug("Unsupported video codec. id=%d.", v_codec_context->codec_id); + return false; + } + ////v_codec_context->time_base = (AVRational){1,30}; + //avcodec_open(v_codec_context, vCodec) //deprecated + if(avcodec_open2(v_codec_context, vCodec, NULL)<0) { + qDebug("open video codec failed"); + return false; + } + return true; +} + +AVFormatContext* AVInfo::formatContext() +{ + return _format_context; +} + + +/* +static void dump_stream_format(AVFormatContext *ic, int i, int index, int is_output) +{ + char buf[256]; + int flags = (is_output ? ic->oformat->flags : ic->iformat->flags); + AVStream *st = ic->streams[i]; + int g = av_gcd(st->time_base.num, st->time_base.den); + AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0); + avcodec_string(buf, sizeof(buf), st->codec, is_output); + av_log(NULL, AV_LOG_INFO, " Stream #%d.%d", index, i); + // the pid is an important information, so we display it + // XXX: add a generic system + if (flags & AVFMT_SHOW_IDS) + av_log(NULL, AV_LOG_INFO, "[0x%x]", st->id); + if (lang) + av_log(NULL, AV_LOG_INFO, "(%s)", lang->value); + av_log(NULL, AV_LOG_DEBUG, ", %d, %d/%d", st->codec_info_nb_frames, st->time_base.num/g, st->time_base.den/g); + av_log(NULL, AV_LOG_INFO, ": %s", buf); + if (st->sample_aspect_ratio.num && // default + av_cmp_q(st->sample_aspect_ratio, st->codec->sample_aspect_ratio)) { + AVRational display_aspect_ratio; + av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, + st->codec->width*st->sample_aspect_ratio.num, + st->codec->height*st->sample_aspect_ratio.den, + 1024*1024); + av_log(NULL, AV_LOG_INFO, ", PAR %d:%d DAR %d:%d", + st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, + display_aspect_ratio.num, display_aspect_ratio.den); + } + if(st->codec->codec_type == AVMEDIA_TYPE_VIDEO){ + if(st->avg_frame_rate.den && st->avg_frame_rate.num) + print_fps(av_q2d(st->avg_frame_rate), "fps"); + if(st->r_frame_rate.den && st->r_frame_rate.num) + print_fps(av_q2d(st->r_frame_rate), "tbr"); + if(st->time_base.den && st->time_base.num) + print_fps(1/av_q2d(st->time_base), "tbn"); + if(st->codec->time_base.den && st->codec->time_base.num) + print_fps(1/av_q2d(st->codec->time_base), "tbc"); + } + if (st->disposition & AV_DISPOSITION_DEFAULT) + av_log(NULL, AV_LOG_INFO, " (default)"); + if (st->disposition & AV_DISPOSITION_DUB) + av_log(NULL, AV_LOG_INFO, " (dub)"); + if (st->disposition & AV_DISPOSITION_ORIGINAL) + av_log(NULL, AV_LOG_INFO, " (original)"); + if (st->disposition & AV_DISPOSITION_COMMENT) + av_log(NULL, AV_LOG_INFO, " (comment)"); + if (st->disposition & AV_DISPOSITION_LYRICS) + av_log(NULL, AV_LOG_INFO, " (lyrics)"); + if (st->disposition & AV_DISPOSITION_KARAOKE) + av_log(NULL, AV_LOG_INFO, " (karaoke)"); + if (st->disposition & AV_DISPOSITION_FORCED) + av_log(NULL, AV_LOG_INFO, " (forced)"); + if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED) + av_log(NULL, AV_LOG_INFO, " (hearing impaired)"); + if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED) + av_log(NULL, AV_LOG_INFO, " (visual impaired)"); + if (st->disposition & AV_DISPOSITION_CLEAN_EFFECTS) + av_log(NULL, AV_LOG_INFO, " (clean effects)"); + av_log(NULL, AV_LOG_INFO, "\n"); + dump_metadata(NULL, st->metadata, " "); +}*/ +void AVInfo::dump() +{ + qDebug("Version\n" + "libavcodec: %#x\n" + "libavformat: %#x\n" +// "libavdevice: %#x\n" + "libavutil: %#x" + ,avcodec_version() + ,avformat_version() +// ,avdevice_version() + ,avutil_version()); + av_dump_format(_format_context, 0, qPrintable(_file_name), false); + qDebug("video format: %s [%s]", qPrintable(videoFormatName()), qPrintable(videoFormatLongName())); + qDebug("Audio: %s [%s]", qPrintable(audioCodecName()), qPrintable(audioCodecLongName())); + qDebug("sample rate: %d, channels: %d", a_codec_context->sample_rate, a_codec_context->channels); +} + +QString AVInfo::fileName() const +{ + return _format_context->filename; +} + +QString AVInfo::videoFormatName() const +{ + return formatName(_format_context, false); +} + +QString AVInfo::videoFormatLongName() const +{ + return formatName(_format_context, true); +} + +qint64 AVInfo::startTime() const +{ + return _format_context->start_time; +} + +qint64 AVInfo::duration() const +{ + return _format_context->duration; +} + +int AVInfo::bitRate() const +{ + return _format_context->bit_rate; +} + +qreal AVInfo::frameRate() const +{ + AVStream *stream = _format_context->streams[videoStream()]; + return (qreal)stream->r_frame_rate.num / (qreal)stream->r_frame_rate.den; + //codecCtx->time_base.den / codecCtx->time_base.num +} + +qint64 AVInfo::frames() const +{ + return _format_context->streams[videoStream()]->nb_frames; //0? +} + +bool AVInfo::isInput() const +{ + return _is_input; +} + +int AVInfo::audioStream() const +{ + static int audio_stream = -2; //-2: not parsed, -1 not found. + if (audio_stream != -2) + return audio_stream; + audio_stream = -1; + for(unsigned int i=0; i<_format_context->nb_streams; ++i) { + if(_format_context->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO) { + audio_stream = i; + break; + } + } + return audio_stream; +} + +int AVInfo::videoStream() const +{ + static int video_stream = -2; //-2: not parsed, -1 not found. + if (video_stream != -2) + return video_stream; + video_stream = -1; + for(unsigned int i=0; i<_format_context->nb_streams; ++i) { + if(_format_context->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) { + video_stream = i; + break; + } + } + return video_stream; +} + +int AVInfo::width() const +{ + return videoCodecContext()->width; +} + +int AVInfo::height() const +{ + return videoCodecContext()->height; +} + +QSize AVInfo::frameSize() const +{ + return QSize(width(), height()); +} + +//codec +AVCodecContext* AVInfo::audioCodecContext() const +{ + return a_codec_context; +} + +AVCodecContext* AVInfo::videoCodecContext() const +{ + return v_codec_context; +} + +/*! + call avcodec_open2() first! + check null ptr? +*/ +QString AVInfo::audioCodecName() const +{ + return a_codec_context->codec->name; + //return v_codec_context->codec_name; //codec_name is empty? codec_id is correct +} + +QString AVInfo::audioCodecLongName() const +{ + return a_codec_context->codec->long_name; +} + +QString AVInfo::videoCodecName() const +{ + return v_codec_context->codec->name; + //return v_codec_context->codec_name; //codec_name is empty? codec_id is correct +} + +QString AVInfo::videoCodecLongName() const +{ + return v_codec_context->codec->long_name; +} + + +bool AVInfo::findAVCodec() +{ + static int video_stream = -2; //-2: not parsed, -1 not found. + static int audio_stream = -2; //-2: not parsed, -1 not found. + if (video_stream != -2 && audio_stream != -2) + return (video_stream != -1) && (audio_stream != -1); + video_stream = -1; + audio_stream = -1; + for(unsigned int i=0; i<_format_context->nb_streams; ++i) { + if(_format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO + && video_stream < 0) { + video_stream = i; + v_codec_context = _format_context->streams[video_stream]->codec; + //if !vaapi + if (v_codec_context->codec_id == CODEC_ID_H264) { + v_codec_context->thread_type = FF_THREAD_FRAME; //FF_THREAD_SLICE; + v_codec_context->thread_count = QThread::idealThreadCount(); + } else { + //v_codec_context->lowres = lowres; + } + } + if(_format_context->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO + && audio_stream < 0) { + audio_stream = i; + a_codec_context = _format_context->streams[audio_stream]->codec; + } + if (audio_stream >=0 && video_stream >= 0) + return true; + } + return false; +} + +QString AVInfo::formatName(AVFormatContext *ctx, bool longName) const +{ + if (isInput()) + return longName ? ctx->iformat->long_name : ctx->iformat->name; + else + return longName ? ctx->oformat->long_name : ctx->oformat->name; +} + +} diff --git a/src/AVOutput.cpp b/src/AVOutput.cpp new file mode 100644 index 000000000..ff7f60a87 --- /dev/null +++ b/src/AVOutput.cpp @@ -0,0 +1,14 @@ +#include + +namespace QtAV { +AVOutput::AVOutput() +{ +} + +//TODO: why need this? +AVOutput::~AVOutput() +{ + +} + +} diff --git a/src/AVPlayer.cpp b/src/AVPlayer.cpp new file mode 100644 index 000000000..b01c8bb78 --- /dev/null +++ b/src/AVPlayer.cpp @@ -0,0 +1,150 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace QtAV { +AVPlayer::AVPlayer(QObject *parent) : + QObject(parent),aFrame(0),renderer(0),vFrame(0),audio(0) +{ + avTimerId = -1; + audio = new AudioOutput(); + audio_dec = new AudioDecoder(); + audio_thread = new AudioThread(this); + audio_thread->setPacketQueue(&audio_queue); + audio_thread->setDecoder(audio_dec); + audio_thread->setOutput(audio); + video_thread = 0; +} + +AVPlayer::~AVPlayer() +{ + audio_thread->terminate(); + if (aFrame) + av_free(aFrame); + if (vFrame) + av_free(vFrame); + if (audio) { + delete audio; + audio = 0; + } + if (audio_dec) { + delete audio_dec; + audio_dec = 0; + } + +} + +void AVPlayer::setRenderer(VideoRenderer *renderer) +{ + this->renderer = renderer; +} +//TODO: when is the end +bool AVPlayer::play(const QString& path) +{ + if (!path.isEmpty()) + filename = path; + if (avTimerId > 0) + killTimer(avTimerId); + if (!avinfo.loadFile(filename)) { + return false; + } + avinfo.dump(); + + formatCtx = avinfo.formatContext(); + vCodecCtx = avinfo.videoCodecContext(); + aCodecCtx = avinfo.audioCodecContext(); + audio->setSampleRate(aCodecCtx->sample_rate); + audio->setChannels(aCodecCtx->channels); + audio->open(); + int videoStream = avinfo.videoStream(); + //audio + //if (videoStream < 0) + // return false; + + AVStream *m_v_stream = formatCtx->streams[videoStream]; + qDebug("[AVFormatContext::duration = %lld]", avinfo.duration()); + qDebug("[AVStream::start_time = %lld]", m_v_stream->start_time); + qDebug("[AVCodecContext::time_base = %d, %d, %.2f %.2f]", vCodecCtx->time_base.num, vCodecCtx->time_base.den + ,1.0 * vCodecCtx->time_base.num / vCodecCtx->time_base.den + ,1.0 / (1.0 * vCodecCtx->time_base.num / vCodecCtx->time_base.den)); + qDebug("[AVStream::avg_frame_rate = %d, %d, %.2f]", m_v_stream->avg_frame_rate.num, m_v_stream->avg_frame_rate.den + ,1.0 * m_v_stream->avg_frame_rate.num / m_v_stream->avg_frame_rate.den); + qDebug("[AVStream::r_frame_rate = %d, %d, %.2f]", m_v_stream->r_frame_rate.num, m_v_stream->r_frame_rate.den + ,1.0 * m_v_stream->r_frame_rate.num / m_v_stream->r_frame_rate.den); + qDebug("[AVStream::time_base = %d, %d, %.2f]", m_v_stream->time_base.num, m_v_stream->time_base.den + ,1.0 * m_v_stream->time_base.num / m_v_stream->time_base.den); + frameNo=0; + m_drop_count = 0; + + aFrame = avcodec_alloc_frame(); + vFrame = avcodec_alloc_frame(); + + audio_dec->setCodecContext(aCodecCtx); + audio_dec->setFrame(aFrame); + audio_thread->setDecoder(audio_dec); + audio_thread->start(QThread::HighestPriority); + + avTimerId = startTimer(1000/avinfo.frameRate()); + m_fps1.wake(); + + return true; +} + +void AVPlayer::timerEvent(QTimerEvent* e) +{ + if (e->timerId() != avTimerId) + return; + static AVPacket packet; + static int videoStream = avinfo.videoStream(); + static int audioStream = avinfo.audioStream(); + while (av_read_frame(formatCtx, &packet) >=0 ) { + if (packet.stream_index == audioStream) { + QAVPacket pkt; + pkt.data = QByteArray((const char*)packet.data, packet.size); + pkt.duration = packet.duration; + pkt.pts = packet.pts; + audio_queue.enqueue(pkt); + audio_thread->wakeAll(); + av_free_packet(&packet); //TODO: why is needed for static var? + } else if (packet.stream_index == videoStream) { + int len = avcodec_decode_video2(vCodecCtx, vFrame, &frameFinished, &packet); + if (len < 0) { + fprintf(stderr, "Error while decoding vFrame %d\n", frameNo); + return; + } + if (frameFinished) { + frameNo++; + m_fps1.add(1); + if(m_time_line.isNull()) + m_time_line.start(); + //qDebug() << "m_time_line.elapsed()" << m_time_line.elapsed(); + if(packet.pts < m_time_line.elapsed()) { + printf("=====>SKIP[frameNo] %d [packet.pts] %lld\r", frameNo, packet.pts); + m_drop_count++; + //continue; + } + //qDebug(" [frameNo] %d" "[packet.pts] %d", frameNo, packet.pts); + + QByteArray data = renderer->scale(vCodecCtx, vFrame); //scaler? + renderer->write(data); + } + av_free_packet(&packet); + break; + } else { //subtitle + av_free_packet(&packet); + continue; + } + } +} + +} diff --git a/src/AVThread.cpp b/src/AVThread.cpp new file mode 100644 index 000000000..9a905a54a --- /dev/null +++ b/src/AVThread.cpp @@ -0,0 +1,52 @@ +#include +#include + +namespace QtAV { +AVThread::AVThread(QObject *parent) : + QThread(parent),d_ptr(new AVThreadPrivate()) +{ +} + +AVThread::AVThread(AVThreadPrivate &d, QObject *parent) + :QThread(parent),d_ptr(&d) +{ + qDebug("protected ctor"); +} + +AVThread::~AVThread() +{ +} + +void AVThread::wakeAll() +{ + d_ptr->not_empty_cond.wakeOne(); +} + +void AVThread::setPacketQueue(QAVPacketQueue *queue) +{ + d_ptr->packets = queue; +} + + +void AVThread::setDecoder(AVDecoder *decoder) +{ + d_ptr->dec = decoder; +} + +AVDecoder* AVThread::decoder() const +{ + return d_ptr->dec; +} + +void AVThread::setOutput(AVOutput *out) +{ + d_ptr->writer = out; +} + +AVOutput* AVThread::output() const +{ + return d_ptr->writer; +} + +} + diff --git a/src/AudioDecoder.cpp b/src/AudioDecoder.cpp new file mode 100644 index 000000000..cd19e58f7 --- /dev/null +++ b/src/AudioDecoder.cpp @@ -0,0 +1,68 @@ +#include + +#ifdef __cplusplus +extern "C" +{ +#endif //__cplusplus +#include "libavformat/avformat.h" +#include "libswscale/swscale.h" +#include "libavcodec/avcodec.h" +#ifdef __cplusplus +} +#endif //__cplusplus + +namespace QtAV { +// +bool AudioDecoder::decode(const QByteArray &encoded) +{ + int bytes_consumed = 0, frameFinished; + + AVPacket packet; + av_new_packet(&packet, encoded.size()); + memcpy(packet.data, encoded.data(), encoded.size() ); +//TODO: use AVPacket directly instead of QAVPacket? + bytes_consumed = avcodec_decode_audio4(codec_ctx, frame_, &frameFinished, &packet); + av_free_packet(&packet); + if (frameFinished) { + const int samples_with_channels = frame_->nb_samples * codec_ctx->channels; + decoded.resize(samples_with_channels * sizeof(float)); + float *decoded_data = (float*)decoded.data(); + switch (codec_ctx->sample_fmt) { + case AV_SAMPLE_FMT_U8: + { uint8_t *data = (uint8_t*)*frame_->data; + for (int i = 0; i < samples_with_channels; i++) + decoded_data[i] = (data[i] - 0x7F) / 128.0f; + break;} + case AV_SAMPLE_FMT_S16: + { int16_t *data = (int16_t*)*frame_->data; + for (int i = 0; i < samples_with_channels; i++) + decoded_data[i] = data[i] / 32768.0f; + break;} + case AV_SAMPLE_FMT_S32: + { int32_t *data = (int32_t*)*frame_->data; + for (int i = 0; i < samples_with_channels; i++) + decoded_data[i] = data[i] / 2147483648.0f; + break;} + case AV_SAMPLE_FMT_FLT: + { memcpy(decoded_data, *frame_->data, decoded.size()); + break;} + case AV_SAMPLE_FMT_DBL: + { double *data = ( double * )*frame_->data; + for (int i = 0; i < samples_with_channels; i++) + decoded_data[ i ] = data[ i ]; + break;} + default: + decoded.clear(); + break; + } +/* + if ( pts ) + clock = pts; + else + pts = clock; + clock += (double)frame->nb_samples / (double)codec_ctx->sample_rate; +*/ + + } +} +} diff --git a/src/AudioOutput.cpp b/src/AudioOutput.cpp new file mode 100644 index 000000000..66bbade40 --- /dev/null +++ b/src/AudioOutput.cpp @@ -0,0 +1,107 @@ +#include +#include + +namespace QtAV { +AudioOutput::AudioOutput() + :AVOutput() +{ + outputParameters = new PaStreamParameters; + Pa_Initialize(); + + stream = NULL; + sample_rate = 0; + memset(outputParameters, 0, sizeof(PaStreamParameters)); + outputParameters->device = Pa_GetDefaultOutputDevice(); + outputParameters->sampleFormat = paFloat32; + outputParameters->hostApiSpecificStreamInfo = NULL; + outputParameters->suggestedLatency = Pa_GetDeviceInfo(outputParameters->device)->defaultHighOutputLatency; + +} + + +AudioOutput::~AudioOutput() +{ + close(); + Pa_Terminate(); + if (outputParameters) { + delete outputParameters; + outputParameters = 0; + } +} + +void AudioOutput::setSampleRate(int rate) +{ + sample_rate = rate; +} + +void AudioOutput::setChannels(int channels) +{ + outputParameters->channelCount = channels; +} + +int AudioOutput::write(const QByteArray &data) +{ + if (Pa_IsStreamStopped(stream)) + Pa_StartStream(stream); + +#ifndef Q_OS_MAC //? + int diff = Pa_GetStreamWriteAvailable(stream) - outputLatency * sample_rate; + if (diff > 0) { + int newsize = diff * outputParameters->channelCount * sizeof(float); + char a[newsize]; + memset(a, 0, newsize); + Pa_WriteStream(stream, a, diff); + } +#endif + +#ifdef Q_OS_LINUX + int chn = outputParameters->channelCount; + if (chn == 6 || chn == 8) { + float *audio_buffer = (float *)data.data(); + int size_per_chn = data.size() >> 2; + for (int i = 0 ; i < size_per_chn; i += chn) { + float tmp = audio_buffer[i+2]; + audio_buffer[i+2] = audio_buffer[i+4]; + audio_buffer[i+4] = tmp; + tmp = audio_buffer[i+3]; + audio_buffer[i+3] = audio_buffer[i+5]; + audio_buffer[i+5] = tmp; + } + } +#endif + + PaError err = Pa_WriteStream(stream, data.data(), data.size() / outputParameters->channelCount / sizeof(float)); + if (err == paUnanticipatedHostError) { + qWarning("Write portaudio stream error: %s", Pa_GetErrorText(err)); + return 0; + } + + return data.size(); +} + +bool AudioOutput::open() +{ + PaError err = Pa_OpenStream(&stream, NULL, outputParameters, sample_rate, 0, paNoFlag, NULL, NULL); + if (err == paNoError) + outputLatency = Pa_GetStreamInfo(stream)->outputLatency; + else + qWarning("Open portaudio stream error: %s", Pa_GetErrorText(err)); + return err == paNoError; +} + +bool AudioOutput::close() +{ + PaError err = paNoError; + if (!stream) { + return true; + } + err = Pa_StopStream(stream); + if (err != paNoError) + qWarning("Stop portaudio stream error: %s", Pa_GetErrorText(err)); + err = Pa_CloseStream(stream); + stream = NULL; + if (err != paNoError) + qWarning("Close portaudio stream error: %s", Pa_GetErrorText(err)); + return err == paNoError; +} +} diff --git a/src/AudioThread.cpp b/src/AudioThread.cpp new file mode 100644 index 000000000..30dc85d18 --- /dev/null +++ b/src/AudioThread.cpp @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif //__cplusplus +#include "libavformat/avformat.h" +#include "libswscale/swscale.h" +#include "libavcodec/avcodec.h" +#ifdef __cplusplus +} +#endif //__cplusplus + +namespace QtAV { +class AudioThreadPrivate : public AVThreadPrivate +{ +public: + +}; + +AudioThread::AudioThread(QObject *parent) + :AVThread(*new AudioThreadPrivate(), parent) +{ +} + +void AudioThread::run() +{ + //No decoder or output, no audio + if (!d_ptr->dec || !d_ptr->writer) + return; + Q_ASSERT(d_ptr->packets != 0); + while (true) { + d_ptr->mutex.lock(); + int i = 0; + while (d_ptr->packets->isEmpty()) { + d_ptr->not_empty_cond.wait(&d_ptr->mutex, 1000); //why sometines return immediatly? + qDebug("audio data size: %d, loop %d", d_ptr->packets->size(), ++i); + } + QAVPacket pkt = d_ptr->packets->dequeue(); + //qDebug("audio data size after dequeue: %d", d_ptr->packets->size()); + if (d_ptr->dec->decode(pkt.data)) { + //play sound + QByteArray decoded(d_ptr->dec->data()); + int decodedSize = decoded.size(); + int decodedPos = 0; + while (decodedSize > 0) { + static int sample_rate = d_ptr->dec->codecContext()->sample_rate; + static int channels = d_ptr->dec->codecContext()->channels; + const double max_len = /*playC.frame_last_delay > 0.0 ? qMin( playC.frame_last_delay, 0.02 ) :*/ 0.02; + const int chunk = qMin(decodedSize, int(ceil(sample_rate * max_len) * channels * sizeof(float))); + + QByteArray decodedChunk; + //if ( playC.vol == 0.0 || playC.muted ) + // decodedChunk.fill( 0, chunk ); + //else + decodedChunk = QByteArray::fromRawData(decoded.data() + decodedPos, chunk); + + decodedPos += chunk; + decodedSize -= chunk; + d_func()->writer->write(decodedChunk); + //qApp->processEvents(QEventLoop::AllEvents); + } + } + d_ptr->mutex.unlock(); + } +} + +} diff --git a/src/GraphicsItemRenderer.cpp b/src/GraphicsItemRenderer.cpp new file mode 100644 index 000000000..7c4ffc5da --- /dev/null +++ b/src/GraphicsItemRenderer.cpp @@ -0,0 +1,32 @@ +#include +#include + +namespace QtAV { +GraphicsItemRenderer::GraphicsItemRenderer(QGraphicsItem * parent) + :QGraphicsItem(parent),ImageRenderer() +{ +} + +GraphicsItemRenderer::~GraphicsItemRenderer() +{ + +} + +int GraphicsItemRenderer::write(const QByteArray &data) +{ + int s = ImageRenderer::write(data); + update(); + return s; +} + +QRectF GraphicsItemRenderer::boundingRect() const +{ + return QRectF(0, 0, videoWidth(), videoHeight()); +} + +void GraphicsItemRenderer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + painter->drawImage(QPointF(), image); +} + +} diff --git a/src/ImageRenderer.cpp b/src/ImageRenderer.cpp new file mode 100644 index 000000000..ce28cb7fc --- /dev/null +++ b/src/ImageRenderer.cpp @@ -0,0 +1,86 @@ +#include +#include + +namespace QtAV { +ImageRenderer::~ImageRenderer() +{ + +} + +int ImageRenderer::write(const QByteArray &data) +{ + //picture.data[0] +#if QT_VERSION >= QT_VERSION_CHECK(4, 0, 0) + image = QImage((uchar*)data.data(), d_func()->width, d_func()->height, QImage::Format_ARGB32_Premultiplied); +#else + image = QImage((uchar*)data.data(), d_func()->width, d_func()->height, 16, NULL, 0, QImage::IgnoreEndian); +#endif + return data.size(); +} + + +void ImageRenderer::setPreview(const QImage &preivew) +{ + this->preview = preivew; + image = preivew; +} + +QImage ImageRenderer::previewImage() const +{ + return preview; +} + +QImage ImageRenderer::currentImage() const +{ + return image; +} + +/* +void WidgetRenderer::render() +{ +#if CONFIG_EZX + QPixmap pix; + pix.convertFromImage(m_displayBuffer); + QPainter v_p(&pix); +#else + QPainter v_p(&m_displayBuffer); +#endif //CONFIG_EZX + //v_p.drawImage(QPoint(0, 0), m_displayBuffer); + QTime v_elapsed(0, 0, 0, 0); + v_elapsed = v_elapsed.addMSecs(m_time_line.elapsed()); +#if QT_VERSION >= 0x030000 + QString v_msg = QString("E=%1 F=%2 D=%3").arg(v_elapsed.toString("hh:mm:ss[zzz]")).arg(frameNo).arg(m_drop_count); +#else + QString v_msg = QString("E=%1 F=%2 D=%3").arg(v_elapsed.toString()).arg(frameNo).arg(m_drop_count); +#endif + QFont v_f("MS UI Gothic", 16, QFont::Bold); + v_f.setFixedPitch(true); + v_p.setFont(v_f); + //[Time etc] + int v_diff = 1; + int v_x_off = 2; + int v_y_off = height() - 4; + v_p.setPen(Qt::white); + v_p.drawText(QPoint(v_x_off+v_diff, v_y_off+v_diff), v_msg); + v_p.setPen(Qt::black); + v_p.drawText(QPoint(v_x_off, v_y_off), v_msg); + //[FPS] + int v_font_size = v_p.font().pointSize(); + v_x_off = 2; + v_y_off = v_font_size + 4; + //v_msg = QString("%1 fps").arg(m_fps1.value(), 6, 'f', 2, ' '); + v_msg = QString::number(m_fps1.value(), 'f', 2) + "fps"; + v_p.setPen(Qt::white); + v_p.drawText(QPoint(v_x_off+v_diff, v_y_off+v_diff), v_msg); + v_p.setPen(Qt::black); + v_p.drawText(QPoint(v_x_off, v_y_off), v_msg); + +#if CONFIG_EZX + bitBlt(this, QPoint(), &pix); +#else + update(); +#endif +} +*/ + +} diff --git a/src/QAVPacket.cpp b/src/QAVPacket.cpp new file mode 100644 index 000000000..7e816e6cb --- /dev/null +++ b/src/QAVPacket.cpp @@ -0,0 +1,9 @@ +#include + +namespace QtAV { +QAVPacket::QAVPacket() + :pts(0),duration(0) +{ +} + +} diff --git a/src/QAVPacketQueue.cpp b/src/QAVPacketQueue.cpp new file mode 100644 index 000000000..5b83aaff3 --- /dev/null +++ b/src/QAVPacketQueue.cpp @@ -0,0 +1,44 @@ +#include +#include + +namespace QtAV { + +QAVPacketQueue::QAVPacketQueue() + :mutex(QMutex::Recursive) +{ +} + +QAVPacketQueue::~QAVPacketQueue() +{ + queue.clear(); +} + +void QAVPacketQueue::enqueue(const QAVPacket& packet) +{ + QMutexLocker lock(&mutex); + Q_UNUSED(lock); + queue.enqueue(packet); +} + +QAVPacket QAVPacketQueue::dequeue() +{ + QMutexLocker lock(&mutex); + Q_UNUSED(lock); + return queue.dequeue(); +} + +QAVPacket QAVPacketQueue::head() +{ + QMutexLocker lock(&mutex); + Q_UNUSED(lock); + return queue.head(); +} + +QAVPacket QAVPacketQueue::tail() +{ + QMutexLocker lock(&mutex); + Q_UNUSED(lock); + return queue.last(); +} + +} diff --git a/src/QtAV/AVDecoder.h b/src/QtAV/AVDecoder.h new file mode 100644 index 000000000..3a6fdc771 --- /dev/null +++ b/src/QtAV/AVDecoder.h @@ -0,0 +1,32 @@ +#ifndef QAVDECODER_H +#define QAVDECODER_H + +#include +#include + +struct AVCodecContext; +struct AVFrame; + +namespace QtAV { +class Q_EXPORT AVDecoder +{ +public: + AVDecoder(); + virtual ~AVDecoder(); + void setCodecContext(AVCodecContext* codecCtx); //protected + AVCodecContext* codecContext() const; + + void setFrame(AVFrame* frame); + AVFrame* frame() const; + + virtual bool decode(const QByteArray& encoded) = 0; //decode AVPacket? + QByteArray data() const; //decoded data + +protected: + AVCodecContext *codec_ctx; //set once and not change + AVFrame *frame_; //set once and not change + QByteArray decoded; +}; +} + +#endif // QAVDECODER_H diff --git a/src/QtAV/AVDemuxThread.h b/src/QtAV/AVDemuxThread.h new file mode 100644 index 000000000..7d8b0e356 --- /dev/null +++ b/src/QtAV/AVDemuxThread.h @@ -0,0 +1,26 @@ +#ifndef QAVDEMUXTHREAD_H +#define QAVDEMUXTHREAD_H + +#include +#include + +namespace QtAV { +class AVDemuxer; +class QAVPacketQueue; +class Q_EXPORT AVDemuxThread : public QThread +{ + Q_OBJECT +public: + explicit AVDemuxThread(QObject *parent = 0); + +protected: + virtual void run(); + +private: + AVDemuxer *demuxer; + QAVPacketQueue *audio_queue, *video_queue; + +}; +} + +#endif // QAVDEMUXTHREAD_H diff --git a/src/QtAV/AVDemuxer.h b/src/QtAV/AVDemuxer.h new file mode 100644 index 000000000..0de0e68e7 --- /dev/null +++ b/src/QtAV/AVDemuxer.h @@ -0,0 +1,22 @@ +#ifndef QAVDEMUXER_H +#define QAVDEMUXER_H + +#include + +struct AVFormatContext; + +namespace QtAV { +class QAVPacket; +class Q_EXPORT AVDemuxer +{ +public: + AVDemuxer(); + + bool readPacket(QAVPacket* packet, int stream); + +private: + AVFormatContext *formatCtx; +}; +} + +#endif // QAVDEMUXER_H diff --git a/src/QtAV/AVInfo.h b/src/QtAV/AVInfo.h new file mode 100644 index 000000000..a9db70598 --- /dev/null +++ b/src/QtAV/AVInfo.h @@ -0,0 +1,85 @@ +/****************************************************************************** + qavinfo.h: description + Copyright (C) 2012 Wang Bin + + 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; either version 2 of the License, or + (at your option) any later version. + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +******************************************************************************/ + + +#ifndef QAVINFO_H +#define QAVINFO_H + +#include +#include +#include +#include + +struct AVFormatContext; +struct AVCodecContext; +struct AVCodec; +struct AVFrame; +struct AVStream; +//TODO: use AVDemuxer + +namespace QtAV { +class Q_EXPORT AVInfo +{ +public: + AVInfo(const QString& fileName = QString()); //parse the file + ~AVInfo(); + bool loadFile(const QString& fileName); + + //format + AVFormatContext* formatContext(); + void dump(); + QString fileName() const; //AVFormatContext::filename + QString audioFormatName() const; + QString audioFormatLongName() const; + QString videoFormatName() const; //AVFormatContext::iformat->name + QString videoFormatLongName() const; //AVFormatContext::iformat->long_name + qint64 startTime() const; //AVFormatContext::start_time + qint64 duration() const; //AVFormatContext::duration + int bitRate() const; //AVFormatContext::bit_rate + qreal frameRate() const; //AVFormatContext::r_frame_rate + qint64 frames() const; //AVFormatContext::nb_frames + bool isInput() const; + int audioStream() const; + int videoStream() const; + + int width() const; //AVCodecContext::width; + int height() const; //AVCodecContext::height + QSize frameSize() const; + + //codec + AVCodecContext* audioCodecContext() const; + AVCodecContext* videoCodecContext() const; + QString audioCodecName() const; + QString audioCodecLongName() const; + QString videoCodecName() const; + QString videoCodecLongName() const; + +private: + bool findAVCodec(); + QString formatName(AVFormatContext *ctx, bool longName = false) const; + + bool _is_input; + AVFormatContext *_format_context; + AVCodecContext *a_codec_context, *v_codec_context; + //copy the info, not parse the file when constructed, then need member vars + QString _file_name; +}; +} + +#endif // QAVINFO_H diff --git a/src/QtAV/AVOutput.h b/src/QtAV/AVOutput.h new file mode 100644 index 000000000..909628478 --- /dev/null +++ b/src/QtAV/AVOutput.h @@ -0,0 +1,19 @@ +#ifndef QAVWRITER_H +#define QAVWRITER_H + +#include +#include + +namespace QtAV { +class Q_EXPORT AVOutput +{ +public: + AVOutput(); + virtual ~AVOutput() = 0; + virtual int write(const QByteArray& data) = 0; + virtual bool open() = 0; + virtual bool close() = 0; +}; +} + +#endif //QAVWRITER_H diff --git a/src/QtAV/AVPlayer.h b/src/QtAV/AVPlayer.h new file mode 100644 index 000000000..a2d466f10 --- /dev/null +++ b/src/QtAV/AVPlayer.h @@ -0,0 +1,91 @@ +#ifndef WIDGET_H +#define WIDGET_H + +#include +#include +#include + +class T_FPS +{ +public: + QTime m_time_line; + double m_fps; + int m_count; + explicit T_FPS() : m_fps(0), m_count(0) {} + bool wake() { + if(!m_time_line.isNull()) return false; + m_time_line.start(); + return true; + } + void restart() { + m_count = 0; + m_time_line.start(); + } + void add(int a) { + m_count += a; + } + double value() { + if(m_time_line.isNull()) { + qDebug("[T_FPS.value()] warning: T_FPS.wake() was not called."); + return 0; + } else if(m_time_line.elapsed() < 1000) { + return m_fps; + } else { + m_fps = 1000.0 * m_count / m_time_line.elapsed(); + restart(); + return m_fps; + } + } +}; + +#include +#include +#include + +namespace QtAV { +class AudioOutput; +class AudioThread; +class QAVVideoThread; +class AudioDecoder; +class VideoRenderer; +class Q_EXPORT AVPlayer : public QObject +{ + Q_OBJECT +public: + explicit AVPlayer(QObject *parent = 0); + ~AVPlayer(); + + void setFile(const QString& fileName) {filename=fileName;} + bool play(const QString& path = QString()); + void setRenderer(VideoRenderer* renderer); + +protected: + int avTimerId; + AVFormatContext *formatCtx; //changed when reading a packet + AVCodecContext *aCodecCtx, *vCodecCtx; //set once and not change + AVFrame *aFrame, *vFrame; //set once and not change + int frameFinished; + QString filename; + int frameNo; + QImage m_displayBuffer; + //T_AVPicture *m_av_picture; + QTime m_time_line; + int m_drop_count; + T_FPS m_fps1; + + AVInfo avinfo; + VideoRenderer *renderer; //list? + AudioOutput *audio; + AudioDecoder *audio_dec; + AudioThread *audio_thread; + QAVVideoThread *video_thread; + QAVPacketQueue audio_queue, video_queue; +protected: + virtual void timerEvent(QTimerEvent *); + + void render(); + +}; +} + +#endif // WIDGET_H diff --git a/src/QtAV/AVThread.h b/src/QtAV/AVThread.h new file mode 100644 index 000000000..0f66cf209 --- /dev/null +++ b/src/QtAV/AVThread.h @@ -0,0 +1,39 @@ +#ifndef AVTHREAD_H +#define AVTHREAD_H + +#include +#include +#include + +namespace QtAV { +class AVDecoder; +class QAVPacketQueue; +class AVThreadPrivate; +class AVOutput; +class Q_EXPORT AVThread : public QThread +{ + Q_OBJECT +public: + explicit AVThread(QObject *parent = 0); + virtual ~AVThread(); + + void wakeAll(); + + void setPacketQueue(QAVPacketQueue *queue); + void packetQueue() const; + + void setDecoder(AVDecoder *decoder); + AVDecoder *decoder() const; + + void setOutput(AVOutput *out); + AVOutput* output() const; + +protected: + AVThread(AVThreadPrivate& d, QObject *parent = 0); + + Q_DECLARE_PRIVATE(AVThread) + QScopedPointer d_ptr; +}; +} + +#endif // AVTHREAD_H diff --git a/src/QtAV/AudioDecoder.h b/src/QtAV/AudioDecoder.h new file mode 100644 index 000000000..2c2a60ffe --- /dev/null +++ b/src/QtAV/AudioDecoder.h @@ -0,0 +1,16 @@ +#ifndef QAVAUDIODECODER_H +#define QAVAUDIODECODER_H + +#include + +namespace QtAV { + +class Q_EXPORT AudioDecoder : public AVDecoder +{ +public: + virtual bool decode(const QByteArray &encoded); + +}; +} + +#endif // QAVAUDIODECODER_H diff --git a/src/QtAV/AudioOutput.h b/src/QtAV/AudioOutput.h new file mode 100644 index 000000000..c78fac6b9 --- /dev/null +++ b/src/QtAV/AudioOutput.h @@ -0,0 +1,35 @@ +#ifndef QAVAUDIOWRITER_H +#define QAVAUDIOWRITER_H + +#include + +struct PaStreamParameters; +typedef void PaStream; + +namespace QtAV { + +class Q_EXPORT AudioOutput : public AVOutput +{ +public: + AudioOutput(); + virtual ~AudioOutput(); + + void setSampleRate(int rate); + void setChannels(int channels); + int write(const QByteArray& data); + + virtual bool open(); + virtual bool close(); + +protected: + PaStreamParameters *outputParameters; + PaStream *stream; + int sample_rate; + double outputLatency; +#ifdef Q_OS_LINUX + bool autoFindMultichannelDevice; +#endif +}; +} + +#endif // QAVAUDIOWRITER_H diff --git a/src/QtAV/AudioThread.h b/src/QtAV/AudioThread.h new file mode 100644 index 000000000..53231e73f --- /dev/null +++ b/src/QtAV/AudioThread.h @@ -0,0 +1,23 @@ +#ifndef QAVAUDIOTHREAD_H +#define QAVAUDIOTHREAD_H + +#include +#include + +namespace QtAV { + +class AudioDecoder; +class AudioThreadPrivate; +class Q_EXPORT AudioThread : public AVThread +{ + Q_OBJECT + Q_DECLARE_PRIVATE(AudioThread) +public: + explicit AudioThread(QObject *parent = 0); + +protected: + virtual void run(); +}; +} + +#endif // QAVAUDIOTHREAD_H diff --git a/src/QtAV/GraphicsItemRenderer.h b/src/QtAV/GraphicsItemRenderer.h new file mode 100644 index 000000000..5298c097c --- /dev/null +++ b/src/QtAV/GraphicsItemRenderer.h @@ -0,0 +1,21 @@ +#ifndef QAVGRAPHICSITEMRENDERER_H +#define QAVGRAPHICSITEMRENDERER_H + +#include +#include +#include + +namespace QtAV { +class Q_EXPORT GraphicsItemRenderer : public QGraphicsItem, public ImageRenderer +{ +public: + GraphicsItemRenderer(QGraphicsItem * parent = 0); + virtual ~GraphicsItemRenderer(); + virtual int write(const QByteArray &data); + + QRectF boundingRect() const; + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); +}; +} + +#endif // QAVGRAPHICSITEMRENDERER_H diff --git a/src/QtAV/ImageRenderer.h b/src/QtAV/ImageRenderer.h new file mode 100644 index 000000000..d6ecb5cb9 --- /dev/null +++ b/src/QtAV/ImageRenderer.h @@ -0,0 +1,22 @@ +#ifndef QAVIMAGERENDERER_H +#define QAVIMAGERENDERER_H + +#include +#include + +namespace QtAV { +class Q_EXPORT ImageRenderer : public VideoRenderer +{ +public: + virtual ~ImageRenderer(); + virtual int write(const QByteArray &data); + void setPreview(const QImage& preview); + QImage previewImage() const; + QImage currentImage() const; +protected: + QImage image; + QImage preview; +}; +} + +#endif // QAVIMAGERENDERER_H diff --git a/src/QtAV/QAVPacket.h b/src/QtAV/QAVPacket.h new file mode 100644 index 000000000..087414895 --- /dev/null +++ b/src/QtAV/QAVPacket.h @@ -0,0 +1,18 @@ +#ifndef QAVPACKET_H +#define QAVPACKET_H + +#include +#include + +namespace QtAV { +class Q_EXPORT QAVPacket +{ +public: + QAVPacket(); + + QByteArray data; + qreal pts, duration; +}; +} + +#endif // QAVPACKET_H diff --git a/src/QtAV/QAVPacketQueue.h b/src/QtAV/QAVPacketQueue.h new file mode 100644 index 000000000..5205ee83a --- /dev/null +++ b/src/QtAV/QAVPacketQueue.h @@ -0,0 +1,39 @@ +#ifndef QAVPACKETQUEUE_H +#define QAVPACKETQUEUE_H + +#include +#include +#include + +namespace QtAV { +class QAVPacket; +class Q_EXPORT QAVPacketQueue +{ +public: + QAVPacketQueue(); + ~QAVPacketQueue(); + + inline bool isEmpty() const; + inline int size() const; + void enqueue(const QAVPacket& packet); + QAVPacket dequeue(); + QAVPacket head(); + QAVPacket tail(); + +private: + QQueue queue; + QMutex mutex; +}; + +bool QAVPacketQueue::isEmpty() const +{ + return queue.isEmpty(); +} + +int QAVPacketQueue::size() const +{ + return queue.size(); +} +} + +#endif // QAVPACKETQUEUE_H diff --git a/src/QtAV/QtAV_Global.h b/src/QtAV/QtAV_Global.h new file mode 100644 index 000000000..5687c2c55 --- /dev/null +++ b/src/QtAV/QtAV_Global.h @@ -0,0 +1,58 @@ +/****************************************************************************** + qtav_global.h: description + Copyright (C) 2011-2012 Wang Bin + + 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; either version 2 of the License, or + (at your option) any later version. + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +******************************************************************************/ + +#ifndef QTAV_GLOBAL_H +#define QTAV_GLOBAL_H + +#include + +#undef LIB_VERSION + +#define MAJOR 1 //((LIB_VERSION&0xff0000)>>16) +#define MINOR 0 //((LIB_VERSION&0xff00)>>8) +#define PATCH 0 //(LIB_VERSION&0xff) + +#define VERSION_CHK(major, minor, patch) \ + (((major&0xff)<<16) | ((minor&0xff)<<8) | (patch&0xff)) + +#define LIB_VERSION VERSION_CHK(MAJOR, MINOR, PATCH) + +/*! Stringify \a x. */ +#define _TOSTR(x) #x +/*! Stringify \a x, perform macro expansion. */ +#define TOSTR(x) _TOSTR(x) + +/* C++11 requires a space between literal and identifier */ +static const char* const version_string = TOSTR(MAJOR) "." TOSTR(MINOR) "." TOSTR(PATCH) "(" __DATE__ ", " __TIME__ ")"; +#define LIB_VERSION_STR TOSTR(MAJOR) "." TOSTR(MINOR) "." TOSTR(PATCH) +#define LIB_VERSION_STR_LONG LIB_VERSION_STR "(" __DATE__ ", " __TIME__ ")" + + + + +#if defined(Q_DLL_LIBRARY) +# undef Q_EXPORT +# define Q_EXPORT Q_DECL_EXPORT +#else +# undef Q_EXPORT +# define Q_EXPORT //Q_DECL_IMPORT //only for vc? +#endif + +#endif // QTAV_GLOBAL_H + diff --git a/src/QtAV/VideoRenderer.h b/src/QtAV/VideoRenderer.h new file mode 100644 index 000000000..af024a02b --- /dev/null +++ b/src/QtAV/VideoRenderer.h @@ -0,0 +1,36 @@ +#ifndef QAVVIDEORENDERER_H +#define QAVVIDEORENDERER_H + +#include +#include +#include + +struct AVCodecContext; +struct AVFrame; + +namespace QtAV { +class VideoRendererPrivate; +class Q_EXPORT VideoRenderer : public AVOutput +{ +public: + VideoRenderer(); + virtual ~VideoRenderer() = 0; + + virtual bool open() {return true;} + virtual bool close() {return true;} + void resizeVideo(const QSize& size); + void resizeVideo(int width, int height); + QByteArray scale(AVCodecContext* codecCtx, AVFrame *frame); + QSize videoSize() const; + int videoWidth() const; + int videoHeight() const; + //return the bytes writed + //virtual int write(const QByteArray& data) = 0; + +protected: + Q_DECLARE_PRIVATE(VideoRenderer) + VideoRendererPrivate* d_ptr; +}; +} + +#endif // QAVVIDEORENDERER_H diff --git a/src/QtAV/WidgetRenderer.h b/src/QtAV/WidgetRenderer.h new file mode 100644 index 000000000..155aa9be9 --- /dev/null +++ b/src/QtAV/WidgetRenderer.h @@ -0,0 +1,33 @@ +#ifndef QAVWIDGETRENDERER_H +#define QAVWIDGETRENDERER_H + +#include +#include + +namespace QtAV { +class Q_EXPORT WidgetRenderer : public QWidget, public ImageRenderer +{ + Q_OBJECT +public: + enum GestureAction { GestureMove, GestureResize}; + explicit WidgetRenderer(QWidget *parent = 0); + virtual ~WidgetRenderer(); + virtual int write(const QByteArray &data); + void setPreview(const QImage& preivew); + +protected: + virtual void resizeEvent(QResizeEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void mouseMoveEvent(QMouseEvent *); + virtual void mouseDoubleClickEvent(QMouseEvent *); +#if !CONFIG_EZX + virtual void paintEvent(QPaintEvent *); +#endif + +private: + QPoint pos, gPos; + GestureAction action; +}; +} + +#endif // QAVWIDGETRENDERER_H diff --git a/src/QtAV/private/AVThread_p.h b/src/QtAV/private/AVThread_p.h new file mode 100644 index 000000000..366fb728f --- /dev/null +++ b/src/QtAV/private/AVThread_p.h @@ -0,0 +1,49 @@ +#ifndef AVTHREAD_P_H +#define AVTHREAD_P_H + +//#include +#include +#include +#include + +#include +#include +#include +#include + +namespace QtAV { +class AVDecoder; +class QAVPacket; +class AVThreadPrivate +{ +public: + AVThreadPrivate():packets(0),dec(0),writer(0) {} + virtual ~AVThreadPrivate() { + /*if (dec) { + delete dec; + dec = 0; + } + if (writer) { + delete writer; + writer = 0; + } + if (packets) { + delete packets; + packets = 0; + }*/ + } + void enqueue(const QAVPacket& pkt) { + packets->enqueue(pkt); + not_empty_cond.wakeAll(); + } + + + QAVPacketQueue *packets; + AVDecoder *dec; + AVOutput *writer; + QMutex mutex; + QWaitCondition not_empty_cond; +}; +} + +#endif // AVTHREAD_P_H diff --git a/src/QtAV/private/VideoRenderer_p.h b/src/QtAV/private/VideoRenderer_p.h new file mode 100644 index 000000000..2456241d3 --- /dev/null +++ b/src/QtAV/private/VideoRenderer_p.h @@ -0,0 +1,40 @@ +#ifndef QAVVIDEORENDERER_P_H +#define QAVVIDEORENDERER_P_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif //__cplusplus +#include "libavformat/avformat.h" +#include "libswscale/swscale.h" +#include "libavcodec/avcodec.h" +#ifdef __cplusplus +} +#endif //__cplusplus + +#if CONFIG_EZX +#define PIX_FMT PIX_FMT_BGR565 +#else +#define PIX_FMT PIX_FMT_RGB32 +#endif //CONFIG_EZX + +namespace QtAV { +class VideoRendererPrivate +{ +public: + VideoRendererPrivate():width(0),height(0),pix_fmt(PIX_FMT),numBytes(0) { + } + + void resizePicture(int width, int height); + + int width, height; + AVPicture picture; + enum PixelFormat pix_fmt; + int numBytes; + QByteArray data; +}; +} + +#endif // QAVVIDEORENDERER_P_H diff --git a/src/QtAV/qtav.h b/src/QtAV/qtav.h new file mode 100644 index 000000000..148ac902d --- /dev/null +++ b/src/QtAV/qtav.h @@ -0,0 +1,27 @@ +/****************************************************************************** + qtav.h: description + Copyright (C) 2011-2012 Wang Bin + + 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; either version 2 of the License, or + (at your option) any later version. + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +******************************************************************************/ + +#ifndef QTAV_H +#define QTAV_H + +#include "qtav_global.h" + + +#endif //QTAV_H + diff --git a/src/VideoRenderer.cpp b/src/VideoRenderer.cpp new file mode 100644 index 000000000..43c790b73 --- /dev/null +++ b/src/VideoRenderer.cpp @@ -0,0 +1,109 @@ +#include +#include +/*! + EZX: + PIX_FMT_BGR565, 16bpp, + PIX_FMT_RGB18, 18bpp, +*/ + +namespace QtAV { + +void VideoRendererPrivate::resizePicture(int width, int height) +{ + int v_numBytes = avpicture_get_size(pix_fmt, width, height); + qDebug("[v_numBytes][m_numBytes] %d %d", v_numBytes, numBytes); + if(numBytes < v_numBytes) + { + numBytes = v_numBytes; + data.resize(numBytes); + } + //picture的数据按pix_fmt格式自动"关联"到 data + avpicture_fill( + &picture, + reinterpret_cast(data.data()), + pix_fmt, + width, + height + ); + this->width = width; + this->height = height; +} + + + +VideoRenderer::VideoRenderer() + :d_ptr(new VideoRendererPrivate) +{ +} + +VideoRenderer::~VideoRenderer() +{ + if (d_ptr) { + delete d_ptr; + d_ptr = 0; + } +} + +void VideoRenderer::resizeVideo(const QSize &size) +{ + resizeVideo(size.width(), size.height()); +} + +void VideoRenderer::resizeVideo(int width, int height) +{ + Q_D(VideoRenderer); + if (width >0 && height >0) + d->resizePicture(width, height); +} + +QByteArray VideoRenderer::scale(AVCodecContext *codecCtx, AVFrame *frame) +{ + Q_D(VideoRenderer); + SwsContext *v_sws_ctx = sws_getContext( + codecCtx->width, //int srcW, + codecCtx->height, //int srcH, + codecCtx->pix_fmt, //enum PixelFormat srcFormat, + d->width, //int dstW, + d->height, //int dstH, + d->pix_fmt, //enum PixelFormat dstFormat, + SWS_BICUBIC, //int flags, + NULL, //SwsFilter *srcFilter, + NULL, //SwsFilter *dstFilter, + NULL //const double *param + ); + int v_scale_result = sws_scale( + v_sws_ctx, + frame->data, + frame->linesize, + 0, + codecCtx->height, + d->picture.data, + d->picture.linesize + ); + Q_UNUSED(v_scale_result); + sws_freeContext(v_sws_ctx); + if (frame->interlaced_frame) + avpicture_deinterlace(&d->picture, &d->picture, d->pix_fmt, d->width, d->height); + return d->data; +} + + +QSize VideoRenderer::videoSize() const +{ + Q_D(const VideoRenderer); + return QSize(d->width, d->width); +} + +int VideoRenderer::videoWidth() const +{ + Q_D(const VideoRenderer); + return d->width; +} + +int VideoRenderer::videoHeight() const +{ + Q_D(const VideoRenderer); + return d->width; +} + +} diff --git a/src/WidgetRenderer.cpp b/src/WidgetRenderer.cpp new file mode 100644 index 000000000..173581edc --- /dev/null +++ b/src/WidgetRenderer.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#if CONFIG_EZX +#include +#endif //CONFIG_EZX + +namespace QtAV { +WidgetRenderer::WidgetRenderer(QWidget *parent) : + QWidget(parent),ImageRenderer() +{ +#if CONFIG_EZX + QWallpaper::setAppWallpaperMode(QWallpaper::Off); +#endif + action = GestureMove; +} + +WidgetRenderer::~WidgetRenderer() +{ + +} + +int WidgetRenderer::write(const QByteArray &data) +{ + ImageRenderer::write(data); + +#if CONFIG_EZX + QPixmap pix; + pix.convertFromImage(image); + //QPainter v_p(&pix); +#else + //QPainter v_p(&image); +#endif //CONFIG_EZX + +#if CONFIG_EZX + bitBlt(this, QPoint(), &pix); +#else + update(); +#endif + return data.size(); +} + +void WidgetRenderer::resizeEvent(QResizeEvent *e) +{ + resizeVideo(e->size()); +} + +void WidgetRenderer::mousePressEvent(QMouseEvent *e) +{ + gPos = e->globalPos(); + pos = e->pos(); +} + +void WidgetRenderer::mouseMoveEvent(QMouseEvent *e) +{ + int dx = e->globalPos().x() - gPos.x(); + int dy = e->globalPos().y() - gPos.y(); + gPos = e->globalPos(); + + static int x = mapToGlobal(QPoint()).x(); + static int y = mapToGlobal(QPoint()).y(); + static int w = width(); + static int h = height(); + switch (action) { + case GestureMove: + x += dx; + y += dy; + move(x, y); + break; + case GestureResize: + if(pos.x()w/2) { + w += dx; + } + if(pos.y()h/2) { + h += dy; + } + setGeometry(x, y, w, h); + break; + } +#if CONFIG_EZX + repaint(false); +#else + repaint(); +#endif +} + +void WidgetRenderer::mouseDoubleClickEvent(QMouseEvent *) +{ + if (action == GestureMove) + action = GestureResize; + else + action = GestureMove; +} + +#if !CONFIG_EZX +void WidgetRenderer::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.drawImage(QPoint(), image); +} +#endif + +} diff --git a/src/libQtAV.pri b/src/libQtAV.pri new file mode 100644 index 000000000..5580bdda0 --- /dev/null +++ b/src/libQtAV.pri @@ -0,0 +1,156 @@ +# qmake library building template pri file +# Copyright (C) 2011 Wang Bin +# Shanghai, China. +# +# 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; either version 2 of the License, or +# (at your option) any later version. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +############################## HOW TO ################################## +# Suppose the library name is XX +# Usually what you need to change are: staticlink, LIB_VERSION, NAME and DLLDESTDIR. +# And rename xx-buildlib and LIBXX_PRI_INCLUDED +# the contents of libXX.pro is: +# TEMPLATE = lib +# QT -= gui +# CONFIG *= xx-buildlib +# PROJECTROOT = $$PWD/.. +# STATICLINK = 1 #or 0 +# include(libXX.pri) +# include($${PROJECTROOT}/common.pri) +# HEADERS = ... +# SOURCES = ... +# ... +# the content of other pro using this library is: +# TEMPLATE = app +# PROJECTROOT = $$PWD/.. +# STATICLINK = 1 #or 0 +# include(dir_of_XX/libXX.pri) +# include($${PROJECTROOT}/common.pri) +# HEADERS = ... +# SOURCES = ... +# + +NAME = QtAV +!isEmpty(LIB$$upper($$NAME)_PRI_INCLUDED):error("lib$${NAME}.pri already included"), unset(NAME) +#!isEmpty(LIBQTAV_PRI_INCLUDED):error("libQtAV.pri already included") +eval(LIB$$upper($$NAME)_PRI_INCLUDED = 1) + +LIB_VERSION = 1.0.0 #0.x.y may be wrong for dll +isEmpty(STATICLINK): STATICLINK = 0 #1 or 0. use static lib or not + +TEMPLATE += fakelib +PROJECT_TARGETNAME = $$qtLibraryTarget($$NAME) +TEMPLATE -= fakelib + +isEmpty(PROJECTROOT): PROJECTROOT = $$PWD/.. +include($${PROJECTROOT}/common.pri) +#load($${PROJECTROOT}/common.pri) +CONFIG += depend_includepath #? + +PROJECT_SRCPATH = $$PWD +isEmpty(BUILD_DIR):BUILD_DIR=$$(BUILD_DIR) +isEmpty(BUILD_DIR):BUILD_DIR=$$[BUILD_DIR] +isEmpty(BUILD_DIR):BUILD_DIR=$$OUT_PWD #remove these? +PROJECT_LIBDIR = $$qtLongName($$BUILD_DIR/lib) + +#for system include path +*msvc* { +} else { + QMAKE_CXXFLAGS += -isystem $$PROJECT_SRCPATH/.. +} +INCLUDEPATH *= $$PROJECT_SRCPATH $$PROJECT_SRCPATH/.. $$PROJECT_SRCPATH/QtAV +DEPENDPATH *= $$PROJECT_SRCPATH +QMAKE_LFLAGS_RPATH += #will append to rpath dir + +#eval() ? +#!qtav-buildlib { +!contains(CONFIG, $$lower($$NAME)-buildlib) { + #The following may not need to change + CONFIG *= link_prl + LIBS *= -L$$PROJECT_LIBDIR -l$$qtLibName($$NAME) + isEqual(STATICLINK, 1) { + PRE_TARGETDEPS += $$PROJECT_LIBDIR/$$qtStaticLib($$NAME) + } else { + win32 { + PRE_TARGETDEPS *= $$PROJECT_LIBDIR/$$qtSharedLib($$NAME, $$LIB_VERSION) + } else { + PRE_TARGETDEPS *= $$PROJECT_LIBDIR/$$qtSharedLib($$NAME) +# $$[QT_INSTALL_LIBS] and $$DESTDIR will be auto added to rpath +# Current (sub)project dir is auto added to the first value as prefix. e.g. QMAKE_RPATHDIR = .. ==> -Wl,-rpath,ROOT/.. +# Executable dir search: ld -z origin, g++ -Wl,-R,'$ORIGIN', in makefile -Wl,-R,'$$ORIGIN' +# Working dir search: "." +# TODO: for macx. see qtcreator/src/rpath.pri. search exe dir first(use QMAKE_LFLAGS = '$$RPATH_FLAG' $$QMAKE_LFLAGS) + unix:!macx { + QMAKE_RPATHDIR *= $$PROJECT_LIBDIR:\'\$\$ORIGIN\':\'\$\$ORIGIN/lib\':. + QMAKE_LFLAGS *= -Wl,-z,origin + } + } + } +} else { + #Add your additional configuration first. e.g. + #UINT64_C: C99 math features, need -D__STDC_CONSTANT_MACROS in CXXFLAGS + DEFINES += __STDC_CONSTANT_MACROS + +# win32: LIBS += -lUser32 +# The following may not need to change + + #TEMPLATE = lib + VERSION = $$LIB_VERSION + TARGET = $$PROJECT_TARGETNAME + DESTDIR= $$PROJECT_LIBDIR + + CONFIG *= create_prl # + isEqual(STATICLINK, 1) { + CONFIG -= shared dll ##otherwise the following shared is true, why? + CONFIG *= staticlib + } else { + DEFINES += Q_DLL_LIBRARY #win32-msvc* + CONFIG *= shared #shared includes dll + } + + shared { + !isEqual(DESTDIR, $$BUILD_DIR/bin): DLLDESTDIR = $$BUILD_DIR/bin #copy shared lib there + CONFIG(release, debug|release): !isEmpty(QMAKE_STRIP): QMAKE_POST_LINK = -$$QMAKE_STRIP $$PROJECT_LIBDIR/$$qtSharedLib($$NAME) + + #copy from the pro creator creates. + symbian { + MMP_RULES += EXPORTUNFROZEN + TARGET.UID3 = 0xE4CC8061 + TARGET.CAPABILITY = + TARGET.EPOCALLOWDLLDATA = 1 + addFiles.sources = $$qtSharedLib($$NAME, $$LIB_VERSION) + addFiles.path = !:/sys/bin + DEPLOYMENT += addFiles + } + } + unix:!symbian { + maemo5 { + target.path = /opt/usr/lib + } else { + target.path = /usr/lib + } + INSTALLS += target + } +} +!ezx: LIBS += -L/usr/local/lib +LIBS += -Lextra -lavcodec -lavformat -lavutil -lswscale -lportaudio +win32: LIBS += -lwinmm -lksguid #portaudio + +unix:QMAKE_RPATHDIR += /usr/local/lib + +unset(LIB_VERSION) +unset(PROJECT_SRCPATH) +unset(PROJECT_LIBDIR) +unset(PROJECT_TARGETNAME) + diff --git a/src/libQtAV.pro b/src/libQtAV.pro new file mode 100644 index 000000000..6dd2f1f90 --- /dev/null +++ b/src/libQtAV.pro @@ -0,0 +1,59 @@ +TEMPLATE = lib + +QT += core gui opengl +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG *= qtav-buildlib +#var with '_' can not pass to pri? +STATICLINK = 0 +PROJECTROOT = $$PWD/.. +isEmpty(BUILD_DIR):BUILD_DIR=$$(BUILD_DIR) +isEmpty(BUILD_DIR):BUILD_DIR=$$[BUILD_DIR] +isEmpty(BUILD_DIR):BUILD_IN_SRC = yes +!isEmpty(BUILD_IN_SRC):BUILD_DIR=$$OUT_PWD/../out +include($${PROJECTROOT}/common.pri) +!include(libQtAV.pri): error(could not find libQtAV.pri) + +#src +unix: SOURCES += +else:win32: SOURCES += + +SOURCES += \ + AudioThread.cpp \ + AVThread.cpp \ + AudioDecoder.cpp \ + AudioOutput.cpp \ + AVDecoder.cpp \ + AVDemuxer.cpp \ + AVDemuxThread.cpp \ + GraphicsItemRenderer.cpp \ + ImageRenderer.cpp \ + AVInfo.cpp \ + QAVPacket.cpp \ + QAVPacketQueue.cpp \ + AVPlayer.cpp \ + VideoRenderer.cpp \ + WidgetRenderer.cpp \ + AVOutput.cpp + +HEADERS += QtAV/QtAV_Global.h QtAV/qtav.h \ + QtAV/AVThread.h \ + QtAV/AudioThread.h \ + QtAV/private/AVThread_p.h \ + QtAV/AudioDecoder.h \ + QtAV/AudioOutput.h \ + QtAV/AVDecoder.h \ + QtAV/AVDemuxer.h \ + QtAV/AVDemuxThread.h \ + QtAV/GraphicsItemRenderer.h \ + QtAV/ImageRenderer.h \ + QtAV/AVInfo.h \ + QtAV/QAVPacket.h \ + QtAV/QAVPacketQueue.h \ + QtAV/AVPlayer.h \ + QtAV/VideoRenderer.h \ + QtAV/private/VideoRenderer_p.h \ + QtAV/WidgetRenderer.h \ + QtAV/AVOutput.h + + diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 000000000..1d8a74e43 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,43 @@ +#if CONFIG_EZX +#include +#else +#include +typedef QApplication ZApplication; +#endif //CONFIG_EZX + +#include +#include +#include + +#include +#include +#include +using namespace QtAV; + +int main(int argc, char *argv[]) +{ + ZApplication a(argc, argv); + + AVPlayer player; + if (argc>1) + player.setFile(argv[1]); + else + QMessageBox::warning(0, "Usage", QString("%1 path/of/video").arg(qApp->arguments().at(0))); + + QGraphicsScene s; + s.setSceneRect(0, 0, 800, 600); + QGraphicsView v(&s); + + GraphicsItemRenderer g; + g.resizeVideo(800, 600); + s.addItem(&g); + + WidgetRenderer w; + w.setWindowTitle(argv[1]); + player.setRenderer(&w); + w.resize(400, 300); + w.resizeVideo(w.size()); + w.show(); + player.play(); + return a.exec(); +} diff --git a/test/tst_QtAV.pro b/test/tst_QtAV.pro new file mode 100644 index 000000000..9b8bb8462 --- /dev/null +++ b/test/tst_QtAV.pro @@ -0,0 +1,20 @@ +TEMPLATE = app + + +TARGET = tst_qtav +STATICLINK = 0 +PROJECTROOT = $$PWD/.. +isEmpty(BUILD_DIR):BUILD_DIR=$$(BUILD_DIR) +isEmpty(BUILD_DIR):BUILD_DIR=$$[BUILD_DIR] +isEmpty(BUILD_DIR):BUILD_IN_SRC = yes + +!isEmpty(BUILD_IN_SRC):BUILD_DIR=$$OUT_PWD/../out +include($${PROJECTROOT}/common.pri) +include($$PROJECTROOT/src/libQtAV.pri) + +#win32:LIBS += -lUser32 + +SOURCES += main.cpp +HEADERS += + +