From 0cd8751fce0d482e058898d177b9a2863bc28726 Mon Sep 17 00:00:00 2001 From: beholder Date: Wed, 28 Mar 2018 01:33:17 +0300 Subject: [PATCH 1/6] stop using weird conditional 'auto-adjusting' logic for yAxis, for now use yMin/yMax froup group definition --- Source/GUI/Plot.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Source/GUI/Plot.cpp b/Source/GUI/Plot.cpp index 43494c7cd..21558ec67 100644 --- a/Source/GUI/Plot.cpp +++ b/Source/GUI/Plot.cpp @@ -348,11 +348,8 @@ void Plot::initYAxis() CommonStats* stat = stats( streamPos() ); const struct per_group& group = PerStreamType[plotType].PerGroup[plotGroup]; - double yMin = stat->y_Min[plotGroup]; - double yMax = stat->y_Max[plotGroup]; - - if ( ( group.Min != group.Max ) && ( yMax - yMin >= ( group.Max - group.Min) / 2 ) ) - yMax = group.Max; + auto yMin = group.Min; + auto yMax = group.Max; if ( yMin != yMax ) { From 82715418c98bcb1823267f946d439b1c41f61476 Mon Sep 17 00:00:00 2001 From: beholder Date: Wed, 28 Mar 2018 03:39:54 +0300 Subject: [PATCH 2/6] do not consider '0' as valid 'bitsPerRawSample' anymore. return 8 by default --- Source/Core/FileInformation.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/Core/FileInformation.cpp b/Source/Core/FileInformation.cpp index 21fa1bc8b..5cbcfb048 100644 --- a/Source/Core/FileInformation.cpp +++ b/Source/Core/FileInformation.cpp @@ -858,13 +858,16 @@ int FileInformation::BitsPerRawSample() const if(streamBitsPerRawSample != 0) return streamBitsPerRawSample; - if(Glue) + if(Glue && Glue->BitsPerRawSample_Get() != 0) return Glue->BitsPerRawSample_Get(); - if(ReferenceStat()) - return FFmpeg_Glue::guessBitsPerRawSampleFromFormat(*ReferenceStat()->pix_fmt); + if(ReferenceStat()) { + auto guessedBitsPerRawSample = FFmpeg_Glue::guessBitsPerRawSampleFromFormat(*ReferenceStat()->pix_fmt); + if(guessedBitsPerRawSample != 0) + return guessedBitsPerRawSample; + } - return 0; + return 8; } FileInformation::SignalServerCheckUploadedStatus FileInformation::signalServerCheckUploadedStatus() const From 12ba65e97017b4ecae759401059c73de83db4a60 Mon Sep 17 00:00:00 2001 From: beholder Date: Wed, 28 Mar 2018 03:42:59 +0300 Subject: [PATCH 3/6] scriptable min/max for y-axis syntax supported: "bitsPerRawSample < 10" - expression "function() { return bitsPerRawSample * 20; }" - function "3" - just a number nullptr - automatic picking min/max --- Source/Core/AudioCore.cpp | 10 ++++---- Source/Core/Core.h | 9 +++---- Source/Core/VideoCore.cpp | 52 +++++++++++++++++++-------------------- Source/GUI/Plot.cpp | 35 ++++++++++++++++++++++++-- Source/GUI/Plot.h | 3 +++ Source/GUI/Plots.cpp | 21 +--------------- Source/GUI/Plots.h | 1 - 7 files changed, 71 insertions(+), 60 deletions(-) diff --git a/Source/Core/AudioCore.cpp b/Source/Core/AudioCore.cpp index 3363991ba..7ef7b1b90 100644 --- a/Source/Core/AudioCore.cpp +++ b/Source/Core/AudioCore.cpp @@ -12,7 +12,7 @@ struct per_group AudioPerGroup [Group_AudioMax]= { //R128 { - Item_R128M, 1, -70, 0, 3, "R.128", false, + Item_R128M, 1, "-70", "0", 3, "R.128", false, "R 128 refers to a European Broadcasting Union (EBU) specification\n" "document governing several loudness parameters, including momentary,\n" "integrated, and short-term loudness. QCTools specifically examines momentary\n" @@ -23,7 +23,7 @@ struct per_group AudioPerGroup [Group_AudioMax]= }, //aphasemeter { - Item_aphasemeter, 1, -1, 1, 3, "Audio Phase", false, + Item_aphasemeter, 1, "-1", "1", 3, "Audio Phase", false, "The audio phase value represents the mean phase of current audio frame. Value is\n" "in range [-1, 1]. The -1 means left and right channels are completely out of\n" "phase and 1 means channels are in phase.", @@ -31,7 +31,7 @@ struct per_group AudioPerGroup [Group_AudioMax]= }, //astats levels { - Item_DC_offset, 3, -1, 1, 3, "Levels", true, + Item_DC_offset, 3, "-1", "1", 3, "Levels", true, "For selected audio tracks this graph plots the DC offset (mean\n" "amplitude displacement from zero), minimal sample level, and \n" "maximum sample level. Note that this value is plotted per audio\n" @@ -40,7 +40,7 @@ struct per_group AudioPerGroup [Group_AudioMax]= }, //astats diff { - Item_Min_difference, 3, 0, 1, 3, "Aud Diffs", false, + Item_Min_difference, 3, "0", "1", 3, "Aud Diffs", false, "For selected audio tracks this graph plots the minimal difference\n" "between two consecutive samples, maximal difference between two\n" "consecutive samples. and the mean difference between two consecutive\n" @@ -52,7 +52,7 @@ struct per_group AudioPerGroup [Group_AudioMax]= }, //astats rms { - Item_Peak_level, 3, -70, 0, 3, "RMS", false, + Item_Peak_level, 3, "-70", "0", 3, "RMS", false, "For selected audio tracks this graph plots the Standard peak and RMS\n" "level measured in dBFS and the Peak and trough values for RMS level\n" "measured over a short window. Note that this value is plotted per\n" diff --git a/Source/Core/Core.h b/Source/Core/Core.h index 16edc83b2..1424c0582 100755 --- a/Source/Core/Core.h +++ b/Source/Core/Core.h @@ -41,18 +41,15 @@ struct per_group { const std::size_t Start; //Item const std::size_t Count; - const double Min; - const double Max; + const char* MinFormula; + const char* MaxFormula; const double StepsCount; const char* Name; const bool CheckedByDefault; const char* Description; activefilter ActiveFilterGroup; - - void setMax(double value) { - (const_cast (Max)) = value; - } }; + struct per_item { const std::size_t Group1; //Group diff --git a/Source/Core/VideoCore.cpp b/Source/Core/VideoCore.cpp index 6c4e16dfc..a77aacbdf 100644 --- a/Source/Core/VideoCore.cpp +++ b/Source/Core/VideoCore.cpp @@ -12,7 +12,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= { //Y { - Item_YMIN, 5, 0, 255, 3, "Y", true, + Item_YMIN, 5, "function() { return bitsPerRawSample * 20; }", nullptr, 3, "Y", true, "For all samples of the Y plane, plot the MAXimum, MIVimum, AVeraGe,\n" "LOW (10th percentile), and HIGH (90th percentile) values for each frame.\n" "The Y plane provides information about video brightness or luminance.", @@ -20,7 +20,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //U { - Item_UMIN, 5, 0, 255, 3, "U", true, + Item_UMIN, 5, "0", "255", 3, "U", true, "For all samples of the U plane, plot the MAXimum, MIVimum, AVeraGe,\n" "LOW (10th percentile), and HIGH (90th percentile) values for each frame.\n" "The U plane (along with V) provides information about video color.", @@ -28,7 +28,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //V { - Item_VMIN, 5, 0, 255, 3, "V", true, + Item_VMIN, 5, "0", "bitsPerRawSample < 10", 3, "V", true, "For all samples of the V plane, plot the MAXimum, MIVimum, AVeraGe,\n" "LOW (10th percentile), and HIGH (90th percentile) values for each frame.\n" "The V plane (along with U) provides information about video color.", @@ -36,7 +36,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //YDiff { - Item_YDIF, 1, 0, 255, 3, "YDiff", false, + Item_YDIF, 1, "0", "255", 3, "YDiff", false, "YDIF plots the amount of differences between the Y plane of the current\n" "frame and the preceding one. It indicates the extent of visual change\n" "from one frame to the next.", @@ -44,7 +44,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //UDiff { - Item_UDIF, 1, 0, 255, 3, "UDiff", false, + Item_UDIF, 1, "0", "255", 3, "UDiff", false, "UDIF plots the amount of differences between the U plane of the current\n" "frame and the preceding one. It indicates the extent of visual change\n" "from one frame to the next.", @@ -52,7 +52,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //VDiff { - Item_VDIF, 1, 0, 255, 3, "VDiff", false, + Item_VDIF, 1, "0", "255", 3, "VDiff", false, "VDIF plots the amount of differences between the V plane of the current\n" "frame and the preceding one. It indicates the extent of visual change\n" "from one frame to the next.", @@ -60,13 +60,13 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //Diffs { - Item_VDIF, 3, 0, 255, 3, "Diffs", true, + Item_VDIF, 3, "0", "255", 3, "Diffs", true, "Plots YDIF, UDIF, and VDIF all together.", ActiveFilter_Video_signalstats, }, //Sat { - Item_SATMIN, 5, 0, 180, 4, "Sat", true, + Item_SATMIN, 5, "0", "180", 4, "Sat", true, "This filter does the equivalent of plotting all pixels in a vectorscope\n" "and measuring the distance from that plotted points to the center of the\n" "vectorscope. The MAXimum, MIVimum, AVeraGe, LOW (10th percentile), and\n" @@ -82,7 +82,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //Hue { - Item_HUEMED, 2, 0, 360, 4, "Hue", false, + Item_HUEMED, 2, "0", "360", 4, "Hue", false, "The hue filter expresses the average color hue in degrees from 0 to 360.\n" "Degrees are measured clockwise starting from 0 at the bottom side of a\n" "vectorscope. Skin tone is 147 degrees. Hue is plotted via median and average.", @@ -90,7 +90,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //TOUT { - Item_TOUT, 1, 0, 0.9, 4, "TOUT", false, + Item_TOUT, 1, "0", "0.9", 4, "TOUT", false, "Pixels are labeled as temporal outliers (TOUT) if they are unlike the corresponding\n" "pixels of the previous and subsequent frames. Peaks in TOUT can show areas with\n" "skew or tracking issues which cause white speckle in the video. It can also\n" @@ -99,7 +99,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //VREP { - Item_VREP, 1, 0, 1, 4, "VREP", false, + Item_VREP, 1, "0", "1", 4, "VREP", false, "Vertical Line Repetitions, or the VREP filter, is useful in detecting\n" "the use of a dropout compensator in the digitization of analog video. VREP\n" "plots the number of repeated horizontal lines which is untypical for an analog\n" @@ -109,7 +109,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //BRNG { - Item_BRNG, 1, 0, 1, 4, "BRNG", false, + Item_BRNG, 1, "0", "1", 4, "BRNG", false, "The BRNG filter is one that identifies the amount of pixels that fall\n" "outside the standard video broadcast range of 16-235 pixels for Y or\n" "16-240 for U and V.", @@ -117,7 +117,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //CropW { - Item_Crop_x1, 2, 0, 0, 4, "CropW", false, + Item_Crop_x1, 2, "0", "0", 4, "CropW", false, "CropW plots the number of columns of pixels would could be safely removed\n" "from the left or right side of the image without removing any non-black\n" "pixels. It would detect video frames with pillarboxing.", @@ -125,7 +125,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //CropH { - Item_Crop_y1, 2, 0, 0, 4, "CropH", false, + Item_Crop_y1, 2, "0", "0", 4, "CropH", false, "CropW plots the number of row of pixels would could be safely removed\n" "from the top or bottom side of the image without removing any non-black\n" "pixels. It would detect video frames with letterboxing.", @@ -133,14 +133,14 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //CropF { - Item_Crop_w, 2, 0, 0, 4, "CropF", false, + Item_Crop_w, 2, "0", "0", 4, "CropF", false, "Presents the total number of rows (Crop Height) and columns (Crop Width)\n" "which could be removed from the edges to only remove black pixels.", ActiveFilter_Video_cropdetect, }, //MSEf { - Item_MSE_v, 3, 0, 0, 4, "MSEf", false, + Item_MSE_v, 3, "0", "0", 4, "MSEf", false, "Plots an assessment of visual difference of field 1 versus field 2 via\n" "Mean Square Error for each plane (Y, U, and V). Higher values may be\n" "indicative of differences between the images of field 1 and field 2 as\n" @@ -149,7 +149,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //PSNRf { - Item_PSNR_v, 3, 0, 0, 4, "PSNRf", false, + Item_PSNR_v, 3, "0", "0", 4, "PSNRf", false, "Plots an assessment of visual difference of field 1 versus field 2 via\n" "Peak Signal to Noise Ratio for each plane (Y, U, and V). Lower values may\n" "be indicative of differences between the images of field 1 and field 2 as\n" @@ -158,7 +158,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //SSIMf { - Item_SSIM_Y, 4, 0, 0, 4, "SSIMf", false, + Item_SSIM_Y, 4, "0", "0", 4, "SSIMf", false, "Plots an assessment of visual difference of field 1 versus field 2 via\n" "SSIM (Structural SImilarity Metric) for each plane (Y, U, and V). Lower values may\n" "be indicative of differences between the images of field 1 and field 2 as\n" @@ -167,7 +167,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //idet.single { - Item_IDET_S_BFF, 4, 0, 2, 4, "idet S", false, + Item_IDET_S_BFF, 4, "0", "2", 4, "idet S", false, "Plots an interpretation of the interlacement pattern of the visual image.\n" "This version uses single frame detection which considers only immediately\n" "adjacent frames when classifying each frame. Each frame's classification is\n" @@ -176,7 +176,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //idet.multiple { - Item_IDET_M_BFF, 4, 0, 2, 4, "idet M", false, + Item_IDET_M_BFF, 4, "0", "2", 4, "idet M", false, "Plots an interpretation of the interlacement pattern of the visual image.\n" "This version uses multiple frame detection which incorporates the classification\n" "history of previous frames. Each frame's classification is plotted with a\n" @@ -185,7 +185,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //idet.repeat { - Item_IDET_R_B, 3, 0, 2, 4, "idet R", false, + Item_IDET_R_B, 3, "0", "2", 4, "idet R", false, "Plots an interpretation of the interlacement pattern of the visual image.\n" "This plot shows fields that are repeated between adjacent frames (a sign\n" "of telecine). Each frame's classification is plotted with a half-life of 1.", @@ -193,7 +193,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //deflicker { - Item_DEFL, 1, -1, 1, 4, "flicker", false, + Item_DEFL, 1, "-1", "1", 4, "flicker", false, "Plots a quantification of the temporal frame luminance variation by an\n" "arithmetric mean of sets of 5 frames. The plotted value shows the relative\n" "change in luminance that would be used by libavfilter's deflicker filter.", @@ -201,7 +201,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //entropy { - Item_ENTR_Y, 3, 0, 1, 4, "entr", true, + Item_ENTR_Y, 3, "0", "1", 4, "entr", true, "Entropy\n" "Plots the graylevel entropy of the histogram of the color channels.\n" "A color channel with only a single shade will have entropy of 0,\n" @@ -210,7 +210,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //entropy-diff { - Item_ENTR_Y_D, 3, 0, 1, 4, "entr diff", true, + Item_ENTR_Y_D, 3, "0", "1", 4, "entr diff", true, "Entropy Difference\n" "Plots the frame-to-frame difference in the graylevel entropy\n" "of the histogram of the color channels. Incoherancy in plotted\n" @@ -222,14 +222,14 @@ struct per_group VideoPerGroup [Group_VideoMax]= }, //Item_pkt_duration_time { - Item_pkt_duration_time, 1, 0, 2, 4, "pkt_duration_time", false, + Item_pkt_duration_time, 1, "0", "2", 4, "pkt_duration_time", false, "Plots the duration in seconds of each frame. If the file is of constant\n" "frame rate than this should be a straight line.", ActiveFilter_Video_signalstats, }, //Item_pkt_size { - Item_pkt_size, 1, 0, 0, 4, "pkt_size", false, + Item_pkt_size, 1, "0", "0", 4, "pkt_size", false, "Plots the size in bytes of each frame. If the file is of uncompressed\n" "frame rate than this should be a straight line, but a lossless or\n" "lossy file should show the variety of frame sizes.", diff --git a/Source/GUI/Plot.cpp b/Source/GUI/Plot.cpp index 21558ec67..1cc50aea2 100644 --- a/Source/GUI/Plot.cpp +++ b/Source/GUI/Plot.cpp @@ -348,8 +348,25 @@ void Plot::initYAxis() CommonStats* stat = stats( streamPos() ); const struct per_group& group = PerStreamType[plotType].PerGroup[plotGroup]; - auto yMin = group.Min; - auto yMax = group.Max; + auto yMin = 0; + if(m_minValue.isNull() || m_minValue.isError() || m_minValue.isUndefined()) { + yMin = stat->y_Min[plotGroup]; // auto-select min + } else { + if(m_minValue.isNumber()) + yMin = m_minValue.toNumber(); + else if(m_minValue.isCallable()) + yMin = m_minValue.call().toNumber(); + } + + auto yMax = 0; + if(m_maxValue.isNull() || m_maxValue.isError() || m_maxValue.isUndefined()) { + yMax = stat->y_Max[plotGroup]; // auto-select min + } else { + if(m_maxValue.isNumber()) + yMax = m_maxValue.toNumber(); + else if(m_maxValue.isCallable()) + yMax = m_maxValue.call().toNumber(); + } if ( yMin != yMax ) { @@ -442,6 +459,20 @@ Plot::Plot( size_t streamPos, size_t Type, size_t Group, const FileInformation* int s = m_charBackground.saturation(); int v = m_charBackground.value(); + const struct per_group& group = PerStreamType[m_type].PerGroup[m_group]; + auto bitsPerRawSample = m_fileInformation->BitsPerRawSample(); + + m_engine.globalObject().setProperty("bitsPerRawSample", bitsPerRawSample); + m_engine.globalObject().setProperty("two_pow_bitsPerRawSample_minus_one", (1 << bitsPerRawSample) - 1); + m_engine.globalObject().setProperty("sqrt_pow_bitsPerRawSample_2", sqrt(2) * (1 << bitsPerRawSample) / 2); + + m_minValue = m_engine.evaluate(group.MinFormula); + bool isValid = m_minValue.isCallable(); + isValid = m_minValue.isError(); + isValid = m_minValue.isUndefined(); + + m_maxValue = m_engine.evaluate(group.MaxFormula); + m_barchartBackground = QColor::fromHsv(h + 60, s, v); setAutoReplot( false ); diff --git a/Source/GUI/Plot.h b/Source/GUI/Plot.h index 6900394fa..e77a0e63f 100644 --- a/Source/GUI/Plot.h +++ b/Source/GUI/Plot.h @@ -473,6 +473,9 @@ private Q_SLOTS: const QwtPlotCurve* curve( int index ) const; QColor curveColor( int index ) const; + QJSValue m_maxValue; + QJSValue m_minValue; + QJSEngine m_engine; const size_t m_streamPos; const size_t m_type; const size_t m_group; diff --git a/Source/GUI/Plots.cpp b/Source/GUI/Plots.cpp index 197cbdba0..54d566878 100755 --- a/Source/GUI/Plots.cpp +++ b/Source/GUI/Plots.cpp @@ -154,7 +154,7 @@ Plots::Plots( QWidget *parent, FileInformation* fileInformation ) : { if (m_fileInfoData->ActiveFilters[PerStreamType[type].PerGroup[group].ActiveFilterGroup]) { - Plot* plot = new Plot( streamPos, type, group, fileInformation, this ); + Plot* plot = new Plot( streamPos, type, group, m_fileInfoData, this ); const size_t plotType = plot->type(); const size_t plotGroup = plot->group(); @@ -164,9 +164,6 @@ Plots::Plots( QWidget *parent, FileInformation* fileInformation ) : plot->addGuidelines(m_fileInfoData->BitsPerRawSample()); - if(type == Type_Video) - adjustGroupMax(group, m_fileInfoData->BitsPerRawSample()); - // we allow to shrink the plot below height of the size hint plot->plotLayout()->setAlignCanvasToScales(false); plot->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Expanding ); @@ -623,22 +620,6 @@ bool Plots::eventFilter( QObject *object, QEvent *event ) return QWidget::eventFilter( object, event ); } -void Plots::adjustGroupMax(int group, int bitsPerRawSample) -{ - int defaultBitsPerRawSample = 8; - if(bitsPerRawSample == 0) - bitsPerRawSample = defaultBitsPerRawSample; - - if(group == Group_Y || group == Group_U || group == Group_V || group == Group_YDiff || group == Group_UDiff || group == Group_VDiff) - { - PerStreamType[Type_Video].GetPerGroup(group)->setMax((1 << bitsPerRawSample) - 1); - } - if(group == Group_Sat) - { - PerStreamType[Type_Video].GetPerGroup(group)->setMax(sqrt(2) * (1 << bitsPerRawSample) / 2); - } -} - void Plots::changeOrder(QList > orderedFilterInfo) { if(orderedFilterInfo.empty()) diff --git a/Source/GUI/Plots.h b/Source/GUI/Plots.h index a777281dd..7db65a325 100755 --- a/Source/GUI/Plots.h +++ b/Source/GUI/Plots.h @@ -100,7 +100,6 @@ class Plots : public QWidget int numFrames() const { return stats()->x_Current_Max; } virtual bool eventFilter( QObject *, QEvent * ); - void adjustGroupMax(int group, int bitsPerRawSample); void changeOrder(QList> filterSelectorsInfo); QJsonObject saveBarchartsProfile(); From 98f4973f5fb65a05d6a13e1a940b8a04140205a7 Mon Sep 17 00:00:00 2001 From: beholder Date: Sat, 31 Mar 2018 19:40:04 +0300 Subject: [PATCH 4/6] remove 'bits_per_raw_sample' from AudioStreamStats because it is already handled in CommonStreamStats --- Source/Core/AudioStreamStats.cpp | 21 ++------------------- Source/Core/AudioStreamStats.h | 4 ---- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/Source/Core/AudioStreamStats.cpp b/Source/Core/AudioStreamStats.cpp index 1168a4ab9..793581a6a 100644 --- a/Source/Core/AudioStreamStats.cpp +++ b/Source/Core/AudioStreamStats.cpp @@ -35,8 +35,7 @@ using namespace tinyxml2; AudioStreamStats::AudioStreamStats(XMLElement *streamElement) : CommonStreamStats(streamElement), sample_rate(0), channels(0), - bits_per_sample(0), - bits_per_raw_sample(0) + bits_per_sample(0) { codec_type = "audio"; @@ -59,10 +58,6 @@ AudioStreamStats::AudioStreamStats(XMLElement *streamElement) : CommonStreamStat const char* bits_per_sample_value = streamElement->Attribute("bits_per_sample"); if(bits_per_sample_value) bits_per_sample = std::stoi(bits_per_sample_value); - - const char* bits_per_raw_sample_value = streamElement->Attribute("bits_per_raw_sample"); - if(bits_per_raw_sample_value) - bits_per_raw_sample = std::stoi(bits_per_raw_sample_value); } AudioStreamStats::AudioStreamStats(AVStream* stream, AVFormatContext *context) : CommonStreamStats(stream), @@ -70,8 +65,7 @@ AudioStreamStats::AudioStreamStats(AVStream* stream, AVFormatContext *context) : sample_rate(stream != NULL ? stream->codecpar->sample_rate : 0), channels(stream != NULL ? stream->codecpar->channels : 0), channel_layout(""), - bits_per_sample(stream != NULL ? av_get_bits_per_sample(stream->codecpar->codec_id) : 0), - bits_per_raw_sample(stream != NULL ? stream->codecpar->bits_per_raw_sample : 0) + bits_per_sample(stream != NULL ? av_get_bits_per_sample(stream->codecpar->codec_id) : 0) { Q_UNUSED(context); @@ -116,7 +110,6 @@ void AudioStreamStats::writeStreamInfoToXML(QXmlStreamWriter *writer) writer->writeAttribute("time_base", QString::fromStdString(getTime_base())); writer->writeAttribute("start_pts", QString::fromStdString(getStart_pts())); writer->writeAttribute("start_time", QString::fromStdString(getStart_time())); - writer->writeAttribute("bits_per_raw_sample", QString::number(getBits_per_raw_sample())); CommonStreamStats::writeDispositionInfoToXML(writer); CommonStreamStats::writeMetadataToXML(writer); @@ -171,13 +164,3 @@ void AudioStreamStats::setBits_per_sample(int value) { bits_per_sample = value; } - -int AudioStreamStats::getBits_per_raw_sample() const -{ - return bits_per_raw_sample; -} - -void AudioStreamStats::setBits_per_raw_sample(int value) -{ - bits_per_raw_sample = value; -} diff --git a/Source/Core/AudioStreamStats.h b/Source/Core/AudioStreamStats.h index e434b494c..526e95526 100644 --- a/Source/Core/AudioStreamStats.h +++ b/Source/Core/AudioStreamStats.h @@ -41,16 +41,12 @@ class AudioStreamStats : public CommonStreamStats int getBits_per_sample() const; void setBits_per_sample(int value); - int getBits_per_raw_sample() const; - void setBits_per_raw_sample(int value); - private: std::string sample_fmt; int sample_rate; int channels; std::string channel_layout; int bits_per_sample; - int bits_per_raw_sample; }; #endif // AudioStreamStats_H From d48dcf00f104899582c2a927e1251e68c03dd195 Mon Sep 17 00:00:00 2001 From: beholder Date: Sat, 31 Mar 2018 22:43:21 +0300 Subject: [PATCH 5/6] allow selecting wav files via open dialog --- Source/GUI/mainwindow_More.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/GUI/mainwindow_More.cpp b/Source/GUI/mainwindow_More.cpp index ff3279348..bbbdf7202 100755 --- a/Source/GUI/mainwindow_More.cpp +++ b/Source/GUI/mainwindow_More.cpp @@ -46,7 +46,8 @@ void MainWindow::openFile() options = QFileDialog::DontUseNativeDialog; #endif - QStringList List=QFileDialog::getOpenFileNames(this, "Open file", "", "Video files (*.avi *.mkv *.mov *.mxf *.mp4 *.ts *.m2ts);;\ + QStringList List=QFileDialog::getOpenFileNames(this, "Open file", "", "Audio files (*.wav);;\ + Video files (*.avi *.mkv *.mov *.mxf *.mp4 *.ts *.m2ts);;\ Statistic files (*.qctools.xml *.qctools.xml.gz *.xml.gz *.xml);;\ Statistic files with thumbnails (*.qctools.mkv);;\ All (*.*)", 0, options); From 1f152222d2c30c94174e64a8dcbd166b0a3d7ccc Mon Sep 17 00:00:00 2001 From: beholder Date: Sat, 31 Mar 2018 22:45:14 +0300 Subject: [PATCH 6/6] calculate 'bitsPerRawSample' for audio streams, add 'audio_min', 'audio_max' --- Source/Core/AudioStreamStats.cpp | 30 ++++++++++----- Source/Core/AudioStreamStats.h | 9 +++-- Source/Core/CommonStreamStats.cpp | 5 +++ Source/Core/CommonStreamStats.h | 2 + Source/Core/FFmpeg_Glue.cpp | 51 +++++++++++++++++++++++-- Source/Core/FFmpeg_Glue.h | 7 +++- Source/Core/FileInformation.cpp | 62 +++++++++++++++++++++++++------ Source/Core/FileInformation.h | 4 +- Source/Core/StreamsStats.cpp | 14 ++++++- Source/Core/StreamsStats.h | 3 +- Source/Core/VideoStreamStats.cpp | 2 + Source/GUI/Plot.cpp | 8 +++- 12 files changed, 164 insertions(+), 33 deletions(-) diff --git a/Source/Core/AudioStreamStats.cpp b/Source/Core/AudioStreamStats.cpp index 793581a6a..c7334576c 100644 --- a/Source/Core/AudioStreamStats.cpp +++ b/Source/Core/AudioStreamStats.cpp @@ -38,10 +38,13 @@ AudioStreamStats::AudioStreamStats(XMLElement *streamElement) : CommonStreamStat bits_per_sample(0) { codec_type = "audio"; + stream_type = AVMEDIA_TYPE_AUDIO; const char* sample_fmt_value = streamElement->Attribute("sample_fmt"); - if(sample_fmt_value) - sample_fmt = sample_fmt_value; + if(sample_fmt_value) { + sample_fmt = av_get_sample_fmt(sample_fmt_value); + sample_fmt_string = sample_fmt_value; + } const char* sample_rate_value = streamElement->Attribute("sample_rate"); if(sample_rate_value) @@ -61,7 +64,7 @@ AudioStreamStats::AudioStreamStats(XMLElement *streamElement) : CommonStreamStat } AudioStreamStats::AudioStreamStats(AVStream* stream, AVFormatContext *context) : CommonStreamStats(stream), - sample_fmt(""), + sample_fmt_string(""), sample_rate(stream != NULL ? stream->codecpar->sample_rate : 0), channels(stream != NULL ? stream->codecpar->channels : 0), channel_layout(""), @@ -70,14 +73,16 @@ AudioStreamStats::AudioStreamStats(AVStream* stream, AVFormatContext *context) : Q_UNUSED(context); codec_type = "audio"; + stream_type = AVMEDIA_TYPE_AUDIO; if(stream) { - const char* s = av_get_sample_fmt_name((AVSampleFormat) stream->codecpar->format); + sample_fmt = stream->codecpar->format; + const char* s = av_get_sample_fmt_name((AVSampleFormat) sample_fmt); if (s) - sample_fmt = s; + sample_fmt_string = s; else - sample_fmt = "unknown"; + sample_fmt_string = "unknown"; AVBPrint pbuf; av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); @@ -100,7 +105,7 @@ void AudioStreamStats::writeStreamInfoToXML(QXmlStreamWriter *writer) assert(writer); - writer->writeAttribute("sample_fmt", QString::fromStdString(getSample_fmt())); + writer->writeAttribute("sample_fmt", QString::fromStdString(getSample_fmt_string())); writer->writeAttribute("sample_rate", QString::number(getSample_rate())); writer->writeAttribute("channels", QString::number(getChannels())); writer->writeAttribute("channel_layout", QString::fromStdString(getChannel_layout())); @@ -115,14 +120,19 @@ void AudioStreamStats::writeStreamInfoToXML(QXmlStreamWriter *writer) CommonStreamStats::writeMetadataToXML(writer); } -std::string AudioStreamStats::getSample_fmt() const +int AudioStreamStats::getSample_fmt() const { return sample_fmt; } -void AudioStreamStats::setSample_fmt(const std::string &value) +std::string AudioStreamStats::getSample_fmt_string() const +{ + return sample_fmt_string; +} + +void AudioStreamStats::setSample_fmt_string(const std::string &value) { - sample_fmt = value; + sample_fmt_string = value; } int AudioStreamStats::getSample_rate() const diff --git a/Source/Core/AudioStreamStats.h b/Source/Core/AudioStreamStats.h index 526e95526..31cc250bb 100644 --- a/Source/Core/AudioStreamStats.h +++ b/Source/Core/AudioStreamStats.h @@ -26,8 +26,10 @@ class AudioStreamStats : public CommonStreamStats virtual void writeStreamInfoToXML(QXmlStreamWriter* writer); - std::string getSample_fmt() const; - void setSample_fmt(const std::string &value); + int getSample_fmt() const; + + std::string getSample_fmt_string() const; + void setSample_fmt_string(const std::string &value); int getSample_rate() const; void setSample_rate(const int &value); @@ -42,7 +44,8 @@ class AudioStreamStats : public CommonStreamStats void setBits_per_sample(int value); private: - std::string sample_fmt; + std::string sample_fmt_string; + int sample_fmt; int sample_rate; int channels; std::string channel_layout; diff --git a/Source/Core/CommonStreamStats.cpp b/Source/Core/CommonStreamStats.cpp index 0f0f01d64..59d80dfd3 100644 --- a/Source/Core/CommonStreamStats.cpp +++ b/Source/Core/CommonStreamStats.cpp @@ -207,6 +207,11 @@ std::string CommonStreamStats::getCodec_Type() const return codec_type; } +int CommonStreamStats::getType() const +{ + return stream_type; +} + std::string CommonStreamStats::getCodec_Time_Base() const { return codec_time_base; diff --git a/Source/Core/CommonStreamStats.h b/Source/Core/CommonStreamStats.h index 6e55b40f5..b78e64748 100644 --- a/Source/Core/CommonStreamStats.h +++ b/Source/Core/CommonStreamStats.h @@ -33,6 +33,7 @@ class CommonStreamStats std::string getCodec_Name() const; std::string getCodec_Long_Name() const; std::string getCodec_Type() const; + int getType() const; std::string getCodec_Time_Base() const; std::string getCodec_TagString() const; @@ -75,6 +76,7 @@ class CommonStreamStats std::string codec_name; std::string codec_long_name; std::string codec_type; + int stream_type; std::string codec_time_base; int codec_tag; diff --git a/Source/Core/FFmpeg_Glue.cpp b/Source/Core/FFmpeg_Glue.cpp index 38eaa37a2..7fed40740 100755 --- a/Source/Core/FFmpeg_Glue.cpp +++ b/Source/Core/FFmpeg_Glue.cpp @@ -2408,11 +2408,11 @@ string FFmpeg_Glue::ColorRange_Get() } } -int FFmpeg_Glue::BitsPerRawSample_Get() +int FFmpeg_Glue::BitsPerRawSample_Get(int streamType) { inputdata* InputData=NULL; for (size_t Pos=0; PosType==AVMEDIA_TYPE_VIDEO) + if (InputDatas[Pos] && InputDatas[Pos]->Type==streamType) { InputData=InputDatas[Pos]; break; @@ -2470,7 +2470,23 @@ string FFmpeg_Glue::SampleFormat_Get() case AV_SAMPLE_FMT_NB: return "number of sample formats"; default: return string(); } - } +} + +int FFmpeg_Glue::sampleFormat() +{ + inputdata* InputData=NULL; + for (size_t Pos=0; PosType==AVMEDIA_TYPE_AUDIO) + { + InputData=InputDatas[Pos]; + break; + } + + if (InputData==NULL || InputData->Stream==NULL || InputData->Stream->codec==NULL) + return AV_SAMPLE_FMT_NONE; + + return InputData->Stream->codec->sample_fmt; +} //--------------------------------------------------------------------------- int FFmpeg_Glue::SamplingRate_Get() @@ -2775,6 +2791,35 @@ int FFmpeg_Glue::guessBitsPerRawSampleFromFormat(int pixelFormat) } } +int FFmpeg_Glue::bitsPerAudioSample(int audioFormat) +{ + return av_get_bytes_per_sample((AVSampleFormat) audioFormat); +} + +bool FFmpeg_Glue::isFloatAudioSampleFormat(int audioFormat) +{ + return audioFormat == AV_SAMPLE_FMT_FLT + || audioFormat == AV_SAMPLE_FMT_FLTP + || audioFormat == AV_SAMPLE_FMT_DBL + || audioFormat == AV_SAMPLE_FMT_DBLP; +} + +bool FFmpeg_Glue::isSignedAudioSampleFormat(int audioFormat) +{ + return audioFormat == AV_SAMPLE_FMT_S16 + || audioFormat == AV_SAMPLE_FMT_S16P + || audioFormat == AV_SAMPLE_FMT_S32 + || audioFormat == AV_SAMPLE_FMT_S32P + || audioFormat == AV_SAMPLE_FMT_S64 + || audioFormat == AV_SAMPLE_FMT_S64P; +} + +bool FFmpeg_Glue::isUnsignedAudioSampleFormat(int audioFormat) +{ + return audioFormat == AV_SAMPLE_FMT_U8 + || audioFormat == AV_SAMPLE_FMT_U8P; +} + FFmpeg_Glue::Image::Image() { diff --git a/Source/Core/FFmpeg_Glue.h b/Source/Core/FFmpeg_Glue.h index d83d28f6a..0b3ee033e 100755 --- a/Source/Core/FFmpeg_Glue.h +++ b/Source/Core/FFmpeg_Glue.h @@ -135,11 +135,12 @@ class FFmpeg_Glue string PixFormat_Get(); string ColorSpace_Get(); string ColorRange_Get(); - int BitsPerRawSample_Get(); + int BitsPerRawSample_Get(int streamType = Type_Video); // Audio information string AudioFormat_Get(); string SampleFormat_Get(); + int sampleFormat(); int SamplingRate_Get(); string ChannelLayout_Get(); int ABitDepth_Get(); @@ -153,6 +154,10 @@ class FFmpeg_Glue static QByteArray getAttachment(const QString& fileName, QString& attachmentFileName); static int guessBitsPerRawSampleFromFormat(int pixelFormat); + static int bitsPerAudioSample(int audioFormat); + static bool isFloatAudioSampleFormat(int audioFormat); + static bool isSignedAudioSampleFormat(int audioFormat); + static bool isUnsignedAudioSampleFormat(int audioFormat); // Actions void AddInput_Video(size_t FrameCount, int time_base_num, int time_base_den, int Width, int Height, int BitDepth, bool Compression, int TimecodeBCD=-1); diff --git a/Source/Core/FileInformation.cpp b/Source/Core/FileInformation.cpp index 5cbcfb048..f85f7d85e 100644 --- a/Source/Core/FileInformation.cpp +++ b/Source/Core/FileInformation.cpp @@ -852,22 +852,62 @@ bool FileInformation::isValid() const return Glue != 0; } -int FileInformation::BitsPerRawSample() const +int FileInformation::BitsPerRawSample(int streamType) const { - int streamBitsPerRawSample = streamsStats ? streamsStats->bitsPerRawSample() : 0; - if(streamBitsPerRawSample != 0) - return streamBitsPerRawSample; + if(streamType == Type_Video) { + int streamBitsPerRawSample = streamsStats ? streamsStats->bitsPerRawVideoSample() : 0; + if(streamBitsPerRawSample != 0) + return streamBitsPerRawSample; + + if(Glue && Glue->BitsPerRawSample_Get() != 0) + return Glue->BitsPerRawSample_Get(); + + if(ReferenceStat()) { + auto guessedBitsPerRawSample = FFmpeg_Glue::guessBitsPerRawSampleFromFormat(*ReferenceStat()->pix_fmt); + if(guessedBitsPerRawSample != 0) + return guessedBitsPerRawSample; + } + + return 8; + } else if(streamType == Type_Audio) { + + int avSampleFormat = streamsStats ? streamsStats->avSampleFormat() : 0; + if(avSampleFormat != -1) + return FFmpeg_Glue::bitsPerAudioSample(avSampleFormat) * 8; + + if(Glue && Glue->sampleFormat() != -1) + return FFmpeg_Glue::bitsPerAudioSample(Glue->sampleFormat()) * 8; + } + + return 0; +} + +int FileInformation::audioSampleFormat() const +{ + int avSampleFormat = streamsStats ? streamsStats->avSampleFormat() : 0; + if(avSampleFormat != -1) + return avSampleFormat; - if(Glue && Glue->BitsPerRawSample_Get() != 0) - return Glue->BitsPerRawSample_Get(); + if(Glue && Glue->sampleFormat() != -1) + return Glue->sampleFormat(); - if(ReferenceStat()) { - auto guessedBitsPerRawSample = FFmpeg_Glue::guessBitsPerRawSampleFromFormat(*ReferenceStat()->pix_fmt); - if(guessedBitsPerRawSample != 0) - return guessedBitsPerRawSample; + return -1; +} + +QPair FileInformation::audioRanges() const +{ + auto sampleFormat = audioSampleFormat(); + if(FFmpeg_Glue::isFloatAudioSampleFormat(sampleFormat)) { + return QPair(-1, 1); + } else if(FFmpeg_Glue::isSignedAudioSampleFormat(sampleFormat)) { + auto bprs = BitsPerRawSample(Type_Audio); + return QPair(-pow(2, bprs - 1) - 1, pow(2, bprs - 1)); + } else if(FFmpeg_Glue::isUnsignedAudioSampleFormat(sampleFormat)) { + auto bprs = BitsPerRawSample(Type_Audio); + return QPair(0, pow(2, bprs)); } - return 8; + return QPair(0, 0); } FileInformation::SignalServerCheckUploadedStatus FileInformation::signalServerCheckUploadedStatus() const diff --git a/Source/Core/FileInformation.h b/Source/Core/FileInformation.h index 9479018f1..67c23b69f 100644 --- a/Source/Core/FileInformation.h +++ b/Source/Core/FileInformation.h @@ -97,7 +97,9 @@ class FileInformation : public QThread FormatStats* formatStats; CommonStats* ReferenceStat () const {if (ReferenceStream_Pos audioRanges() const; enum SignalServerCheckUploadedStatus { NotChecked, diff --git a/Source/Core/StreamsStats.cpp b/Source/Core/StreamsStats.cpp index 8eab7f4e2..9f84eff49 100644 --- a/Source/Core/StreamsStats.cpp +++ b/Source/Core/StreamsStats.cpp @@ -98,12 +98,22 @@ void StreamsStats::writeToXML(QXmlStreamWriter *writer) } -int StreamsStats::bitsPerRawSample() const +int StreamsStats::bitsPerRawVideoSample() const { for(auto& stream : streams) { - if(stream->getBitsPerRawSample() != 0) + if(stream->getType() == AVMEDIA_TYPE_VIDEO && stream->getBitsPerRawSample() != 0) return stream->getBitsPerRawSample(); } return 0; } + +int StreamsStats::avSampleFormat() const +{ + for(auto& stream : streams) { + if(stream->getType() == AVMEDIA_TYPE_AUDIO && (static_cast (stream.get()))->getSample_fmt() != AV_SAMPLE_FMT_NONE) + return (static_cast (stream.get()))->getSample_fmt(); + } + + return AV_SAMPLE_FMT_NONE; +} diff --git a/Source/Core/StreamsStats.h b/Source/Core/StreamsStats.h index 2a3fdb24a..352387e35 100644 --- a/Source/Core/StreamsStats.h +++ b/Source/Core/StreamsStats.h @@ -30,7 +30,8 @@ class StreamsStats { bool readFromXML(const char* data, size_t size); void writeToXML(QXmlStreamWriter* writer); - int bitsPerRawSample() const; + int bitsPerRawVideoSample() const; + int avSampleFormat() const; private: std::list streams; diff --git a/Source/Core/VideoStreamStats.cpp b/Source/Core/VideoStreamStats.cpp index 1a823ac25..9d560f172 100644 --- a/Source/Core/VideoStreamStats.cpp +++ b/Source/Core/VideoStreamStats.cpp @@ -54,6 +54,7 @@ static std::string rational_to_string(AVRational r, char sep) { VideoStreamStats::VideoStreamStats(XMLElement *streamElement) : CommonStreamStats(streamElement) { codec_type = "video"; + stream_type = AVMEDIA_TYPE_VIDEO; const char* width_value = streamElement->Attribute("width"); if(width_value) @@ -138,6 +139,7 @@ VideoStreamStats::VideoStreamStats(AVStream* stream, AVFormatContext *context) : } codec_type = "video"; + stream_type = AVMEDIA_TYPE_VIDEO; } void VideoStreamStats::writeStreamInfoToXML(QXmlStreamWriter *writer) diff --git a/Source/GUI/Plot.cpp b/Source/GUI/Plot.cpp index 1cc50aea2..9b2e428fd 100644 --- a/Source/GUI/Plot.cpp +++ b/Source/GUI/Plot.cpp @@ -460,12 +460,18 @@ Plot::Plot( size_t streamPos, size_t Type, size_t Group, const FileInformation* int v = m_charBackground.value(); const struct per_group& group = PerStreamType[m_type].PerGroup[m_group]; - auto bitsPerRawSample = m_fileInformation->BitsPerRawSample(); + auto bitsPerRawSample = m_fileInformation->BitsPerRawSample(type()); m_engine.globalObject().setProperty("bitsPerRawSample", bitsPerRawSample); m_engine.globalObject().setProperty("two_pow_bitsPerRawSample_minus_one", (1 << bitsPerRawSample) - 1); m_engine.globalObject().setProperty("sqrt_pow_bitsPerRawSample_2", sqrt(2) * (1 << bitsPerRawSample) / 2); + if(m_type == Type_Audio) { + auto ranges = m_fileInformation->audioRanges(); + m_engine.globalObject().setProperty("audio_min", ranges.first); + m_engine.globalObject().setProperty("audio_max", ranges.second); + } + m_minValue = m_engine.evaluate(group.MinFormula); bool isValid = m_minValue.isCallable(); isValid = m_minValue.isError();