2021-10-20 21:18:24 +08:00
|
|
|
// This file is part of OpenCV project.
|
|
|
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
|
|
|
// of this distribution and at http://opencv.org/license.html.
|
|
|
|
|
|
|
|
#include "test_precomp.hpp"
|
|
|
|
|
|
|
|
namespace opencv_test { namespace {
|
|
|
|
|
|
|
|
//file name, number of audio channels, epsilon, video type, weight, height, number of frame, number of audio samples, fps, psnr Threshold, backend
|
|
|
|
typedef std::tuple<std::string, int, double, int, int, int, int, int, int, double, VideoCaptureAPIs> paramCombination;
|
|
|
|
//file name, number of audio channels, number of audio samples, epsilon, backend
|
|
|
|
typedef std::tuple<std::string, int, int, double, VideoCaptureAPIs> param;
|
|
|
|
|
|
|
|
class AudioBaseTest
|
|
|
|
{
|
|
|
|
protected:
|
2023-10-06 18:33:21 +08:00
|
|
|
AudioBaseTest(){}
|
2021-10-20 21:18:24 +08:00
|
|
|
void getValidAudioData()
|
|
|
|
{
|
|
|
|
const double step = 3.14/22050;
|
|
|
|
double value = 0;
|
|
|
|
validAudioData.resize(expectedNumAudioCh);
|
|
|
|
for (int nCh = 0; nCh < expectedNumAudioCh; nCh++)
|
|
|
|
{
|
|
|
|
value = 0;
|
|
|
|
for(unsigned int i = 0; i < numberOfSamples; i++)
|
|
|
|
{
|
|
|
|
if (i != 0 && i % 44100 == 0)
|
|
|
|
value = 0;
|
|
|
|
validAudioData[nCh].push_back(sin(value));
|
|
|
|
value += step;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void checkAudio()
|
|
|
|
{
|
|
|
|
getValidAudioData();
|
|
|
|
|
|
|
|
ASSERT_EQ(expectedNumAudioCh, (int)audioData.size());
|
|
|
|
for (unsigned int nCh = 0; nCh < audioData.size(); nCh++)
|
|
|
|
{
|
2022-05-25 07:35:45 +08:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (audioData[nCh].size() == 132924 && numberOfSamples == 131819 && fileName == "test_audio.mp4")
|
|
|
|
throw SkipTestException("Detected failure observed on legacy Windows versions. SKIP");
|
|
|
|
#endif
|
2021-10-20 21:18:24 +08:00
|
|
|
ASSERT_EQ(numberOfSamples, audioData[nCh].size()) << "nCh=" << nCh;
|
|
|
|
for (unsigned int i = 0; i < numberOfSamples; i++)
|
|
|
|
{
|
|
|
|
EXPECT_NEAR(validAudioData[nCh][i], audioData[nCh][i], epsilon) << "sample index=" << i << " nCh=" << nCh;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
protected:
|
|
|
|
int expectedNumAudioCh;
|
|
|
|
unsigned int numberOfSamples;
|
|
|
|
double epsilon;
|
|
|
|
VideoCaptureAPIs backend;
|
|
|
|
std::string root;
|
|
|
|
std::string fileName;
|
|
|
|
|
|
|
|
std::vector<std::vector<double>> validAudioData;
|
|
|
|
std::vector<std::vector<double>> audioData;
|
|
|
|
std::vector<int> params;
|
|
|
|
|
|
|
|
Mat audioFrame;
|
|
|
|
VideoCapture cap;
|
|
|
|
};
|
|
|
|
|
|
|
|
class AudioTestFixture : public AudioBaseTest, public testing::TestWithParam <param>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
AudioTestFixture()
|
|
|
|
{
|
|
|
|
fileName = get<0>(GetParam());
|
|
|
|
expectedNumAudioCh = get<1>(GetParam());
|
|
|
|
numberOfSamples = get<2>(GetParam());
|
|
|
|
epsilon = get<3>(GetParam());
|
|
|
|
backend = get<4>(GetParam());
|
|
|
|
root = "audio/";
|
|
|
|
params = { CAP_PROP_AUDIO_STREAM, 0,
|
|
|
|
CAP_PROP_VIDEO_STREAM, -1,
|
|
|
|
CAP_PROP_AUDIO_DATA_DEPTH, CV_16S };
|
|
|
|
}
|
|
|
|
|
|
|
|
void doTest()
|
|
|
|
{
|
|
|
|
ASSERT_TRUE(cap.open(findDataFile(root + fileName), backend, params));
|
|
|
|
const int audioBaseIndex = static_cast<int>(cap.get(cv::CAP_PROP_AUDIO_BASE_INDEX));
|
|
|
|
const int numberOfChannels = (int)cap.get(CAP_PROP_AUDIO_TOTAL_CHANNELS);
|
|
|
|
ASSERT_EQ(expectedNumAudioCh, numberOfChannels);
|
|
|
|
double f = 0;
|
|
|
|
audioData.resize(numberOfChannels);
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
if (cap.grab())
|
|
|
|
{
|
|
|
|
for (int nCh = 0; nCh < numberOfChannels; nCh++)
|
|
|
|
{
|
2021-12-17 03:43:02 +08:00
|
|
|
ASSERT_TRUE(cap.retrieve(audioFrame, audioBaseIndex + nCh));
|
2021-10-20 21:18:24 +08:00
|
|
|
ASSERT_EQ(CV_16SC1, audioFrame.type()) << audioData[nCh].size();
|
|
|
|
for (int i = 0; i < audioFrame.cols; i++)
|
|
|
|
{
|
|
|
|
f = ((double) audioFrame.at<signed short>(0,i)) / (double) 32768;
|
|
|
|
audioData[nCh].push_back(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { break; }
|
|
|
|
}
|
|
|
|
ASSERT_FALSE(audioData.empty());
|
|
|
|
|
|
|
|
checkAudio();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const param audioParams[] =
|
|
|
|
{
|
2021-12-17 03:43:02 +08:00
|
|
|
#ifdef _WIN32
|
2021-10-20 21:18:24 +08:00
|
|
|
param("test_audio.wav", 1, 132300, 0.0001, cv::CAP_MSMF),
|
|
|
|
param("test_mono_audio.mp3", 1, 133104, 0.12, cv::CAP_MSMF),
|
|
|
|
param("test_stereo_audio.mp3", 2, 133104, 0.12, cv::CAP_MSMF),
|
2021-12-17 03:43:02 +08:00
|
|
|
param("test_audio.mp4", 1, 133104, 0.15, cv::CAP_MSMF),
|
|
|
|
#endif
|
|
|
|
param("test_audio.wav", 1, 132300, 0.0001, cv::CAP_GSTREAMER),
|
|
|
|
param("test_audio.mp4", 1, 132522, 0.15, cv::CAP_GSTREAMER),
|
2021-10-20 21:18:24 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class Audio : public AudioTestFixture{};
|
|
|
|
|
|
|
|
TEST_P(Audio, audio)
|
|
|
|
{
|
|
|
|
if (!videoio_registry::hasBackend(cv::VideoCaptureAPIs(backend)))
|
|
|
|
throw SkipTestException(cv::videoio_registry::getBackendName(backend) + " backend was not found");
|
|
|
|
|
|
|
|
doTest();
|
|
|
|
}
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_CASE_P(/**/, Audio, testing::ValuesIn(audioParams));
|
|
|
|
|
|
|
|
class MediaTestFixture : public AudioBaseTest, public testing::TestWithParam <paramCombination>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
MediaTestFixture():
|
|
|
|
videoType(get<3>(GetParam())),
|
|
|
|
height(get<4>(GetParam())),
|
|
|
|
width(get<5>(GetParam())),
|
|
|
|
numberOfFrames(get<6>(GetParam())),
|
|
|
|
fps(get<8>(GetParam())),
|
|
|
|
psnrThreshold(get<9>(GetParam()))
|
|
|
|
{
|
|
|
|
fileName = get<0>(GetParam());
|
|
|
|
expectedNumAudioCh = get<1>(GetParam());
|
|
|
|
numberOfSamples = get<7>(GetParam());
|
|
|
|
epsilon = get<2>(GetParam());
|
|
|
|
backend = get<10>(GetParam());
|
|
|
|
root = "audio/";
|
|
|
|
params = { CAP_PROP_AUDIO_STREAM, 0,
|
|
|
|
CAP_PROP_VIDEO_STREAM, 0,
|
|
|
|
CAP_PROP_AUDIO_DATA_DEPTH, CV_16S };
|
2023-10-06 18:33:21 +08:00
|
|
|
}
|
2021-10-20 21:18:24 +08:00
|
|
|
|
|
|
|
void doTest()
|
|
|
|
{
|
|
|
|
ASSERT_TRUE(cap.open(findDataFile(root + fileName), backend, params));
|
|
|
|
|
|
|
|
const int audioBaseIndex = static_cast<int>(cap.get(cv::CAP_PROP_AUDIO_BASE_INDEX));
|
|
|
|
const int numberOfChannels = (int)cap.get(CAP_PROP_AUDIO_TOTAL_CHANNELS);
|
|
|
|
ASSERT_EQ(expectedNumAudioCh, numberOfChannels);
|
|
|
|
|
|
|
|
const int samplePerSecond = (int)cap.get(CAP_PROP_AUDIO_SAMPLES_PER_SECOND);
|
|
|
|
ASSERT_EQ(44100, samplePerSecond);
|
|
|
|
int samplesPerFrame = (int)(1./fps*samplePerSecond);
|
|
|
|
|
|
|
|
double audio0_timestamp = 0;
|
|
|
|
|
|
|
|
Mat videoFrame;
|
|
|
|
Mat img(height, width, videoType);
|
|
|
|
audioData.resize(numberOfChannels);
|
|
|
|
for (int frame = 0; frame < numberOfFrames; frame++)
|
|
|
|
{
|
|
|
|
SCOPED_TRACE(cv::format("frame=%d", frame));
|
|
|
|
|
|
|
|
ASSERT_TRUE(cap.grab());
|
|
|
|
if (frame == 0)
|
|
|
|
{
|
|
|
|
double audio_shift = cap.get(CAP_PROP_AUDIO_SHIFT_NSEC);
|
|
|
|
double video0_timestamp = cap.get(CAP_PROP_POS_MSEC) * 1e-3;
|
|
|
|
audio0_timestamp = video0_timestamp + audio_shift * 1e-9;
|
Merge pull request #24243 from kecsap:4.x
Fix gstreamer backend with manual pipelines #24243
- Fix broken seeking in audio/video playback
- Fix broken audio playback
- Fix unreliable seeking
- Estimate frame count if it is not available directly
- Return -1 for frame count and fps if it is not available.
- Return 0 for fps if the video has variable frame rate
- Enable and fix tests
### Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
- [X] I agree to contribute to the project under Apache 2 License.
- [X] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [X] The PR is proposed to the proper branch
- [-] There is a reference to the original bug report and related work => Reproducible test provided
- [-] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
Patch to opencv_extra has the same branch name.
- [X] The feature is well documented and sample code can be built with the project CMake
1. Download two test videos:
```bash
wget https://github.com/ietf-wg-cellar/matroska-test-files/raw/master/test_files/test1.mkv
wget https://test-videos.co.uk/vids/jellyfish/mkv/360/Jellyfish_360_10s_5MB.mkv
```
2. I modified a OpenCV videoio sample to demonstrate the problem, here it is the patch: http://dpaste.com//C9MAT2K6W
3. Build the sample, on Ubuntu:
```bash
g++ -g videocapture_audio_combination.cpp -I/usr/include/opencv4 `pkg-config --libs --cflags opencv4` -o videocapture_audio_combination
```
4. Play an audio stream with seeking BEFORE the fix:
```bash
$ ./videocapture_audio_combination --audio "filesrc location=test1.mkv ! queue ! matroskademux name=demux demux.audio_0 ! decodebin ! audioconvert ! appsink"[ERROR:0@0.009] global cap.cpp:164 open VIDEOIO(GSTREAMER): raised OpenCV exception:
OpenCV(4.8.0-dev) ./modules/videoio/src/cap_gstreamer.cpp:153: error: (-215:Assertion failed) ptr in function 'get'
[ WARN:0@0.009] global cap.cpp:204 open VIDEOIO(GSTREAMER): backend is generally available but can't be used to capture by name
ERROR! Can't to open file: filesrc location=test1.mkv ! queue ! matroskademux name=demux demux.audio_0 ! decodebin ! audioconvert ! appsink
```
5. Play a video stream with seeking BEFORE the fix:
```bash
$ ./videocapture_audio_combination --audio "filesrc location=Jellyfish_360_10s_5MB.mkv ! queue ! matroskademux name=demux demux.video_0 ! decodebin ! videoconvert ! video/x-raw, format=BGR ! appsink drop=1"
[ WARN:0@0.034] global cap_gstreamer.cpp:1728 open OpenCV | GStreamer warning: Cannot query video position: status=1, value=22, duration=300
CAP_PROP_AUDIO_DATA_DEPTH: CV_16S
CAP_PROP_AUDIO_SAMPLES_PER_SECOND: 44100
CAP_PROP_AUDIO_TOTAL_CHANNELS: 0
CAP_PROP_AUDIO_TOTAL_STREAMS: [ WARN:0@0.034] global cap_gstreamer.cpp:1898 getProperty OpenCV | GStreamer: CAP_PROP_AUDIO_TOTAL_STREAMS property is not supported
0
[ WARN:0@0.034] global cap_gstreamer.cpp:1817 getProperty OpenCV | GStreamer: CAP_PROP_POS_MSEC property result may be unrealiable: https://github.com/opencv/opencv/issues/19025
Timestamp: 0.6218
Timestamp: 33.1085
Timestamp: 67.1274
Timestamp: 100.1182
Timestamp: 133.1204
Timestamp: 167.1195
Timestamp: 200.1161
Timestamp: 233.1147
Timestamp: 267.1194
Timestamp: 300.1202
[ WARN:0@0.338] global cap_gstreamer.cpp:1949 setProperty OpenCV | GStreamer warning: GStreamer: unable to seek
0:00:00.338215907 3892572 0x5592899c7580 WARN basesrc gstbasesrc.c:3127:gst_base_src_loop:<filesrc0> error: Internal data stream error.
0:00:00.338235884 3892572 0x5592899c7580 WARN basesrc gstbasesrc.c:3127:gst_base_src_loop:<filesrc0> error: streaming stopped, reason not-linked (-1)
0:00:00.338264287 3892572 0x5592899c7580 WARN queue gstqueue.c:992:gst_queue_handle_sink_event:<queue0> error: Internal data stream error.
0:00:00.338270329 3892572 0x5592899c7580 WARN queue gstqueue.c:992:gst_queue_handle_sink_event:<queue0> error: streaming stopped, reason not-linked (-1)
[ WARN:0@0.339] global cap_gstreamer.cpp:2784 handleMessage OpenCV | GStreamer warning: Embedded video playback halted; module filesrc0 reported: Internal data stream error.
[ WARN:0@0.339] global cap_gstreamer.cpp:1199 startPipeline OpenCV | GStreamer warning: unable to start pipeline
Number of audio samples: 0
Number of video frames: 10
[ WARN:0@0.339] global cap_gstreamer.cpp:1164 isPipelinePlaying OpenCV | GStreamer warning: GStreamer: pipeline have not been created
```
6. Play an audio stream with seeking AFTER the fix:
```bash
$ ./videocapture_audio_combination --audio "filesrc location=test1.mkv ! queue ! matroskademux name=demux demux.audio_0 ! decodebin ! audioconvert ! appsink"CAP_PROP_AUDIO_DATA_DEPTH: CV_16S
CAP_PROP_AUDIO_SAMPLES_PER_SECOND: 48000
CAP_PROP_AUDIO_TOTAL_CHANNELS: 2
CAP_PROP_AUDIO_TOTAL_STREAMS: [ WARN:0@0.025] global cap_gstreamer.cpp:1903 getProperty OpenCV | GStreamer: CAP_PROP_AUDIO_TOTAL_STREAMS property is not supported
0
Timestamp: 0.0000
Timestamp: 24.0000
Timestamp: 48.0000
Timestamp: 72.0000
Timestamp: 96.0000
Timestamp: 120.0000
Timestamp: 144.0000
Timestamp: 168.0000
Timestamp: 192.0000
Timestamp: 216.0000
Timestamp: 3500.0000
Timestamp: 3504.0000
Timestamp: 3528.0000
Timestamp: 3552.0000
Timestamp: 3576.0000
Timestamp: 3600.0000
Timestamp: 3624.0000
Timestamp: 3648.0000
Timestamp: 3672.0000
Timestamp: 3696.0000
Timestamp: 3720.0000
Timestamp: 3744.0000
Timestamp: 3768.0000
Timestamp: 3792.0000
Timestamp: 3816.0000
Timestamp: 3840.0000
Timestamp: 3864.0000
Timestamp: 3888.0000
Timestamp: 3912.0000
Timestamp: 3936.0000
```
7. Play a video stream with seeking AFTER the fix:
```bash
$ ./videocapture_audio_combination --audio "filesrc location=Jellyfish_360_10s_5MB.mkv ! queue ! matroskademux name=demux demux.video_0 ! decodebin ! videoconvert ! video/x-raw, format=BGR ! appsink drop=1"
[ WARN:0@0.033] global cap_gstreamer.cpp:1746 open OpenCV | GStreamer warning: Cannot query video position: status=1, value=22, duration=300
CAP_PROP_AUDIO_DATA_DEPTH: CV_16S
CAP_PROP_AUDIO_SAMPLES_PER_SECOND: 44100
CAP_PROP_AUDIO_TOTAL_CHANNELS: 0
CAP_PROP_AUDIO_TOTAL_STREAMS: [ WARN:0@0.034] global cap_gstreamer.cpp:1903 getProperty OpenCV | GStreamer: CAP_PROP_AUDIO_TOTAL_STREAMS property is not supported
0
Timestamp: 0.0000
Timestamp: 33.0000
Timestamp: 67.0000
Timestamp: 100.0000
Timestamp: 133.0000
Timestamp: 167.0000
Timestamp: 200.0000
Timestamp: 233.0000
Timestamp: 267.0000
Timestamp: 300.0000
0:00:00.335931693 3893501 0x55bbe76ad920 WARN matroskareadcommon matroska-read-common.c:759:gst_matroska_read_common_parse_skip:<demux:sink> Unknown CueTrackPositions subelement 0xf0 - ignoring
0:00:00.335952823 3893501 0x55bbe76ad920 WARN matroskareadcommon matroska-read-common.c:759:gst_matroska_read_common_parse_skip:<demux:sink> Unknown CueTrackPositions subelement 0xf0 - ignoring
0:00:00.335988029 3893501 0x55bbe76ad920 WARN basesrc gstbasesrc.c:1742:gst_base_src_perform_seek:<filesrc0> duplicate event found 184
Timestamp: 3467.0000
Timestamp: 3500.0000
Timestamp: 3533.0000
Timestamp: 3567.0000
Timestamp: 3600.0000
Timestamp: 3633.0000
Timestamp: 3667.0000
Timestamp: 3700.0000
Timestamp: 3733.0000
Timestamp: 3767.0000
Timestamp: 3800.0000
Timestamp: 3833.0000
Timestamp: 3867.0000
Timestamp: 3900.0000
Timestamp: 3933.0000
Timestamp: 3967.0000
Timestamp: 4000.0000
Timestamp: 4033.0000
Timestamp: 4067.0000
Timestamp: 4100.0000
```
2023-11-08 18:41:50 +08:00
|
|
|
|
2021-10-20 21:18:24 +08:00
|
|
|
std::cout << "video0 timestamp: " << video0_timestamp << " audio0 timestamp: " << audio0_timestamp << " (audio shift nanoseconds: " << audio_shift << " , seconds: " << audio_shift * 1e-9 << ")" << std::endl;
|
|
|
|
}
|
|
|
|
ASSERT_TRUE(cap.retrieve(videoFrame));
|
|
|
|
if (epsilon >= 0)
|
|
|
|
{
|
|
|
|
generateFrame(frame, numberOfFrames, img);
|
|
|
|
ASSERT_EQ(img.size, videoFrame.size);
|
|
|
|
double psnr = cvtest::PSNR(img, videoFrame);
|
|
|
|
EXPECT_GE(psnr, psnrThreshold);
|
|
|
|
}
|
|
|
|
|
|
|
|
int audioFrameCols = 0;
|
|
|
|
for (int nCh = 0; nCh < numberOfChannels; nCh++)
|
|
|
|
{
|
|
|
|
ASSERT_TRUE(cap.retrieve(audioFrame, audioBaseIndex+nCh));
|
|
|
|
if (audioFrame.empty())
|
|
|
|
continue;
|
|
|
|
ASSERT_EQ(CV_16SC1, audioFrame.type());
|
|
|
|
if (nCh == 0)
|
|
|
|
audioFrameCols = audioFrame.cols;
|
|
|
|
else
|
|
|
|
ASSERT_EQ(audioFrameCols, audioFrame.cols) << "channel "<< nCh;
|
|
|
|
for (int i = 0; i < audioFrame.cols; i++)
|
|
|
|
{
|
|
|
|
double f = audioFrame.at<signed short>(0,i) / 32768.0;
|
|
|
|
audioData[nCh].push_back(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame < 5 || frame >= numberOfFrames-5)
|
|
|
|
std::cout << "frame=" << frame << ": audioFrameSize=" << audioFrameCols << " videoTimestamp=" << cap.get(CAP_PROP_POS_MSEC) << " ms" << std::endl;
|
|
|
|
else if (frame == 6)
|
|
|
|
std::cout << "frame..." << std::endl;
|
|
|
|
|
|
|
|
if (audioFrameCols == 0)
|
|
|
|
continue;
|
|
|
|
if (frame != 0 && frame != numberOfFrames-1)
|
|
|
|
{
|
|
|
|
// validate audio position
|
|
|
|
EXPECT_NEAR(
|
|
|
|
cap.get(CAP_PROP_AUDIO_POS) / samplePerSecond + audio0_timestamp,
|
|
|
|
cap.get(CAP_PROP_POS_MSEC) * 1e-3,
|
Merge pull request #24243 from kecsap:4.x
Fix gstreamer backend with manual pipelines #24243
- Fix broken seeking in audio/video playback
- Fix broken audio playback
- Fix unreliable seeking
- Estimate frame count if it is not available directly
- Return -1 for frame count and fps if it is not available.
- Return 0 for fps if the video has variable frame rate
- Enable and fix tests
### Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
- [X] I agree to contribute to the project under Apache 2 License.
- [X] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [X] The PR is proposed to the proper branch
- [-] There is a reference to the original bug report and related work => Reproducible test provided
- [-] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
Patch to opencv_extra has the same branch name.
- [X] The feature is well documented and sample code can be built with the project CMake
1. Download two test videos:
```bash
wget https://github.com/ietf-wg-cellar/matroska-test-files/raw/master/test_files/test1.mkv
wget https://test-videos.co.uk/vids/jellyfish/mkv/360/Jellyfish_360_10s_5MB.mkv
```
2. I modified a OpenCV videoio sample to demonstrate the problem, here it is the patch: http://dpaste.com//C9MAT2K6W
3. Build the sample, on Ubuntu:
```bash
g++ -g videocapture_audio_combination.cpp -I/usr/include/opencv4 `pkg-config --libs --cflags opencv4` -o videocapture_audio_combination
```
4. Play an audio stream with seeking BEFORE the fix:
```bash
$ ./videocapture_audio_combination --audio "filesrc location=test1.mkv ! queue ! matroskademux name=demux demux.audio_0 ! decodebin ! audioconvert ! appsink"[ERROR:0@0.009] global cap.cpp:164 open VIDEOIO(GSTREAMER): raised OpenCV exception:
OpenCV(4.8.0-dev) ./modules/videoio/src/cap_gstreamer.cpp:153: error: (-215:Assertion failed) ptr in function 'get'
[ WARN:0@0.009] global cap.cpp:204 open VIDEOIO(GSTREAMER): backend is generally available but can't be used to capture by name
ERROR! Can't to open file: filesrc location=test1.mkv ! queue ! matroskademux name=demux demux.audio_0 ! decodebin ! audioconvert ! appsink
```
5. Play a video stream with seeking BEFORE the fix:
```bash
$ ./videocapture_audio_combination --audio "filesrc location=Jellyfish_360_10s_5MB.mkv ! queue ! matroskademux name=demux demux.video_0 ! decodebin ! videoconvert ! video/x-raw, format=BGR ! appsink drop=1"
[ WARN:0@0.034] global cap_gstreamer.cpp:1728 open OpenCV | GStreamer warning: Cannot query video position: status=1, value=22, duration=300
CAP_PROP_AUDIO_DATA_DEPTH: CV_16S
CAP_PROP_AUDIO_SAMPLES_PER_SECOND: 44100
CAP_PROP_AUDIO_TOTAL_CHANNELS: 0
CAP_PROP_AUDIO_TOTAL_STREAMS: [ WARN:0@0.034] global cap_gstreamer.cpp:1898 getProperty OpenCV | GStreamer: CAP_PROP_AUDIO_TOTAL_STREAMS property is not supported
0
[ WARN:0@0.034] global cap_gstreamer.cpp:1817 getProperty OpenCV | GStreamer: CAP_PROP_POS_MSEC property result may be unrealiable: https://github.com/opencv/opencv/issues/19025
Timestamp: 0.6218
Timestamp: 33.1085
Timestamp: 67.1274
Timestamp: 100.1182
Timestamp: 133.1204
Timestamp: 167.1195
Timestamp: 200.1161
Timestamp: 233.1147
Timestamp: 267.1194
Timestamp: 300.1202
[ WARN:0@0.338] global cap_gstreamer.cpp:1949 setProperty OpenCV | GStreamer warning: GStreamer: unable to seek
0:00:00.338215907 3892572 0x5592899c7580 WARN basesrc gstbasesrc.c:3127:gst_base_src_loop:<filesrc0> error: Internal data stream error.
0:00:00.338235884 3892572 0x5592899c7580 WARN basesrc gstbasesrc.c:3127:gst_base_src_loop:<filesrc0> error: streaming stopped, reason not-linked (-1)
0:00:00.338264287 3892572 0x5592899c7580 WARN queue gstqueue.c:992:gst_queue_handle_sink_event:<queue0> error: Internal data stream error.
0:00:00.338270329 3892572 0x5592899c7580 WARN queue gstqueue.c:992:gst_queue_handle_sink_event:<queue0> error: streaming stopped, reason not-linked (-1)
[ WARN:0@0.339] global cap_gstreamer.cpp:2784 handleMessage OpenCV | GStreamer warning: Embedded video playback halted; module filesrc0 reported: Internal data stream error.
[ WARN:0@0.339] global cap_gstreamer.cpp:1199 startPipeline OpenCV | GStreamer warning: unable to start pipeline
Number of audio samples: 0
Number of video frames: 10
[ WARN:0@0.339] global cap_gstreamer.cpp:1164 isPipelinePlaying OpenCV | GStreamer warning: GStreamer: pipeline have not been created
```
6. Play an audio stream with seeking AFTER the fix:
```bash
$ ./videocapture_audio_combination --audio "filesrc location=test1.mkv ! queue ! matroskademux name=demux demux.audio_0 ! decodebin ! audioconvert ! appsink"CAP_PROP_AUDIO_DATA_DEPTH: CV_16S
CAP_PROP_AUDIO_SAMPLES_PER_SECOND: 48000
CAP_PROP_AUDIO_TOTAL_CHANNELS: 2
CAP_PROP_AUDIO_TOTAL_STREAMS: [ WARN:0@0.025] global cap_gstreamer.cpp:1903 getProperty OpenCV | GStreamer: CAP_PROP_AUDIO_TOTAL_STREAMS property is not supported
0
Timestamp: 0.0000
Timestamp: 24.0000
Timestamp: 48.0000
Timestamp: 72.0000
Timestamp: 96.0000
Timestamp: 120.0000
Timestamp: 144.0000
Timestamp: 168.0000
Timestamp: 192.0000
Timestamp: 216.0000
Timestamp: 3500.0000
Timestamp: 3504.0000
Timestamp: 3528.0000
Timestamp: 3552.0000
Timestamp: 3576.0000
Timestamp: 3600.0000
Timestamp: 3624.0000
Timestamp: 3648.0000
Timestamp: 3672.0000
Timestamp: 3696.0000
Timestamp: 3720.0000
Timestamp: 3744.0000
Timestamp: 3768.0000
Timestamp: 3792.0000
Timestamp: 3816.0000
Timestamp: 3840.0000
Timestamp: 3864.0000
Timestamp: 3888.0000
Timestamp: 3912.0000
Timestamp: 3936.0000
```
7. Play a video stream with seeking AFTER the fix:
```bash
$ ./videocapture_audio_combination --audio "filesrc location=Jellyfish_360_10s_5MB.mkv ! queue ! matroskademux name=demux demux.video_0 ! decodebin ! videoconvert ! video/x-raw, format=BGR ! appsink drop=1"
[ WARN:0@0.033] global cap_gstreamer.cpp:1746 open OpenCV | GStreamer warning: Cannot query video position: status=1, value=22, duration=300
CAP_PROP_AUDIO_DATA_DEPTH: CV_16S
CAP_PROP_AUDIO_SAMPLES_PER_SECOND: 44100
CAP_PROP_AUDIO_TOTAL_CHANNELS: 0
CAP_PROP_AUDIO_TOTAL_STREAMS: [ WARN:0@0.034] global cap_gstreamer.cpp:1903 getProperty OpenCV | GStreamer: CAP_PROP_AUDIO_TOTAL_STREAMS property is not supported
0
Timestamp: 0.0000
Timestamp: 33.0000
Timestamp: 67.0000
Timestamp: 100.0000
Timestamp: 133.0000
Timestamp: 167.0000
Timestamp: 200.0000
Timestamp: 233.0000
Timestamp: 267.0000
Timestamp: 300.0000
0:00:00.335931693 3893501 0x55bbe76ad920 WARN matroskareadcommon matroska-read-common.c:759:gst_matroska_read_common_parse_skip:<demux:sink> Unknown CueTrackPositions subelement 0xf0 - ignoring
0:00:00.335952823 3893501 0x55bbe76ad920 WARN matroskareadcommon matroska-read-common.c:759:gst_matroska_read_common_parse_skip:<demux:sink> Unknown CueTrackPositions subelement 0xf0 - ignoring
0:00:00.335988029 3893501 0x55bbe76ad920 WARN basesrc gstbasesrc.c:1742:gst_base_src_perform_seek:<filesrc0> duplicate event found 184
Timestamp: 3467.0000
Timestamp: 3500.0000
Timestamp: 3533.0000
Timestamp: 3567.0000
Timestamp: 3600.0000
Timestamp: 3633.0000
Timestamp: 3667.0000
Timestamp: 3700.0000
Timestamp: 3733.0000
Timestamp: 3767.0000
Timestamp: 3800.0000
Timestamp: 3833.0000
Timestamp: 3867.0000
Timestamp: 3900.0000
Timestamp: 3933.0000
Timestamp: 3967.0000
Timestamp: 4000.0000
Timestamp: 4033.0000
Timestamp: 4067.0000
Timestamp: 4100.0000
```
2023-11-08 18:41:50 +08:00
|
|
|
(1.0 / fps) * 0.6)
|
2021-10-20 21:18:24 +08:00
|
|
|
<< "CAP_PROP_AUDIO_POS=" << cap.get(CAP_PROP_AUDIO_POS) << " CAP_PROP_POS_MSEC=" << cap.get(CAP_PROP_POS_MSEC);
|
|
|
|
}
|
|
|
|
if (frame != 0 && frame != numberOfFrames-1 && audioData[0].size() != (size_t)numberOfSamples)
|
|
|
|
{
|
2022-12-23 18:15:22 +08:00
|
|
|
if (backend == cv::CAP_MSMF)
|
|
|
|
{
|
|
|
|
int audioSamplesTolerance = samplesPerFrame / 2;
|
|
|
|
// validate audio frame size
|
|
|
|
EXPECT_NEAR(audioFrame.cols, samplesPerFrame, audioSamplesTolerance);
|
|
|
|
}
|
2021-10-20 21:18:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ASSERT_FALSE(cap.grab());
|
|
|
|
ASSERT_FALSE(audioData.empty());
|
|
|
|
|
|
|
|
std::cout << "Total audio samples=" << audioData[0].size() << std::endl;
|
|
|
|
|
|
|
|
if (epsilon >= 0)
|
|
|
|
checkAudio();
|
|
|
|
}
|
|
|
|
protected:
|
|
|
|
const int videoType;
|
|
|
|
const int height;
|
|
|
|
const int width;
|
|
|
|
const int numberOfFrames;
|
|
|
|
const int fps;
|
|
|
|
const double psnrThreshold;
|
|
|
|
};
|
|
|
|
|
2021-12-17 03:43:02 +08:00
|
|
|
class Media : public MediaTestFixture{};
|
|
|
|
|
|
|
|
TEST_P(Media, audio)
|
|
|
|
{
|
|
|
|
if (!videoio_registry::hasBackend(cv::VideoCaptureAPIs(backend)))
|
|
|
|
throw SkipTestException(cv::videoio_registry::getBackendName(backend) + " backend was not found");
|
2023-01-09 18:06:10 +08:00
|
|
|
if (cvtest::skipUnstableTests && backend == CAP_GSTREAMER)
|
|
|
|
throw SkipTestException("Unstable GStreamer test");
|
2021-12-17 03:43:02 +08:00
|
|
|
|
|
|
|
doTest();
|
|
|
|
}
|
|
|
|
|
2021-10-20 21:18:24 +08:00
|
|
|
const paramCombination mediaParams[] =
|
|
|
|
{
|
2022-12-23 18:15:22 +08:00
|
|
|
paramCombination("test_audio.mp4", 1, 0.15, CV_8UC3, 240, 320, 90, 132299, 30, 30., cv::CAP_GSTREAMER)
|
2021-12-17 03:43:02 +08:00
|
|
|
#ifdef _WIN32
|
2022-12-23 18:15:22 +08:00
|
|
|
, paramCombination("test_audio.mp4", 1, 0.15, CV_8UC3, 240, 320, 90, 131819, 30, 30., cv::CAP_MSMF)
|
2021-10-20 21:18:24 +08:00
|
|
|
#if 0
|
|
|
|
// https://filesamples.com/samples/video/mp4/sample_960x400_ocean_with_audio.mp4
|
|
|
|
, paramCombination("sample_960x400_ocean_with_audio.mp4", 2, -1/*eplsilon*/, CV_8UC3, 400, 960, 1116, 2056588, 30, 30., cv::CAP_MSMF)
|
|
|
|
#endif
|
2021-12-17 03:43:02 +08:00
|
|
|
#endif // _WIN32
|
2021-10-20 21:18:24 +08:00
|
|
|
};
|
|
|
|
|
2021-12-17 03:43:02 +08:00
|
|
|
INSTANTIATE_TEST_CASE_P(/**/, Media, testing::ValuesIn(mediaParams));
|
2021-10-20 21:18:24 +08:00
|
|
|
|
2021-12-17 03:43:02 +08:00
|
|
|
TEST(AudioOpenCheck, bad_arg_invalid_audio_stream)
|
2021-10-20 21:18:24 +08:00
|
|
|
{
|
2022-12-23 18:15:22 +08:00
|
|
|
if (!videoio_registry::hasBackend(cv::VideoCaptureAPIs(cv::CAP_MSMF)))
|
|
|
|
throw SkipTestException("CAP_MSMF backend was not found");
|
|
|
|
|
2021-12-17 03:43:02 +08:00
|
|
|
std::string fileName = "audio/test_audio.wav";
|
|
|
|
std::vector<int> params {
|
|
|
|
CAP_PROP_AUDIO_STREAM, 1,
|
|
|
|
CAP_PROP_VIDEO_STREAM, -1, // disabled
|
|
|
|
CAP_PROP_AUDIO_DATA_DEPTH, CV_16S
|
|
|
|
};
|
|
|
|
VideoCapture cap;
|
2022-12-23 18:15:22 +08:00
|
|
|
cap.open(findDataFile(fileName), cv::CAP_MSMF, params);
|
2021-12-17 03:43:02 +08:00
|
|
|
ASSERT_FALSE(cap.isOpened());
|
2021-10-20 21:18:24 +08:00
|
|
|
}
|
|
|
|
|
2021-12-17 03:43:02 +08:00
|
|
|
TEST(AudioOpenCheck, bad_arg_invalid_audio_stream_video)
|
|
|
|
{
|
2022-12-23 18:15:22 +08:00
|
|
|
if (!videoio_registry::hasBackend(cv::VideoCaptureAPIs(cv::CAP_MSMF)))
|
|
|
|
throw SkipTestException("CAP_MSMF backend was not found");
|
|
|
|
|
2021-12-17 03:43:02 +08:00
|
|
|
std::string fileName = "audio/test_audio.mp4";
|
|
|
|
std::vector<int> params {
|
|
|
|
CAP_PROP_AUDIO_STREAM, 1,
|
|
|
|
CAP_PROP_VIDEO_STREAM, 0,
|
|
|
|
CAP_PROP_AUDIO_DATA_DEPTH, CV_16S
|
|
|
|
};
|
|
|
|
VideoCapture cap;
|
2022-12-23 18:15:22 +08:00
|
|
|
cap.open(findDataFile(fileName), cv::CAP_MSMF, params);
|
2021-12-17 03:43:02 +08:00
|
|
|
ASSERT_FALSE(cap.isOpened());
|
|
|
|
}
|
2021-10-20 21:18:24 +08:00
|
|
|
|
2022-12-23 18:15:22 +08:00
|
|
|
|
2021-12-17 03:43:02 +08:00
|
|
|
TEST(AudioOpenCheck, MSMF_bad_arg_invalid_audio_sample_per_second)
|
2021-10-26 22:33:53 +08:00
|
|
|
{
|
2022-12-23 18:15:22 +08:00
|
|
|
if (!videoio_registry::hasBackend(cv::VideoCaptureAPIs(cv::CAP_MSMF)))
|
|
|
|
throw SkipTestException("CAP_MSMF backend was not found");
|
|
|
|
|
2021-10-26 22:33:53 +08:00
|
|
|
std::string fileName = "audio/test_audio.mp4";
|
2021-12-17 03:43:02 +08:00
|
|
|
std::vector<int> params {
|
|
|
|
CAP_PROP_AUDIO_STREAM, 0,
|
|
|
|
CAP_PROP_VIDEO_STREAM, -1, // disabled
|
|
|
|
CAP_PROP_AUDIO_SAMPLES_PER_SECOND, (int)1e9
|
|
|
|
};
|
2021-10-26 22:33:53 +08:00
|
|
|
VideoCapture cap;
|
|
|
|
cap.open(findDataFile(fileName), cv::CAP_MSMF, params);
|
|
|
|
ASSERT_FALSE(cap.isOpened());
|
|
|
|
}
|
|
|
|
|
2021-12-04 18:37:10 +08:00
|
|
|
TEST(AudioOpenCheck, bad_arg_invalid_audio_sample_per_second)
|
|
|
|
{
|
|
|
|
std::string fileName = "audio/test_audio.mp4";
|
2021-12-17 03:43:02 +08:00
|
|
|
std::vector<int> params {
|
|
|
|
CAP_PROP_AUDIO_STREAM, 0,
|
|
|
|
CAP_PROP_VIDEO_STREAM, -1, // disabled
|
|
|
|
CAP_PROP_AUDIO_SAMPLES_PER_SECOND, -1000
|
|
|
|
};
|
2021-12-04 18:37:10 +08:00
|
|
|
VideoCapture cap;
|
2021-12-17 03:43:02 +08:00
|
|
|
cap.open(findDataFile(fileName), cv::CAP_ANY, params);
|
2021-12-04 18:37:10 +08:00
|
|
|
ASSERT_FALSE(cap.isOpened());
|
|
|
|
}
|
|
|
|
|
2021-10-20 21:18:24 +08:00
|
|
|
}} //namespace
|