/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * 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 "AudioDecoder.h" #include "AudioResampler.h" #include "Packet.h" #include "AVDecoder_p.h" #include "AVCompat.h" #include "mkid.h" #include "factory.h" #include "version.h" #include "Logger.h" namespace FAV { class AudioDecoderFFmpegPrivate; class AudioDecoderFFmpeg : public AudioDecoder { Q_OBJECT Q_DISABLE_COPY(AudioDecoderFFmpeg) DPTR_DECLARE_PRIVATE(AudioDecoderFFmpeg) Q_PROPERTY(QString codecName READ codecName WRITE setCodecName NOTIFY codecNameChanged) public: AudioDecoderFFmpeg(); AudioDecoderId id() const Q_DECL_OVERRIDE Q_DECL_FINAL; virtual QString description() const Q_DECL_OVERRIDE Q_DECL_FINAL { const int patch = QTAV_VERSION_PATCH(avcodec_version()); return QStringLiteral("%1 avcodec %2.%3.%4") .arg(patch>=100?QStringLiteral("FFmpeg"):QStringLiteral("Libav")) .arg(QTAV_VERSION_MAJOR(avcodec_version())).arg(QTAV_VERSION_MINOR(avcodec_version())).arg(patch); } #if (PLAY_SYNC_FIX2) virtual bool send(const Packet* packet) Q_DECL_OVERRIDE Q_DECL_FINAL; virtual bool receive() Q_DECL_OVERRIDE Q_DECL_FINAL; #endif // PLAY_SYNC_FIX2 bool decode(const Packet& packet) Q_DECL_OVERRIDE Q_DECL_FINAL; AudioFrame frame() Q_DECL_OVERRIDE Q_DECL_FINAL; Q_SIGNALS: void codecNameChanged() Q_DECL_OVERRIDE Q_DECL_FINAL; }; AudioDecoderId AudioDecoderId_FFmpeg = mkid::id32base36_6<'F','F','m','p','e','g'>::value; FACTORY_REGISTER(AudioDecoder, FFmpeg, "FFmpeg") class AudioDecoderFFmpegPrivate Q_DECL_FINAL: public AudioDecoderPrivate { public: AudioDecoderFFmpegPrivate() : AudioDecoderPrivate() , frame(av_frame_alloc()) { avcodec_register_all(); } ~AudioDecoderFFmpegPrivate() { if (frame) { av_frame_free(&frame); frame = 0; } } AVFrame *frame; //set once and not change }; AudioDecoderId AudioDecoderFFmpeg::id() const { return AudioDecoderId_FFmpeg; } AudioDecoderFFmpeg::AudioDecoderFFmpeg() : AudioDecoder(*new AudioDecoderFFmpegPrivate()) { } #if (PLAY_SYNC_FIX2) bool AudioDecoderFFmpeg::send(const Packet* packet) { return true; } bool AudioDecoderFFmpeg::receive() { return true; } #endif // PLAY_SYNC_FIX2 bool AudioDecoderFFmpeg::decode(const Packet &packet) { if (!isAvailable()) return false; DPTR_D(AudioDecoderFFmpeg); d.decoded.clear(); int got_frame_ptr = 0; int ret = 0; if (packet.isEOF()) { AVPacket eofpkt; av_init_packet(&eofpkt); eofpkt.data = NULL; eofpkt.size = 0; ret = avcodec_decode_audio4(d.codec_ctx, d.frame, &got_frame_ptr, &eofpkt); } else { // const AVPacket*: ffmpeg >= 1.0. no libav ret = avcodec_decode_audio4(d.codec_ctx, d.frame, &got_frame_ptr, (AVPacket*)packet.asAVPacket()); } d.undecoded_size = qMin(packet.data.size() - ret, packet.data.size()); if (ret == AVERROR(EAGAIN)) { return false; } if (ret < 0) { qWarning("[AudioDecoder] %s", av_err2str(ret)); return false; } if (!got_frame_ptr) { qWarning("[AudioDecoder] got_frame_ptr=false. decoded: %d, un: %d", ret, d.undecoded_size); return !packet.isEOF(); } #if USE_AUDIO_FRAME return true; #endif d.resampler->setInSampesPerChannel(d.frame->nb_samples); if (!d.resampler->convert((const quint8**)d.frame->extended_data)) { return false; } d.decoded = d.resampler->outData(); return true; return !d.decoded.isEmpty(); } AudioFrame AudioDecoderFFmpeg::frame() { DPTR_D(AudioDecoderFFmpeg); AudioFormat fmt; fmt.setSampleFormatFFmpeg(d.frame->format); fmt.setChannelLayoutFFmpeg(d.frame->channel_layout); fmt.setSampleRate(d.frame->sample_rate); if (!fmt.isValid()) {// need more data to decode to get a frame return AudioFrame(); } AudioFrame f(fmt); //av_frame_get_pkt_duration ffmpeg f.setBits(d.frame->extended_data); // TODO: ref f.setBytesPerLine(d.frame->linesize[0], 0); // for correct alignment f.setSamplesPerChannel(d.frame->nb_samples); // TODO: ffplay check AVFrame.pts, pkt_pts, last_pts+nb_samples. move to AudioFrame::from(AVFrame*) //f.setTimestamp((double)d.frame->pkt_pts/1000.0); f.setPTS((double)d.frame->pkt_pts/1000.0); f.setAudioResampler(d.resampler); // TODO: remove. it's not safe if frame is shared. use a pool or detach if ref >1 return f; } } //namespace FAV #include "AudioDecoderFFmpeg.moc"