Subonsic API: added estimateContentLength parameter support for stream endpoint. fixes #244
This commit is contained in:
@@ -18,32 +18,48 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "TranscodeResourceHandler.hpp"
|
#include "TranscodeResourceHandler.hpp"
|
||||||
|
#include "utils/Logger.hpp"
|
||||||
|
|
||||||
namespace Av
|
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>
|
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
|
// TODO set some nice HTTP return code
|
||||||
|
|
||||||
TranscodeResourceHandler::TranscodeResourceHandler(const std::filesystem::path& trackPath, const TranscodeParameters& parameters)
|
TranscodeResourceHandler::TranscodeResourceHandler(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters, bool estimateContentLength)
|
||||||
: _transcoder {trackPath, parameters}
|
: _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*
|
Wt::Http::ResponseContinuation*
|
||||||
TranscodeResourceHandler::processRequest(const Wt::Http::Request& /*request*/, Wt::Http::Response& response)
|
TranscodeResourceHandler::processRequest(const Wt::Http::Request& /*request*/, Wt::Http::Response& response)
|
||||||
{
|
{
|
||||||
|
if (_estimatedContentLength)
|
||||||
|
response.setContentLength(*_estimatedContentLength);
|
||||||
response.setMimeType(_transcoder.getOutputMimeType());
|
response.setMimeType(_transcoder.getOutputMimeType());
|
||||||
|
|
||||||
if (_nbBytesReady > 0)
|
if (_bytesReadyCount > 0)
|
||||||
{
|
{
|
||||||
response.out().write(reinterpret_cast<const char *>(&_buffer[0]), _nbBytesReady);
|
response.out().write(reinterpret_cast<const char *>(&_buffer[0]), _bytesReadyCount);
|
||||||
_nbBytesReady = 0;
|
_bytesReadyCount = 0;
|
||||||
|
_totalServedByteCount += _bytesReadyCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_transcoder.finished())
|
if (!_transcoder.finished())
|
||||||
@@ -52,13 +68,30 @@ namespace Av
|
|||||||
continuation->waitForMoreData();
|
continuation->waitForMoreData();
|
||||||
_transcoder.asyncRead(_buffer.data(), _buffer.size(), [=](std::size_t nbBytesRead)
|
_transcoder.asyncRead(_buffer.data(), _buffer.size(), [=](std::size_t nbBytesRead)
|
||||||
{
|
{
|
||||||
assert(_nbBytesReady == 0);
|
assert(_bytesReadyCount == 0);
|
||||||
_nbBytesReady = nbBytesRead;
|
_bytesReadyCount = nbBytesRead;
|
||||||
continuation->haveMoreData();
|
continuation->haveMoreData();
|
||||||
});
|
});
|
||||||
|
|
||||||
return continuation;
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "av/TranscodeParameters.hpp"
|
#include "av/TranscodeParameters.hpp"
|
||||||
#include "utils/IResourceHandler.hpp"
|
#include "utils/IResourceHandler.hpp"
|
||||||
@@ -28,19 +29,19 @@
|
|||||||
|
|
||||||
namespace Av
|
namespace Av
|
||||||
{
|
{
|
||||||
|
|
||||||
class TranscodeResourceHandler final : public IResourceHandler
|
class TranscodeResourceHandler final : public IResourceHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TranscodeResourceHandler(const std::filesystem::path& trackPath, const TranscodeParameters& parameters);
|
TranscodeResourceHandler(const InputFileParameters& inputFileParameters, const TranscodeParameters& parameters, bool estimateContentLength);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Wt::Http::ResponseContinuation* processRequest(const Wt::Http::Request& request, Wt::Http::Response& reponse) override;
|
Wt::Http::ResponseContinuation* processRequest(const Wt::Http::Request& request, Wt::Http::Response& reponse) override;
|
||||||
|
|
||||||
static constexpr std::size_t _chunkSize {32768};
|
static constexpr std::size_t _chunkSize {32768};
|
||||||
|
std::optional<std::size_t> _estimatedContentLength;
|
||||||
std::array<std::byte, _chunkSize> _buffer;
|
std::array<std::byte, _chunkSize> _buffer;
|
||||||
std::size_t _nbBytesReady {};
|
std::size_t _bytesReadyCount {};
|
||||||
const std::filesystem::path _trackPath;
|
std::size_t _totalServedByteCount {};
|
||||||
Transcoder _transcoder;
|
Transcoder _transcoder;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
namespace Av {
|
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::atomic<size_t> globalId {};
|
||||||
static std::filesystem::path ffmpegPath;
|
static std::filesystem::path ffmpegPath;
|
||||||
@@ -43,10 +43,10 @@ Transcoder::init()
|
|||||||
throw Exception {"File '" + ffmpegPath.string() + "' does not exist!"};
|
throw Exception {"File '" + ffmpegPath.string() + "' does not exist!"};
|
||||||
}
|
}
|
||||||
|
|
||||||
Transcoder::Transcoder(const std::filesystem::path& filePath, const TranscodeParameters& parameters)
|
Transcoder::Transcoder(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters)
|
||||||
: _id {globalId++}
|
: _debugId {globalId++}
|
||||||
, _filePath {filePath}
|
, _inputFileParameters {inputFileParameters}
|
||||||
, _parameters {parameters}
|
, _transcodeParameters {transcodeParameters}
|
||||||
{
|
{
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
@@ -61,17 +61,17 @@ Transcoder::start()
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!std::filesystem::exists(_filePath))
|
if (!std::filesystem::exists(_inputFileParameters.trackPath))
|
||||||
throw Exception {"File '" + _filePath.string() + "' does not exist!"};
|
throw Exception {"File '" + _inputFileParameters.trackPath.string() + "' does not exist!"};
|
||||||
else if (!std::filesystem::is_regular_file( _filePath) )
|
else if (!std::filesystem::is_regular_file( _inputFileParameters.trackPath) )
|
||||||
throw Exception {"File '" + _filePath.string() + "' is not regular!"};
|
throw Exception {"File '" + _inputFileParameters.trackPath.string() + "' is not regular!"};
|
||||||
}
|
}
|
||||||
catch (const std::filesystem::filesystem_error& e)
|
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;
|
std::vector<std::string> args;
|
||||||
|
|
||||||
@@ -90,22 +90,22 @@ Transcoder::start()
|
|||||||
args.emplace_back("-ss");
|
args.emplace_back("-ss");
|
||||||
|
|
||||||
std::ostringstream oss;
|
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());
|
args.emplace_back(oss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input file
|
// Input file
|
||||||
args.emplace_back("-i");
|
args.emplace_back("-i");
|
||||||
args.emplace_back(_filePath.string());
|
args.emplace_back(_inputFileParameters.trackPath.string());
|
||||||
|
|
||||||
// Stream mapping, if set
|
// Stream mapping, if set
|
||||||
if (_parameters.stream)
|
if (_transcodeParameters.stream)
|
||||||
{
|
{
|
||||||
args.emplace_back("-map");
|
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
|
// Strip metadata
|
||||||
args.emplace_back("-map_metadata");
|
args.emplace_back("-map_metadata");
|
||||||
@@ -117,10 +117,10 @@ Transcoder::start()
|
|||||||
|
|
||||||
// Output bitrates
|
// Output bitrates
|
||||||
args.emplace_back("-b:a");
|
args.emplace_back("-b:a");
|
||||||
args.emplace_back(std::to_string(_parameters.bitrate));
|
args.emplace_back(std::to_string(_transcodeParameters.bitrate));
|
||||||
|
|
||||||
// Codecs and formats
|
// Codecs and formats
|
||||||
switch (_parameters.format)
|
switch (_transcodeParameters.format)
|
||||||
{
|
{
|
||||||
case Format::MP3:
|
case Format::MP3:
|
||||||
args.emplace_back("-f");
|
args.emplace_back("-f");
|
||||||
@@ -156,10 +156,10 @@ Transcoder::start()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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");
|
args.emplace_back("pipe:1");
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace Av
|
|||||||
class Transcoder
|
class Transcoder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Transcoder(const std::filesystem::path& file, const TranscodeParameters& parameters);
|
Transcoder(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters);
|
||||||
~Transcoder();
|
~Transcoder();
|
||||||
|
|
||||||
Transcoder(const Transcoder&) = delete;
|
Transcoder(const Transcoder&) = delete;
|
||||||
@@ -49,7 +49,7 @@ namespace Av
|
|||||||
std::size_t readSome(std::byte* buffer, std::size_t bufferSize);
|
std::size_t readSome(std::byte* buffer, std::size_t bufferSize);
|
||||||
|
|
||||||
const std::string& getOutputMimeType() const { return _outputMimeType; }
|
const std::string& getOutputMimeType() const { return _outputMimeType; }
|
||||||
const TranscodeParameters& getParameters() const { return _parameters; }
|
const TranscodeParameters& getParameters() const { return _transcodeParameters; }
|
||||||
|
|
||||||
bool finished() const;
|
bool finished() const;
|
||||||
|
|
||||||
@@ -58,9 +58,9 @@ namespace Av
|
|||||||
|
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
const std::size_t _id {};
|
const std::size_t _debugId {};
|
||||||
const std::filesystem::path _filePath;
|
const InputFileParameters _inputFileParameters;
|
||||||
const TranscodeParameters _parameters;
|
const TranscodeParameters _transcodeParameters;
|
||||||
|
|
||||||
std::unique_ptr<IChildProcess> _childProcess;
|
std::unique_ptr<IChildProcess> _childProcess;
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "Types.hpp"
|
#include "Types.hpp"
|
||||||
|
|
||||||
namespace Av
|
namespace Av
|
||||||
{
|
{
|
||||||
|
struct InputFileParameters
|
||||||
|
{
|
||||||
|
std::filesystem::path trackPath;
|
||||||
|
std::chrono::milliseconds duration;
|
||||||
|
};
|
||||||
|
|
||||||
struct TranscodeParameters
|
struct TranscodeParameters
|
||||||
{
|
{
|
||||||
Format format;
|
Format format;
|
||||||
|
|||||||
@@ -19,15 +19,15 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "utils/IResourceHandler.hpp"
|
#include "utils/IResourceHandler.hpp"
|
||||||
|
|
||||||
namespace Av
|
namespace Av
|
||||||
{
|
{
|
||||||
|
struct InputFileParameters;
|
||||||
struct TranscodeParameters;
|
struct TranscodeParameters;
|
||||||
|
|
||||||
std::unique_ptr<IResourceHandler> createTranscodeResourceHandler(const std::filesystem::path& trackPath, const TranscodeParameters& parameters);
|
std::unique_ptr<IResourceHandler> createTranscodeResourceHandler(const InputFileParameters& inputFileParameters, const TranscodeParameters& parameters, bool estimateContentLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,8 +54,9 @@ userTranscodeFormatToAvFormat(AudioFormat format)
|
|||||||
|
|
||||||
struct StreamParameters
|
struct StreamParameters
|
||||||
{
|
{
|
||||||
std::filesystem::path trackPath;
|
Av::InputFileParameters inputFileParameters;
|
||||||
std::optional<Av::TranscodeParameters> transcodeParameters;
|
std::optional<Av::TranscodeParameters> transcodeParameters;
|
||||||
|
bool estimateContentLength {};
|
||||||
};
|
};
|
||||||
|
|
||||||
static
|
static
|
||||||
@@ -68,9 +69,12 @@ getStreamParameters(RequestContext& context)
|
|||||||
// Optional params
|
// Optional params
|
||||||
std::optional<std::size_t> maxBitRate {getParameterAs<std::size_t>(context.parameters, "maxBitRate")};
|
std::optional<std::size_t> maxBitRate {getParameterAs<std::size_t>(context.parameters, "maxBitRate")};
|
||||||
std::optional<std::string> format {getParameterAs<std::string>(context.parameters, "format")};
|
std::optional<std::string> format {getParameterAs<std::string>(context.parameters, "format")};
|
||||||
|
bool estimateContentLength {getParameterAs<bool>(context.parameters, "estimateContentLength").value_or(false)};
|
||||||
|
|
||||||
StreamParameters parameters;
|
StreamParameters parameters;
|
||||||
|
|
||||||
|
parameters.estimateContentLength = estimateContentLength;
|
||||||
|
|
||||||
auto transaction {context.dbSession.createSharedTransaction()};
|
auto transaction {context.dbSession.createSharedTransaction()};
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -78,7 +82,8 @@ getStreamParameters(RequestContext& context)
|
|||||||
if (!track)
|
if (!track)
|
||||||
throw RequestedDataNotFoundError {};
|
throw RequestedDataNotFoundError {};
|
||||||
|
|
||||||
parameters.trackPath = track->getPath();
|
parameters.inputFileParameters.trackPath = track->getPath();
|
||||||
|
parameters.inputFileParameters.duration = track->getDuration();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -155,9 +160,9 @@ handleStream(RequestContext& context, const Wt::Http::Request& request, Wt::Http
|
|||||||
{
|
{
|
||||||
StreamParameters streamParameters {getStreamParameters(context)};
|
StreamParameters streamParameters {getStreamParameters(context)};
|
||||||
if (streamParameters.transcodeParameters)
|
if (streamParameters.transcodeParameters)
|
||||||
resourceHandler = Av::createTranscodeResourceHandler(streamParameters.trackPath, *streamParameters.transcodeParameters);
|
resourceHandler = Av::createTranscodeResourceHandler(streamParameters.inputFileParameters, *streamParameters.transcodeParameters, streamParameters.estimateContentLength);
|
||||||
else
|
else
|
||||||
resourceHandler = createFileResourceHandler(streamParameters.trackPath);
|
resourceHandler = createFileResourceHandler(streamParameters.inputFileParameters.trackPath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -136,17 +136,4 @@ namespace StringUtils
|
|||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
|
||||||
std::optional<bool>
|
|
||||||
readAs(std::string_view str)
|
|
||||||
{
|
|
||||||
if (str == "true")
|
|
||||||
return true;
|
|
||||||
else if (str == "false")
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,9 +58,5 @@ namespace StringUtils
|
|||||||
template<>
|
template<>
|
||||||
std::optional<Database::TrackListId>
|
std::optional<Database::TrackListId>
|
||||||
readAs(std::string_view str);
|
readAs(std::string_view str);
|
||||||
|
|
||||||
template<>
|
|
||||||
std::optional<bool>
|
|
||||||
readAs(std::string_view str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,18 @@ readAs(std::string_view str)
|
|||||||
return std::string {str};
|
return std::string {str};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::optional<bool>
|
||||||
|
readAs(std::string_view str)
|
||||||
|
{
|
||||||
|
if (str == "1" || str == "true")
|
||||||
|
return true;
|
||||||
|
else if (str == "0" || str == "false")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string>
|
std::vector<std::string>
|
||||||
splitStringCopy(std::string_view string, std::string_view separators)
|
splitStringCopy(std::string_view string, std::string_view separators)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -85,6 +85,11 @@ template<>
|
|||||||
std::optional<std::string>
|
std::optional<std::string>
|
||||||
readAs(std::string_view str);
|
readAs(std::string_view str);
|
||||||
|
|
||||||
|
template<>
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<bool>
|
||||||
|
readAs(std::string_view str);
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
std::string
|
std::string
|
||||||
replaceInString(std::string_view str, const std::string& from, const std::string& to);
|
replaceInString(std::string_view str, const std::string& from, const std::string& to);
|
||||||
|
|||||||
@@ -109,3 +109,12 @@ TEST(StringUtils, escapeString)
|
|||||||
EXPECT_EQ(StringUtils::escapeString("**||", "*|", '_'), "_*_*_|_|");
|
EXPECT_EQ(StringUtils::escapeString("**||", "*|", '_'), "_*_*_|_|");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(StringUtils, readAs)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(StringUtils::readAs<bool>("true"), true);
|
||||||
|
EXPECT_EQ(StringUtils::readAs<bool>("1"), true);
|
||||||
|
EXPECT_EQ(StringUtils::readAs<bool>("false"), false);
|
||||||
|
EXPECT_EQ(StringUtils::readAs<bool>("0"), false);
|
||||||
|
EXPECT_EQ(StringUtils::readAs<bool>("foo"), std::nullopt);
|
||||||
|
EXPECT_EQ(StringUtils::readAs<bool>(""), std::nullopt);
|
||||||
|
}
|
||||||
|
|||||||
@@ -118,59 +118,62 @@ readParameterAs(const Wt::Http::Request& request, const std::string& parameterNa
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TranscodeParameters
|
namespace
|
||||||
{
|
{
|
||||||
std::filesystem::path file;
|
struct TranscodeParameters
|
||||||
Av::TranscodeParameters transcodeParameters;
|
|
||||||
};
|
|
||||||
|
|
||||||
static
|
|
||||||
std::optional<TranscodeParameters>
|
|
||||||
readTranscodeParameters(const Wt::Http::Request& request)
|
|
||||||
{
|
|
||||||
TranscodeParameters parameters;
|
|
||||||
|
|
||||||
// mandatory parameters
|
|
||||||
const std::optional<Database::TrackId> trackId {readParameterAs<Database::TrackId::ValueType>(request, "trackid")};
|
|
||||||
const auto format {readParameterAs<Database::AudioFormat>(request, "format")};
|
|
||||||
const auto bitrate {readParameterAs<Database::Bitrate>(request, "bitrate")};
|
|
||||||
|
|
||||||
if (!trackId || !format || !bitrate)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
const std::optional<Av::Format> avFormat {AudioFormatToAvFormat(*format)};
|
|
||||||
if (!avFormat)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
// optional parameter
|
|
||||||
std::size_t offset {readParameterAs<std::size_t>(request, "offset").value_or(0)};
|
|
||||||
|
|
||||||
std::filesystem::path trackPath;
|
|
||||||
{
|
{
|
||||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
Av::InputFileParameters inputFileParameters;
|
||||||
|
Av::TranscodeParameters transcodeParameters;
|
||||||
|
};
|
||||||
|
|
||||||
const Database::Track::pointer track {Database::Track::find(LmsApp->getDbSession(), *trackId)};
|
std::optional<TranscodeParameters>
|
||||||
if (!track)
|
readTranscodeParameters(const Wt::Http::Request& request)
|
||||||
{
|
{
|
||||||
LOG(ERROR) << "Missing track";
|
TranscodeParameters parameters;
|
||||||
|
|
||||||
|
// mandatory parameters
|
||||||
|
const std::optional<Database::TrackId> trackId {readParameterAs<Database::TrackId::ValueType>(request, "trackid")};
|
||||||
|
const auto format {readParameterAs<Database::AudioFormat>(request, "format")};
|
||||||
|
const auto bitrate {readParameterAs<Database::Bitrate>(request, "bitrate")};
|
||||||
|
|
||||||
|
if (!trackId || !format || !bitrate)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
|
||||||
|
|
||||||
parameters.file = track->getPath();
|
|
||||||
|
|
||||||
if (!Database::isAudioBitrateAllowed(*bitrate))
|
if (!Database::isAudioBitrateAllowed(*bitrate))
|
||||||
{
|
{
|
||||||
LOG(ERROR) << "Bitrate '" << *bitrate << "' is not allowed";
|
LOG(ERROR) << "Bitrate '" << *bitrate << "' is not allowed";
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::optional<Av::Format> avFormat {AudioFormatToAvFormat(*format)};
|
||||||
|
if (!avFormat)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
// optional parameter
|
||||||
|
std::size_t offset {readParameterAs<std::size_t>(request, "offset").value_or(0)};
|
||||||
|
|
||||||
|
std::filesystem::path trackPath;
|
||||||
|
{
|
||||||
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
|
||||||
|
const Database::Track::pointer track {Database::Track::find(LmsApp->getDbSession(), *trackId)};
|
||||||
|
if (!track)
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "Missing track";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters.inputFileParameters.trackPath = track->getPath();
|
||||||
|
parameters.inputFileParameters.duration = track->getDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters.transcodeParameters.stripMetadata = true;
|
||||||
|
parameters.transcodeParameters.format = *avFormat;
|
||||||
|
parameters.transcodeParameters.bitrate = *bitrate;
|
||||||
|
parameters.transcodeParameters.offset = std::chrono::seconds {offset};
|
||||||
|
|
||||||
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters.transcodeParameters.stripMetadata = true;
|
|
||||||
parameters.transcodeParameters.format = *avFormat;
|
|
||||||
parameters.transcodeParameters.bitrate = *bitrate;
|
|
||||||
parameters.transcodeParameters.offset = std::chrono::seconds {offset};
|
|
||||||
|
|
||||||
return parameters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -186,7 +189,7 @@ AudioTranscodeResource::handleRequest(const Wt::Http::Request& request,
|
|||||||
{
|
{
|
||||||
const std::optional<TranscodeParameters>& parameters {readTranscodeParameters(request)};
|
const std::optional<TranscodeParameters>& parameters {readTranscodeParameters(request)};
|
||||||
if (parameters)
|
if (parameters)
|
||||||
resourceHandler = Av::createTranscodeResourceHandler(parameters->file, parameters->transcodeParameters);
|
resourceHandler = Av::createTranscodeResourceHandler(parameters->inputFileParameters, parameters->transcodeParameters, false /* estimate content length */);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user