diff --git a/webrtc/base/BUILD.gn b/webrtc/base/BUILD.gn index 565cdb7a1d..880d897986 100644 --- a/webrtc/base/BUILD.gn +++ b/webrtc/base/BUILD.gn @@ -471,6 +471,8 @@ rtc_static_library("rtc_base") { "testbase64.h", "testclient.cc", "testclient.h", + "transformadapter.cc", + "transformadapter.h", "virtualsocketserver.cc", "virtualsocketserver.h", "window.h", diff --git a/webrtc/base/base.gyp b/webrtc/base/base.gyp index d98dfa0526..7ab73b5137 100644 --- a/webrtc/base/base.gyp +++ b/webrtc/base/base.gyp @@ -432,6 +432,8 @@ 'testbase64.h', 'testclient.cc', 'testclient.h', + 'transformadapter.cc', + 'transformadapter.h', 'virtualsocketserver.cc', 'virtualsocketserver.h', 'window.h', diff --git a/webrtc/base/transformadapter.cc b/webrtc/base/transformadapter.cc new file mode 100644 index 0000000000..d6d85b5372 --- /dev/null +++ b/webrtc/base/transformadapter.cc @@ -0,0 +1,197 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/transformadapter.h" + +#include + +#include "webrtc/base/common.h" + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// + +TransformAdapter::TransformAdapter(StreamInterface * stream, + TransformInterface * transform, + bool direction_read) + : StreamAdapterInterface(stream), transform_(transform), + direction_read_(direction_read), state_(ST_PROCESSING), len_(0) { +} + +TransformAdapter::~TransformAdapter() { + TransformAdapter::Close(); + delete transform_; +} + +StreamResult +TransformAdapter::Read(void * buffer, size_t buffer_len, + size_t * read, int * error) { + if (!direction_read_) + return SR_EOS; + + while (state_ != ST_ERROR) { + if (state_ == ST_COMPLETE) + return SR_EOS; + + // Buffer more data + if ((state_ == ST_PROCESSING) && (len_ < sizeof(buffer_))) { + size_t subread; + StreamResult result = StreamAdapterInterface::Read( + buffer_ + len_, + sizeof(buffer_) - len_, + &subread, + &error_); + if (result == SR_BLOCK) { + return SR_BLOCK; + } else if (result == SR_ERROR) { + state_ = ST_ERROR; + break; + } else if (result == SR_EOS) { + state_ = ST_FLUSHING; + } else { + len_ += subread; + } + } + + // Process buffered data + size_t in_len = len_; + size_t out_len = buffer_len; + StreamResult result = transform_->Transform(buffer_, &in_len, + buffer, &out_len, + (state_ == ST_FLUSHING)); + ASSERT(result != SR_BLOCK); + if (result == SR_EOS) { + // Note: Don't signal SR_EOS this iteration, unless out_len is zero + state_ = ST_COMPLETE; + } else if (result == SR_ERROR) { + state_ = ST_ERROR; + error_ = -1; // TODO: propagate error + break; + } else if ((out_len == 0) && (state_ == ST_FLUSHING)) { + // If there is no output AND no more input, then something is wrong + state_ = ST_ERROR; + error_ = -1; // TODO: better error code? + break; + } + + len_ -= in_len; + if (len_ > 0) + memmove(buffer_, buffer_ + in_len, len_); + + if (out_len == 0) + continue; + + if (read) + *read = out_len; + return SR_SUCCESS; + } + + if (error) + *error = error_; + return SR_ERROR; +} + +StreamResult +TransformAdapter::Write(const void * data, size_t data_len, + size_t * written, int * error) { + if (direction_read_) + return SR_EOS; + + size_t bytes_written = 0; + while (state_ != ST_ERROR) { + if (state_ == ST_COMPLETE) + return SR_EOS; + + if (len_ < sizeof(buffer_)) { + // Process buffered data + size_t in_len = data_len; + size_t out_len = sizeof(buffer_) - len_; + StreamResult result = transform_->Transform(data, &in_len, + buffer_ + len_, &out_len, + (state_ == ST_FLUSHING)); + + ASSERT(result != SR_BLOCK); + if (result == SR_EOS) { + // Note: Don't signal SR_EOS this iteration, unless no data written + state_ = ST_COMPLETE; + } else if (result == SR_ERROR) { + ASSERT(false); // When this happens, think about what should be done + state_ = ST_ERROR; + error_ = -1; // TODO: propagate error + break; + } + + len_ = out_len; + bytes_written = in_len; + } + + size_t pos = 0; + while (pos < len_) { + size_t subwritten; + StreamResult result = StreamAdapterInterface::Write(buffer_ + pos, + len_ - pos, + &subwritten, + &error_); + if (result == SR_BLOCK) { + ASSERT(false); // TODO: we should handle this + return SR_BLOCK; + } else if (result == SR_ERROR) { + state_ = ST_ERROR; + break; + } else if (result == SR_EOS) { + state_ = ST_COMPLETE; + break; + } + + pos += subwritten; + } + + len_ -= pos; + if (len_ > 0) + memmove(buffer_, buffer_ + pos, len_); + + if (bytes_written == 0) + continue; + + if (written) + *written = bytes_written; + return SR_SUCCESS; + } + + if (error) + *error = error_; + return SR_ERROR; +} + +void +TransformAdapter::Close() { + if (!direction_read_ && (state_ == ST_PROCESSING)) { + state_ = ST_FLUSHING; + do { + Write(0, 0, NULL, NULL); + } while (state_ == ST_FLUSHING); + } + state_ = ST_COMPLETE; + StreamAdapterInterface::Close(); +} + +bool TransformAdapter::GetAvailable(size_t* size) const { + return false; +} + +bool TransformAdapter::ReserveSize(size_t size) { + return true; +} + +bool TransformAdapter::Rewind() { + return false; +} + +} // namespace rtc diff --git a/webrtc/base/transformadapter.h b/webrtc/base/transformadapter.h new file mode 100644 index 0000000000..290d5605ff --- /dev/null +++ b/webrtc/base/transformadapter.h @@ -0,0 +1,84 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_BASE_TRANSFORMADAPTER_H__ +#define WEBRTC_BASE_TRANSFORMADAPTER_H__ + +#include "webrtc/base/stream.h" + +namespace rtc { +/////////////////////////////////////////////////////////////////////////////// + +class TransformInterface { +public: + virtual ~TransformInterface() { } + + // Transform should convert the in_len bytes of input into the out_len-sized + // output buffer. If flush is true, there will be no more data following + // input. + // After the transformation, in_len contains the number of bytes consumed, and + // out_len contains the number of bytes ready in output. + // Note: Transform should not return SR_BLOCK, as there is no asynchronous + // notification available. + virtual StreamResult Transform(const void * input, size_t * in_len, + void * output, size_t * out_len, + bool flush) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// + +// TransformAdapter causes all data passed through to be transformed by the +// supplied TransformInterface object, which may apply compression, encryption, +// etc. + +class TransformAdapter : public StreamAdapterInterface { +public: + // Note that the transformation is unidirectional, in the direction specified + // by the constructor. Operations in the opposite direction result in SR_EOS. + TransformAdapter(StreamInterface * stream, + TransformInterface * transform, + bool direction_read); + ~TransformAdapter() override; + + StreamResult Read(void* buffer, + size_t buffer_len, + size_t* read, + int* error) override; + StreamResult Write(const void* data, + size_t data_len, + size_t* written, + int* error) override; + void Close() override; + + // Apriori, we can't tell what the transformation does to the stream length. + bool GetAvailable(size_t* size) const override; + bool ReserveSize(size_t size) override; + + // Transformations might not be restartable + virtual bool Rewind(); + +private: + enum State { ST_PROCESSING, ST_FLUSHING, ST_COMPLETE, ST_ERROR }; + enum { BUFFER_SIZE = 1024 }; + + TransformInterface * transform_; + bool direction_read_; + State state_; + int error_; + + char buffer_[BUFFER_SIZE]; + size_t len_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // WEBRTC_BASE_TRANSFORMADAPTER_H__