/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV (from 2014) 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 "VideoDecoderFFmpegBase.h" #include "Packet.h" #include "Logger.h" namespace FAV { extern ColorSpace colorSpaceFromFFmpeg(AVColorSpace cs); extern ColorRange colorRangeFromFFmpeg(AVColorRange cr); static void SetColorDetailsByFFmpeg(VideoFrame *f, AVFrame* frame, AVCodecContext* codec_ctx) { ColorSpace cs = colorSpaceFromFFmpeg(av_frame_get_colorspace(frame)); if (cs == ColorSpace_Unknown) { cs = colorSpaceFromFFmpeg(codec_ctx->colorspace); } f->setColorSpace(cs); ColorRange cr = colorRangeFromFFmpeg(av_frame_get_color_range(frame)); if (cr == ColorRange_Unknown) { // check yuvj format. TODO: deprecated, check only for old ffmpeg? const AVPixelFormat pixfmt = ((AVPixelFormat)frame->format) == AV_PIX_FMT_NONE ? ((AVPixelFormat)codec_ctx->pix_fmt) : ((AVPixelFormat)frame->format); switch (pixfmt) { //case QTAV_PIX_FMT_C(YUVJ411P): //not in ffmpeg<2 and libav case QTAV_PIX_FMT_C(YUVJ420P): case QTAV_PIX_FMT_C(YUVJ422P): case QTAV_PIX_FMT_C(YUVJ440P): case QTAV_PIX_FMT_C(YUVJ444P): cr = ColorRange_Full; break; default: break; } } if (cr == ColorRange_Unknown) { cr = colorRangeFromFFmpeg(codec_ctx->color_range); if (cr == ColorRange_Unknown) { if (f->format().isXYZ()) { cr = ColorRange_Full; cs = ColorSpace_XYZ; // not here } else if (!f->format().isRGB()) { // qDebug("prefer limited yuv range"); cr = ColorRange_Limited; } } } f->setColorRange(cr); } void VideoDecoderFFmpegBasePrivate::updateColorDetails(VideoFrame *f) { if (f->format().pixelFormatFFmpeg() == frame->format) { SetColorDetailsByFFmpeg(f, frame, codec_ctx); return; } // hw decoder output frame may have a different format, e.g. gl interop frame may have rgb format for rendering(stored as yuv) const bool rgb_frame = f->format().isRGB(); if (rgb_frame) { //qDebug("rgb output frame (yuv coded)"); f->setColorSpace(f->format().isPlanar() ? ColorSpace_GBR : ColorSpace_RGB); f->setColorRange(ColorRange_Full); return; } // yuv frame. When happens? const bool rgb_coded = (av_pix_fmt_desc_get(codec_ctx->pix_fmt)->flags & AV_PIX_FMT_FLAG_RGB) == AV_PIX_FMT_FLAG_RGB; if (rgb_coded) { if (f->width() >= 1280 && f->height() >= 576) { f->setColorSpace(ColorSpace_BT709); } else { f->setColorSpace(ColorSpace_BT601); } f->setColorRange(ColorRange_Limited); } else { SetColorDetailsByFFmpeg(f, frame, codec_ctx); } } qreal VideoDecoderFFmpegBasePrivate::getDAR(AVFrame *f) { // lavf 54.5.100 av_guess_sample_aspect_ratio: stream.sar > frame.sar qreal dar = 0; if (f->height > 0) { dar = (qreal)f->width/(qreal)f->height; } // prefer sar from AVFrame if sar != 1/1 if (f->sample_aspect_ratio.num > 1) { dar *= av_q2d(f->sample_aspect_ratio); } else if (codec_ctx && codec_ctx->sample_aspect_ratio.num > 1) // skip 1/1 { dar *= av_q2d(codec_ctx->sample_aspect_ratio); } return dar; } VideoDecoderFFmpegBase::VideoDecoderFFmpegBase(VideoDecoderFFmpegBasePrivate &d): VideoDecoder(d) { } #if (PLAY_SYNC_FIX2) bool VideoDecoderFFmpegBase::send(const Packet* packet) { if (!isAvailable()) { return false; } DPTR_D(VideoDecoderFFmpegBase); int ret = 0; if(packet == NULL) { ret = avcodec_send_packet(d.codec_ctx,NULL); } else { if (packet->isEOF()) { AVPacket eofpkt; av_init_packet(&eofpkt); eofpkt.data = NULL; eofpkt.size = 0; ret = avcodec_send_packet(d.codec_ctx,&eofpkt); //qInfo() << "SEND EOF PACKET" << ret << __FUNCTION__; } else { AVPacket* avpacket = (AVPacket*)packet->asAVPacket(); ret = avcodec_send_packet(d.codec_ctx,avpacket); } } return (ret == 0); } bool VideoDecoderFFmpegBase::receive() { DPTR_D(VideoDecoderFFmpegBase); int ret = avcodec_receive_frame(d.codec_ctx,d.frame); if (!d.codec_ctx->width || !d.codec_ctx->height) { return false; } d.width = d.frame->width; d.height = d.frame->height; // if(ret != 0) { // qInfo() << "RECEVIE ERROR" << ret << __FUNCTION__; // } return (ret == 0); } #endif // PLAY_SYNC_FIX2 bool VideoDecoderFFmpegBase::decode(const Packet &packet) { if (!isAvailable()) { return false; } DPTR_D(VideoDecoderFFmpegBase); // some decoders might need other fields like flags&AV_PKT_FLAG_KEY // const AVPacket*: ffmpeg >= 1.0. no libav 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_video2(d.codec_ctx, d.frame, &got_frame_ptr, &eofpkt); } else { AVPacket* avpacket = (AVPacket*)packet.asAVPacket(); ret = avcodec_decode_video2(d.codec_ctx, d.frame, &got_frame_ptr, avpacket); if(got_frame_ptr == 0) { // av_free_packet(avpacket); } #if (DEBUG_PLAYER_AVPACKET) qInfo() << "---------------------------------------------------" << "\ngot_frame_ptr:" << got_frame_ptr << "\nstream:" << avpacket->stream_index << "\ndts:" << avpacket->dts << "\npts:" << avpacket->pts << "\nduration:" << avpacket->duration; #endif } //qDebug("pic_type=%c", av_get_picture_type_char(d.frame->pict_type)); d.undecoded_size = qMin(packet.data.size() - ret, packet.data.size()); if (ret < 0) { // qWarning("[VideoDecoderFFmpegBase] %s", av_err2str(ret)); return false; } if (!got_frame_ptr) { #if (DEBUG_PLAYER_AVPACKET) qInfo() << "packet.data.size()" << packet.data.size() << "ret" << ret << "undecoded_size:" << d.undecoded_size; #endif #if (SKIP_FIRST_CORRUPT_FRAME) return false; #else qWarning("no frame could be decompressed: %s %d/%d", av_err2str(ret), d.undecoded_size, packet.data.size()); return !packet.isEOF(); #endif } if (!d.codec_ctx->width || !d.codec_ctx->height) { // qWarning("codec width height 0"); return false; } d.width = d.frame->width; // TODO: remove? used in hwdec d.height = d.frame->height; //avcodec_align_dimensions2(d.codec_ctx, &d.width_align, &d.height_align, aligns); return true; } } //namespace FAV