Subonsic API: added estimateContentLength parameter support for stream endpoint. fixes #244

This commit is contained in:
emeric
2022-08-20 14:56:16 +02:00
parent 49f0d40515
commit 1fb838c8d0
13 changed files with 162 additions and 104 deletions
+42 -9
View File
@@ -18,32 +18,48 @@
*/
#include "TranscodeResourceHandler.hpp"
#include "utils/Logger.hpp"
namespace Av
{
namespace
{
std::size_t
doEstimateContentLength(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters)
{
const std::size_t estimatedContentLength {transcodeParameters.bitrate / 8 * std::chrono::duration_cast<std::chrono::milliseconds>(inputFileParameters.duration).count() / 1000};
return estimatedContentLength;
}
}
std::unique_ptr<IResourceHandler>
createTranscodeResourceHandler(const std::filesystem::path& trackPath, const TranscodeParameters& parameters)
createTranscodeResourceHandler(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters, bool estimateContentLength)
{
return std::make_unique<TranscodeResourceHandler>(trackPath, parameters);
return std::make_unique<TranscodeResourceHandler>(inputFileParameters, transcodeParameters, estimateContentLength);
}
// TODO set some nice HTTP return code
TranscodeResourceHandler::TranscodeResourceHandler(const std::filesystem::path& trackPath, const TranscodeParameters& parameters)
: _transcoder {trackPath, parameters}
TranscodeResourceHandler::TranscodeResourceHandler(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters, bool estimateContentLength)
: _estimatedContentLength {estimateContentLength ? std::make_optional(doEstimateContentLength(inputFileParameters, transcodeParameters)) : std::nullopt}
, _transcoder {inputFileParameters, transcodeParameters}
{
if (_estimatedContentLength)
LMS_LOG(TRANSCODE, DEBUG) << "Estimated content length = " << *_estimatedContentLength;
}
Wt::Http::ResponseContinuation*
TranscodeResourceHandler::processRequest(const Wt::Http::Request& /*request*/, Wt::Http::Response& response)
{
if (_estimatedContentLength)
response.setContentLength(*_estimatedContentLength);
response.setMimeType(_transcoder.getOutputMimeType());
if (_nbBytesReady > 0)
if (_bytesReadyCount > 0)
{
response.out().write(reinterpret_cast<const char *>(&_buffer[0]), _nbBytesReady);
_nbBytesReady = 0;
response.out().write(reinterpret_cast<const char *>(&_buffer[0]), _bytesReadyCount);
_bytesReadyCount = 0;
_totalServedByteCount += _bytesReadyCount;
}
if (!_transcoder.finished())
@@ -52,13 +68,30 @@ namespace Av
continuation->waitForMoreData();
_transcoder.asyncRead(_buffer.data(), _buffer.size(), [=](std::size_t nbBytesRead)
{
assert(_nbBytesReady == 0);
_nbBytesReady = nbBytesRead;
assert(_bytesReadyCount == 0);
_bytesReadyCount = nbBytesRead;
continuation->haveMoreData();
});
return continuation;
}
else
{
// pad with 0 if necessary as duration may not be accurate
if (_estimatedContentLength && *_estimatedContentLength > _totalServedByteCount)
{
const std::size_t padSize {*_estimatedContentLength - _totalServedByteCount};
LMS_LOG(TRANSCODE, DEBUG) << "Adding " << padSize << " padding bytes";
for (std::size_t i {}; i < padSize; ++i)
response.out().put(0);
_totalServedByteCount += padSize;
}
LMS_LOG(TRANSCODE, DEBUG) << "Transcoding finished. Total served byte count = " << _totalServedByteCount;
}
return {};
}
@@ -21,6 +21,7 @@
#include <array>
#include <filesystem>
#include <optional>
#include "av/TranscodeParameters.hpp"
#include "utils/IResourceHandler.hpp"
@@ -28,19 +29,19 @@
namespace Av
{
class TranscodeResourceHandler final : public IResourceHandler
{
public:
TranscodeResourceHandler(const std::filesystem::path& trackPath, const TranscodeParameters& parameters);
TranscodeResourceHandler(const InputFileParameters& inputFileParameters, const TranscodeParameters& parameters, bool estimateContentLength);
private:
Wt::Http::ResponseContinuation* processRequest(const Wt::Http::Request& request, Wt::Http::Response& reponse) override;
static constexpr std::size_t _chunkSize {32768};
std::optional<std::size_t> _estimatedContentLength;
std::array<std::byte, _chunkSize> _buffer;
std::size_t _nbBytesReady {};
const std::filesystem::path _trackPath;
std::size_t _bytesReadyCount {};
std::size_t _totalServedByteCount {};
Transcoder _transcoder;
};
}
+20 -20
View File
@@ -30,7 +30,7 @@
namespace Av {
#define LOG(sev) LMS_LOG(TRANSCODE, sev) << "[" << _id << "] - "
#define LOG(sev) LMS_LOG(TRANSCODE, sev) << "[" << _debugId << "] - "
static std::atomic<size_t> globalId {};
static std::filesystem::path ffmpegPath;
@@ -43,10 +43,10 @@ Transcoder::init()
throw Exception {"File '" + ffmpegPath.string() + "' does not exist!"};
}
Transcoder::Transcoder(const std::filesystem::path& filePath, const TranscodeParameters& parameters)
: _id {globalId++}
, _filePath {filePath}
, _parameters {parameters}
Transcoder::Transcoder(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters)
: _debugId {globalId++}
, _inputFileParameters {inputFileParameters}
, _transcodeParameters {transcodeParameters}
{
start();
}
@@ -61,17 +61,17 @@ Transcoder::start()
try
{
if (!std::filesystem::exists(_filePath))
throw Exception {"File '" + _filePath.string() + "' does not exist!"};
else if (!std::filesystem::is_regular_file( _filePath) )
throw Exception {"File '" + _filePath.string() + "' is not regular!"};
if (!std::filesystem::exists(_inputFileParameters.trackPath))
throw Exception {"File '" + _inputFileParameters.trackPath.string() + "' does not exist!"};
else if (!std::filesystem::is_regular_file( _inputFileParameters.trackPath) )
throw Exception {"File '" + _inputFileParameters.trackPath.string() + "' is not regular!"};
}
catch (const std::filesystem::filesystem_error& e)
{
throw Exception {"File error '" + _filePath.string() + "': " + e.what()};
throw Exception {"File error '" + _inputFileParameters.trackPath.string() + "': " + e.what()};
}
LOG(INFO) << "Transcoding file '" << _filePath.string() << "'";
LOG(INFO) << "Transcoding file '" << _inputFileParameters.trackPath.string() << "'";
std::vector<std::string> args;
@@ -90,22 +90,22 @@ Transcoder::start()
args.emplace_back("-ss");
std::ostringstream oss;
oss << std::fixed << std::showpoint << std::setprecision(3) << (_parameters.offset.count() / float {1000});
oss << std::fixed << std::showpoint << std::setprecision(3) << (_transcodeParameters.offset.count() / float {1000});
args.emplace_back(oss.str());
}
// Input file
args.emplace_back("-i");
args.emplace_back(_filePath.string());
args.emplace_back(_inputFileParameters.trackPath.string());
// Stream mapping, if set
if (_parameters.stream)
if (_transcodeParameters.stream)
{
args.emplace_back("-map");
args.emplace_back("0:" + std::to_string(*_parameters.stream));
args.emplace_back("0:" + std::to_string(*_transcodeParameters.stream));
}
if (_parameters.stripMetadata)
if (_transcodeParameters.stripMetadata)
{
// Strip metadata
args.emplace_back("-map_metadata");
@@ -117,10 +117,10 @@ Transcoder::start()
// Output bitrates
args.emplace_back("-b:a");
args.emplace_back(std::to_string(_parameters.bitrate));
args.emplace_back(std::to_string(_transcodeParameters.bitrate));
// Codecs and formats
switch (_parameters.format)
switch (_transcodeParameters.format)
{
case Format::MP3:
args.emplace_back("-f");
@@ -156,10 +156,10 @@ Transcoder::start()
break;
default:
throw Exception {"Unhandled format (" + std::to_string(static_cast<int>(_parameters.format)) + ")"};
throw Exception {"Unhandled format (" + std::to_string(static_cast<int>(_transcodeParameters.format)) + ")"};
}
_outputMimeType = formatToMimetype(_parameters.format);
_outputMimeType = formatToMimetype(_transcodeParameters.format);
args.emplace_back("pipe:1");
+5 -5
View File
@@ -32,7 +32,7 @@ namespace Av
class Transcoder
{
public:
Transcoder(const std::filesystem::path& file, const TranscodeParameters& parameters);
Transcoder(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters);
~Transcoder();
Transcoder(const Transcoder&) = delete;
@@ -49,7 +49,7 @@ namespace Av
std::size_t readSome(std::byte* buffer, std::size_t bufferSize);
const std::string& getOutputMimeType() const { return _outputMimeType; }
const TranscodeParameters& getParameters() const { return _parameters; }
const TranscodeParameters& getParameters() const { return _transcodeParameters; }
bool finished() const;
@@ -58,9 +58,9 @@ namespace Av
void start();
const std::size_t _id {};
const std::filesystem::path _filePath;
const TranscodeParameters _parameters;
const std::size_t _debugId {};
const InputFileParameters _inputFileParameters;
const TranscodeParameters _transcodeParameters;
std::unique_ptr<IChildProcess> _childProcess;