Skip to content

Commit 00dfbc9

Browse files
committed
Fix frame orientation detection on Windows based on biHeight sign
- Use abs(biHeight) to get actual frame height - Check biHeight sign to determine orientation (negative=top-to-bottom, positive=bottom-to-top) - Remove incorrect pixel-format-based orientation detection - Add m_inputOrientation member variable to store orientation across frames - Reset m_firstFrameArrived when opening device Fixes #34
1 parent e198969 commit 00dfbc9

2 files changed

Lines changed: 24 additions & 4 deletions

File tree

src/ccap_imp_windows.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,7 @@ bool ProviderDirectShow::open(std::string_view deviceName) {
727727
m_isOpened = true;
728728
m_isRunning = false;
729729
m_frameIndex = 0;
730+
m_firstFrameArrived = false;
730731
return true;
731732
}
732733

@@ -757,9 +758,27 @@ HRESULT STDMETHODCALLTYPE ProviderDirectShow::SampleCB(double sampleTime, IMedia
757758
if (SUCCEEDED(hr)) {
758759
VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)mt.pbFormat;
759760
m_frameProp.width = vih->bmiHeader.biWidth;
760-
m_frameProp.height = vih->bmiHeader.biHeight;
761-
m_frameProp.fps = 10000000.0 / vih->AvgTimePerFrame;
761+
// biHeight may be negative. Negative height indicates top-to-bottom orientation.
762+
// Positive height indicates bottom-to-top orientation (standard Windows DIB format).
763+
m_frameProp.height = abs(vih->bmiHeader.biHeight);
764+
765+
// For YUV formats, always assume TopToBottom orientation regardless of biHeight
766+
// This fixes issues with some virtual cameras (like OBS) that report positive biHeight
767+
// but actually deliver TopToBottom data
762768
auto info = findPixelFormatInfo(mt.subtype);
769+
bool isYUVFormat = (info.pixelFormat & kPixelFormatYUVColorBit) != 0;
770+
771+
if (isYUVFormat) {
772+
// YUV data is typically TopToBottom, ignore biHeight sign
773+
m_inputOrientation = FrameOrientation::TopToBottom;
774+
CCAP_LOG_V("ccap: YUV format detected, using TopToBottom orientation (biHeight=%d)\n", vih->bmiHeader.biHeight);
775+
} else if (vih->bmiHeader.biHeight < 0) {
776+
m_inputOrientation = FrameOrientation::TopToBottom;
777+
} else {
778+
m_inputOrientation = FrameOrientation::BottomToTop;
779+
}
780+
781+
m_frameProp.fps = 10000000.0 / vih->AvgTimePerFrame;
763782
if (info.pixelFormat != PixelFormat::Unknown) {
764783
m_frameProp.cameraPixelFormat = info.pixelFormat;
765784
}
@@ -781,15 +800,14 @@ HRESULT STDMETHODCALLTYPE ProviderDirectShow::SampleCB(double sampleTime, IMedia
781800
uint32_t bufferLen = mediaSample->GetActualDataLength();
782801
bool isInputYUV = (m_frameProp.cameraPixelFormat & kPixelFormatYUVColorBit);
783802
bool isOutputYUV = (m_frameProp.outputPixelFormat & kPixelFormatYUVColorBit);
784-
auto inputOrientation = isInputYUV ? FrameOrientation::TopToBottom : FrameOrientation::BottomToTop;
785803

786804
newFrame->pixelFormat = m_frameProp.cameraPixelFormat;
787805
newFrame->width = m_frameProp.width;
788806
newFrame->height = m_frameProp.height;
789807
newFrame->orientation = isOutputYUV ? FrameOrientation::TopToBottom : m_frameOrientation;
790808
newFrame->nativeHandle = mediaSample;
791809

792-
bool shouldFlip = newFrame->orientation != inputOrientation && !isOutputYUV;
810+
bool shouldFlip = newFrame->orientation != m_inputOrientation && !isOutputYUV;
793811
bool shouldConvert = m_frameProp.cameraPixelFormat != m_frameProp.outputPixelFormat;
794812
bool zeroCopy = !shouldConvert && !shouldFlip;
795813

src/ccap_imp_windows.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ class ProviderDirectShow : public ProviderImp, public ISampleGrabberCB {
118118
std::vector<std::string> m_allDeviceNames;
119119

120120
std::chrono::steady_clock::time_point m_startTime{};
121+
FrameOrientation m_inputOrientation = FrameOrientation::TopToBottom;
122+
121123
bool m_firstFrameArrived = false;
122124

123125
// State variables

0 commit comments

Comments
 (0)