From 75d6c30b9607c5bde03cc784ebcc633955223493 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 4 Jan 2016 15:39:04 +0100 Subject: [PATCH 01/11] InputFile: updated log when no more data to read on file --- src/AvTranscoder/file/InputFile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AvTranscoder/file/InputFile.cpp b/src/AvTranscoder/file/InputFile.cpp index 38086a24..72e95f77 100644 --- a/src/AvTranscoder/file/InputFile.cpp +++ b/src/AvTranscoder/file/InputFile.cpp @@ -63,7 +63,7 @@ bool InputFile::readNextPacket( CodedData& data, const size_t streamIndex ) const int ret = av_read_frame( &_formatContext.getAVFormatContext(), &data.getAVPacket() ); if( ret < 0 ) // error or end of file { - LOG_INFO( "No more data to read on file '" << _filename << "'" ) + LOG_INFO( "No more data to read on file '" << _filename << "' for stream " << streamIndex ) return false; } From ef0c996cfe11adc7b47955c09fad8ac98327acf3 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 4 Jan 2016 16:03:05 +0100 Subject: [PATCH 02/11] StreamTranscoder: fixed private method getProcessCase The process case of a StreamTranscoder could change during the process (depending on the state of the _currentDecoder attribute). --- src/AvTranscoder/transcoder/StreamTranscoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.cpp b/src/AvTranscoder/transcoder/StreamTranscoder.cpp index 4e90eead..7acb815b 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.cpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.cpp @@ -556,9 +556,9 @@ void StreamTranscoder::setOffset( const float offset ) StreamTranscoder::EProcessCase StreamTranscoder::getProcessCase() const { - if( _inputStream && _inputDecoder ) + if( _inputStream && _inputDecoder && _currentDecoder == _inputDecoder ) return eProcessCaseTranscode; - else if( _inputStream && ! _inputDecoder ) + else if( _inputStream && ! _inputDecoder && ! _currentDecoder ) return eProcessCaseRewrap; else return eProcessCaseGenerator; From 28fcd823cd26d1494bf1712588a890251a524100 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 4 Jan 2016 16:05:26 +0100 Subject: [PATCH 03/11] StreamTranscoder: updated accessor of getProcessCase scope The method is now public. --- src/AvTranscoder/transcoder/StreamTranscoder.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AvTranscoder/transcoder/StreamTranscoder.hpp b/src/AvTranscoder/transcoder/StreamTranscoder.hpp index 912af924..181a90e3 100644 --- a/src/AvTranscoder/transcoder/StreamTranscoder.hpp +++ b/src/AvTranscoder/transcoder/StreamTranscoder.hpp @@ -101,10 +101,6 @@ class AvExport StreamTranscoder */ void setOffset( const float offset ); -private: - bool processRewrap(); - bool processTranscode( const int subStreamIndex = -1 ); ///< By default transcode all channels - //@{ // Get the current process case. enum EProcessCase { @@ -115,6 +111,10 @@ class AvExport StreamTranscoder EProcessCase getProcessCase() const; //@} +private: + bool processRewrap(); + bool processTranscode( const int subStreamIndex = -1 ); ///< By default transcode all channels + private: IInputStream* _inputStream; ///< Input stream to read next packet (has link, no ownership) IOutputStream* _outputStream; ///< Output stream to wrap next packet (has link, no ownership) From a5b2348136e63c7c254cc61c4826009436b9d557 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 4 Jan 2016 16:24:22 +0100 Subject: [PATCH 04/11] Transcoder: added log when a process ended --- src/AvTranscoder/transcoder/Transcoder.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/AvTranscoder/transcoder/Transcoder.cpp b/src/AvTranscoder/transcoder/Transcoder.cpp index 984bf34d..b17eede9 100644 --- a/src/AvTranscoder/transcoder/Transcoder.cpp +++ b/src/AvTranscoder/transcoder/Transcoder.cpp @@ -252,21 +252,26 @@ ProcessStat Transcoder::process( IProgress& progress ) // check if JobStatusCancel if( progress.progress( ( progressDuration > outputDuration ) ? outputDuration : progressDuration, outputDuration ) == eJobStatusCancel ) + { + LOG_INFO( "End of process because the job was canceled." ) break; + } // check progressDuration if( progressDuration >= outputDuration ) + { + LOG_INFO( "End of process because the output program duration (" << progressDuration << "s) is equal or upper than " << outputDuration << "s." ) break; + } LOG_DEBUG( "Process frame " << frame ) frameProcessed = processFrame(); - ++frame; } _outputFile.endWrap(); - LOG_INFO( "End of process" ) + LOG_INFO( "End of process: " << frame << " frames processed" ) LOG_INFO( "Get process statistics" ) ProcessStat processStat; From 95d9d420c91188c32e3c517ab09dfbdf07fbe5eb Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 4 Jan 2016 16:28:19 +0100 Subject: [PATCH 05/11] Transcoder: fixed how to process a frame for each stream * Try to process a new frame for each stream (do not exit the method if a stream failed to process). * Skip the generated streams because they always succeed. --- src/AvTranscoder/transcoder/Transcoder.cpp | 25 ++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/AvTranscoder/transcoder/Transcoder.cpp b/src/AvTranscoder/transcoder/Transcoder.cpp index b17eede9..bfbae42a 100644 --- a/src/AvTranscoder/transcoder/Transcoder.cpp +++ b/src/AvTranscoder/transcoder/Transcoder.cpp @@ -209,16 +209,33 @@ bool Transcoder::processFrame() if( _streamTranscoders.size() == 0 ) return false; + // For each stream, process a frame + size_t nbStreamProcessStatusFailed = 0; for( size_t streamIndex = 0; streamIndex < _streamTranscoders.size(); ++streamIndex ) { LOG_DEBUG( "Process stream " << streamIndex << "/" << ( _streamTranscoders.size() - 1 ) ) - - bool streamProcessStatus = _streamTranscoders.at( streamIndex )->processFrame(); - if( ! streamProcessStatus ) + const bool currentStreamProcessStatus = _streamTranscoders.at( streamIndex )->processFrame(); + if( ! currentStreamProcessStatus ) { - return false; + LOG_WARN( "Failed to process stream " << streamIndex ) + ++nbStreamProcessStatusFailed; } } + + // Get the number of streams without the generators (they always succeed) + size_t nbStreamsWithoutGenerator = _streamTranscoders.size(); + for( size_t streamIndex = 0; streamIndex < _streamTranscoders.size(); ++streamIndex ) + { + if( _streamTranscoders.at( streamIndex )->getProcessCase() == StreamTranscoder::eProcessCaseGenerator ) + --nbStreamsWithoutGenerator; + } + + // If all streams failed to process a new frame + if( nbStreamsWithoutGenerator != 0 && nbStreamProcessStatusFailed == nbStreamsWithoutGenerator ) + { + LOG_INFO( "End of process because all streams (except generators) failed to process a new frame." ) + return false; + } return true; } From 087c3aa76dcbde89ac1dfef8672feaf2d2455635 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 4 Jan 2016 16:35:09 +0100 Subject: [PATCH 06/11] Transcoder: fixed how to get the progressDuration during a process Added getCurrentOutputDuration private method. --- src/AvTranscoder/transcoder/Transcoder.cpp | 16 +++++++++++++++- src/AvTranscoder/transcoder/Transcoder.hpp | 7 +++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/AvTranscoder/transcoder/Transcoder.cpp b/src/AvTranscoder/transcoder/Transcoder.cpp index bfbae42a..76140422 100644 --- a/src/AvTranscoder/transcoder/Transcoder.cpp +++ b/src/AvTranscoder/transcoder/Transcoder.cpp @@ -265,7 +265,7 @@ ProcessStat Transcoder::process( IProgress& progress ) bool frameProcessed = true; while( frameProcessed ) { - const float progressDuration = _outputFile.getStream( 0 ).getStreamDuration(); + const float progressDuration = getCurrentOutputDuration(); // check if JobStatusCancel if( progress.progress( ( progressDuration > outputDuration ) ? outputDuration : progressDuration, outputDuration ) == eJobStatusCancel ) @@ -501,6 +501,20 @@ float Transcoder::getOutputDuration() const } } +float Transcoder::getCurrentOutputDuration() const +{ + float currentOutputDuration = -1; + for( size_t streamIndex = 0; streamIndex < _streamTranscoders.size(); ++streamIndex ) + { + const float currentStreamDuration = _outputFile.getStream( streamIndex ).getStreamDuration(); + if( currentOutputDuration == -1 ) + currentOutputDuration = currentStreamDuration; + else if( currentStreamDuration < currentOutputDuration ) + currentOutputDuration = currentStreamDuration; + } + return currentOutputDuration; +} + void Transcoder::manageSwitchToGenerator() { for( size_t i = 0; i < _streamTranscoders.size(); ++i ) diff --git a/src/AvTranscoder/transcoder/Transcoder.hpp b/src/AvTranscoder/transcoder/Transcoder.hpp index b0407859..5a106be3 100644 --- a/src/AvTranscoder/transcoder/Transcoder.hpp +++ b/src/AvTranscoder/transcoder/Transcoder.hpp @@ -185,6 +185,13 @@ class AvExport Transcoder */ float getOutputDuration() const; + /** + * @brief Get the current duration of the output program + * @note Returns the duration of the smallest stream. + * @return -1 if there is no output stream. + */ + float getCurrentOutputDuration() const; + /** * @brief Set for each StreamTranscoder if it can switch to generator at the end. */ From fbdc5329aeb1f6c90947f8d123224f6f1f1c8aea Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 4 Jan 2016 16:38:47 +0100 Subject: [PATCH 07/11] Transcoder: renamed private method getOutputDuration to getExpectedOutputDuration --- src/AvTranscoder/transcoder/Transcoder.cpp | 12 ++++++------ src/AvTranscoder/transcoder/Transcoder.hpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/AvTranscoder/transcoder/Transcoder.cpp b/src/AvTranscoder/transcoder/Transcoder.cpp index 76140422..4a71dbb1 100644 --- a/src/AvTranscoder/transcoder/Transcoder.cpp +++ b/src/AvTranscoder/transcoder/Transcoder.cpp @@ -258,8 +258,8 @@ ProcessStat Transcoder::process( IProgress& progress ) preProcessCodecLatency(); - const float outputDuration = getOutputDuration(); - LOG_INFO( "Output duration of the process will be " << outputDuration << "s." ) + const float expectedOutputDuration = getExpectedOutputDuration(); + LOG_INFO( "Output duration of the process will be " << expectedOutputDuration << "s." ) size_t frame = 0; bool frameProcessed = true; @@ -268,16 +268,16 @@ ProcessStat Transcoder::process( IProgress& progress ) const float progressDuration = getCurrentOutputDuration(); // check if JobStatusCancel - if( progress.progress( ( progressDuration > outputDuration ) ? outputDuration : progressDuration, outputDuration ) == eJobStatusCancel ) + if( progress.progress( ( progressDuration > expectedOutputDuration ) ? expectedOutputDuration : progressDuration, expectedOutputDuration ) == eJobStatusCancel ) { LOG_INFO( "End of process because the job was canceled." ) break; } // check progressDuration - if( progressDuration >= outputDuration ) + if( progressDuration >= expectedOutputDuration ) { - LOG_INFO( "End of process because the output program duration (" << progressDuration << "s) is equal or upper than " << outputDuration << "s." ) + LOG_INFO( "End of process because the output program duration (" << progressDuration << "s) is equal or upper than " << expectedOutputDuration << "s." ) break; } @@ -482,7 +482,7 @@ float Transcoder::getMaxTotalDuration() const return maxTotalDuration; } -float Transcoder::getOutputDuration() const +float Transcoder::getExpectedOutputDuration() const { switch( _eProcessMethod ) { diff --git a/src/AvTranscoder/transcoder/Transcoder.hpp b/src/AvTranscoder/transcoder/Transcoder.hpp index 5a106be3..0f89c662 100644 --- a/src/AvTranscoder/transcoder/Transcoder.hpp +++ b/src/AvTranscoder/transcoder/Transcoder.hpp @@ -180,10 +180,10 @@ class AvExport Transcoder float getMaxTotalDuration() const; /** - * @brief Get the duration of the output program + * @brief Get the expected duration of the output program * @note Depends on the streams, the process method, and the main stream index. */ - float getOutputDuration() const; + float getExpectedOutputDuration() const; /** * @brief Get the current duration of the output program From 534be5a18ad6ad578f1e820b27ce8d7042f3d792 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 4 Jan 2016 18:06:16 +0100 Subject: [PATCH 08/11] Transcoder: added eProcessMethodProcessAll process method * To stop the process when all the input data are read. * This is the default process method. * Do not check the output program duration wgen we use the process policy. --- src/AvTranscoder/transcoder/Transcoder.cpp | 4 ++-- src/AvTranscoder/transcoder/Transcoder.hpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/AvTranscoder/transcoder/Transcoder.cpp b/src/AvTranscoder/transcoder/Transcoder.cpp index 4a71dbb1..49bf3403 100644 --- a/src/AvTranscoder/transcoder/Transcoder.cpp +++ b/src/AvTranscoder/transcoder/Transcoder.cpp @@ -16,7 +16,7 @@ Transcoder::Transcoder( IOutputFile& outputFile ) , _streamTranscoders() , _streamTranscodersAllocated() , _profileLoader( true ) - , _eProcessMethod ( eProcessMethodBasedOnStream ) + , _eProcessMethod ( eProcessMethodProcessAll ) , _mainStreamIndex( 0 ) , _outputDuration( 0 ) {} @@ -275,7 +275,7 @@ ProcessStat Transcoder::process( IProgress& progress ) } // check progressDuration - if( progressDuration >= expectedOutputDuration ) + if( _eProcessMethod != eProcessMethodProcessAll && progressDuration >= expectedOutputDuration ) { LOG_INFO( "End of process because the output program duration (" << progressDuration << "s) is equal or upper than " << expectedOutputDuration << "s." ) break; diff --git a/src/AvTranscoder/transcoder/Transcoder.hpp b/src/AvTranscoder/transcoder/Transcoder.hpp index 0f89c662..6b03a9dc 100644 --- a/src/AvTranscoder/transcoder/Transcoder.hpp +++ b/src/AvTranscoder/transcoder/Transcoder.hpp @@ -22,6 +22,7 @@ namespace avtranscoder * eProcessMethodLongest: stop transcode at the end of the longest stream. * eProcessMethodBasedOnStream: stop transcode at the end of an indicated stream (@see _indexBasedStream attribute of Transcoder). * eProcessMethodBasedOnDuration: stop transcode at the end of an indicated duration, in seconds (@see _outputDuration attribute of Transcoder). + * eProcessMethodProcessAll: stop the process when all the input data are read. * eProcessMethodInfinity: stop transcode by outside of avTranscoder (streaming mode) */ enum EProcessMethod @@ -30,6 +31,7 @@ enum EProcessMethod eProcessMethodLongest, eProcessMethodBasedOnStream, eProcessMethodBasedOnDuration, + eProcessMethodProcessAll, eProcessMethodInfinity, }; @@ -211,7 +213,7 @@ class AvExport Transcoder ProfileLoader _profileLoader; ///< Objet to get existing profiles, and add new ones for the Transcoder. - EProcessMethod _eProcessMethod; ///< Transcoding policy + EProcessMethod _eProcessMethod; ///< Transcoding policy (eProcessMethodProcessAll by default) size_t _mainStreamIndex; ///< Index of stream used to stop the process of transcode in case of eProcessMethodBasedOnStream. float _outputDuration; ///< Duration of output media used to stop the process of transcode in case of eProcessMethodBasedOnDuration. }; From 3266a27468b1196f3fd6f2d8a0a7ab860ea9b179 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 4 Jan 2016 18:07:24 +0100 Subject: [PATCH 09/11] Transcoder: updated doc of the processing policy --- src/AvTranscoder/transcoder/Transcoder.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/AvTranscoder/transcoder/Transcoder.hpp b/src/AvTranscoder/transcoder/Transcoder.hpp index 6b03a9dc..6023d18a 100644 --- a/src/AvTranscoder/transcoder/Transcoder.hpp +++ b/src/AvTranscoder/transcoder/Transcoder.hpp @@ -17,13 +17,13 @@ namespace avtranscoder { /** - * @brief Enum to set a policy of how we manage the transcode in case of several streams. - * eProcessMethodShortest: stop transcode at the end of the shortest stream. - * eProcessMethodLongest: stop transcode at the end of the longest stream. - * eProcessMethodBasedOnStream: stop transcode at the end of an indicated stream (@see _indexBasedStream attribute of Transcoder). - * eProcessMethodBasedOnDuration: stop transcode at the end of an indicated duration, in seconds (@see _outputDuration attribute of Transcoder). + * @brief Enum to set a policy of how we manage the process in case of several streams. + * eProcessMethodShortest: stop the process at the end of the shortest stream. + * eProcessMethodLongest: stop the process at the end of the longest stream. + * eProcessMethodBasedOnStream: stop the process at the end of an indicated stream (@see _indexBasedStream attribute of Transcoder). + * eProcessMethodBasedOnDuration: stop the process at the end of an indicated duration, in seconds (@see _outputDuration attribute of Transcoder). * eProcessMethodProcessAll: stop the process when all the input data are read. - * eProcessMethodInfinity: stop transcode by outside of avTranscoder (streaming mode) + * eProcessMethodInfinity: stop the process by outside of avTranscoder (streaming mode) */ enum EProcessMethod { @@ -213,7 +213,7 @@ class AvExport Transcoder ProfileLoader _profileLoader; ///< Objet to get existing profiles, and add new ones for the Transcoder. - EProcessMethod _eProcessMethod; ///< Transcoding policy (eProcessMethodProcessAll by default) + EProcessMethod _eProcessMethod; ///< Processing policy (eProcessMethodProcessAll by default) size_t _mainStreamIndex; ///< Index of stream used to stop the process of transcode in case of eProcessMethodBasedOnStream. float _outputDuration; ///< Duration of output media used to stop the process of transcode in case of eProcessMethodBasedOnDuration. }; From 812dcb8b6f8ac708ec489ee1f6b9f6e1e7747b07 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 4 Jan 2016 18:07:44 +0100 Subject: [PATCH 10/11] pyTest: added test to check EProcessMethodProcessAll processing policy --- test/pyTest/testEProcessMethod.py | 32 ++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/pyTest/testEProcessMethod.py b/test/pyTest/testEProcessMethod.py index 2a431120..3ff947d8 100644 --- a/test/pyTest/testEProcessMethod.py +++ b/test/pyTest/testEProcessMethod.py @@ -2,11 +2,13 @@ # Check if environment is setup to run the tests if os.environ.get('AVTRANSCODER_TEST_VIDEO_AVI_FILE') is None or \ + os.environ.get('AVTRANSCODER_TEST_VIDEO_MOV_FILE') is None or \ os.environ.get('AVTRANSCODER_TEST_AUDIO_MOV_FILE') is None or \ os.environ.get('AVTRANSCODER_TEST_AUDIO_WAVE_FILE') is None: from nose.plugins.skip import SkipTest raise SkipTest("Need to define environment variables " "AVTRANSCODER_TEST_VIDEO_AVI_FILE and " + "AVTRANSCODER_TEST_VIDEO_MOV_FILE and " "AVTRANSCODER_TEST_AUDIO_MOV_FILE and " "AVTRANSCODER_TEST_AUDIO_WAVE_FILE") @@ -105,6 +107,35 @@ def testEProcessMethodBasedOnStream(): assert_almost_equals( dst_stream_properties.getDuration(), src_properties_second.getDuration(), delta=0.05 ) +def testEProcessMethodProcessAll(): + """ + Process with method eProcessMethodProcessAll, check the duration of each output stream (which could be differentes). + """ + inputFileName = os.environ['AVTRANSCODER_TEST_VIDEO_MOV_FILE'] + outputFileName = "testEProcessMethodProcessAll.mov" + + ouputFile = av.OutputFile( outputFileName ) + transcoder = av.Transcoder( ouputFile ) + transcoder.setProcessMethod( av.eProcessMethodProcessAll ) + + transcoder.add( inputFileName, 0 ) + transcoder.add( inputFileName, 1 ) + + progress = av.ConsoleProgress() + transcoder.process( progress ) + + # get src file + src_inputFile = av.InputFile( inputFileName ) + src_properties = src_inputFile.getProperties() + + # get dst file + dst_inputFile = av.InputFile( outputFileName ) + dst_properties = dst_inputFile.getProperties() + + assert_equals( src_properties.getStreamProperties()[0].getDuration(), dst_properties.getStreamProperties()[0].getDuration() ) + assert_equals( src_properties.getStreamProperties()[1].getDuration(), dst_properties.getStreamProperties()[1].getDuration() ) + + def testEProcessMethodBasedOnDuration(): """ Process with method eProcessMethodBasedOnDuration, check output duration. @@ -131,4 +162,3 @@ def testEProcessMethodBasedOnDuration(): dst_properties = dst_inputFile.getProperties() assert_equals( dst_properties.getDuration(), outputDuration ) - From 5dc447ef060155e667772b3d8068651894f5fdc4 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Mon, 4 Jan 2016 18:31:20 +0100 Subject: [PATCH 11/11] pyTest: updated testEProcessMethod when check the output duration Check the duration of the stream instead of the duration of the file. --- test/pyTest/testEProcessMethod.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/pyTest/testEProcessMethod.py b/test/pyTest/testEProcessMethod.py index 3ff947d8..62b0d637 100644 --- a/test/pyTest/testEProcessMethod.py +++ b/test/pyTest/testEProcessMethod.py @@ -43,7 +43,7 @@ def testEProcessMethodShortest(): dst_inputFile = av.InputFile( outputFileName ) dst_properties = dst_inputFile.getProperties() - assert_equals( dst_properties.getDuration(), src_properties_shortest.getDuration() ) + assert_equals( src_properties_shortest.getStreamProperties()[0].getDuration(), dst_properties.getStreamProperties()[1].getDuration() ) def testEProcessMethodLongest(): @@ -72,7 +72,7 @@ def testEProcessMethodLongest(): dst_inputFile = av.InputFile( outputFileName ) dst_properties = dst_inputFile.getProperties() - assert_equals( dst_properties.getDuration(), src_properties_longest.getDuration() ) + assert_equals( src_properties_longest.getStreamProperties()[0].getDuration(), dst_properties.getStreamProperties()[0].getDuration() ) def testEProcessMethodBasedOnStream(): @@ -161,4 +161,4 @@ def testEProcessMethodBasedOnDuration(): dst_inputFile = av.InputFile( outputFileName ) dst_properties = dst_inputFile.getProperties() - assert_equals( dst_properties.getDuration(), outputDuration ) + assert_equals( dst_properties.getStreamProperties()[0].getDuration(), outputDuration ) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy