mirror of https://github.com/procxx/kepka.git
				
				
				
			
		
			
				
	
	
		
			135 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			C++
		
	
	
	
/*
 | 
						|
This file is part of Telegram Desktop,
 | 
						|
the official desktop application for the Telegram messaging service.
 | 
						|
 | 
						|
For license and copyright information please follow this link:
 | 
						|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
						|
*/
 | 
						|
#include "media/audio/media_child_ffmpeg_loader.h"
 | 
						|
 | 
						|
#include "core/crash_reports.h"
 | 
						|
 | 
						|
namespace Media {
 | 
						|
namespace {
 | 
						|
 | 
						|
constexpr AVSampleFormat AudioToFormat = AV_SAMPLE_FMT_S16;
 | 
						|
constexpr int64_t AudioToChannelLayout = AV_CH_LAYOUT_STEREO;
 | 
						|
constexpr int32 AudioToChannels = 2;
 | 
						|
 | 
						|
bool IsPlanarFormat(int format) {
 | 
						|
	return (format == AV_SAMPLE_FMT_U8P)
 | 
						|
		|| (format == AV_SAMPLE_FMT_S16P)
 | 
						|
		|| (format == AV_SAMPLE_FMT_S32P)
 | 
						|
		|| (format == AV_SAMPLE_FMT_FLTP)
 | 
						|
		|| (format == AV_SAMPLE_FMT_DBLP)
 | 
						|
		|| (format == AV_SAMPLE_FMT_S64P);
 | 
						|
}
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
ChildFFMpegLoader::ChildFFMpegLoader(
 | 
						|
	std::unique_ptr<ExternalSoundData> &&data)
 | 
						|
: AbstractAudioFFMpegLoader(
 | 
						|
	FileLocation(),
 | 
						|
	QByteArray(),
 | 
						|
	bytes::vector())
 | 
						|
, _parentData(std::move(data)) {
 | 
						|
	Expects(_parentData->codec != nullptr);
 | 
						|
}
 | 
						|
 | 
						|
bool ChildFFMpegLoader::open(crl::time positionMs) {
 | 
						|
	return initUsingContext(
 | 
						|
		_parentData->codec.get(),
 | 
						|
		_parentData->length,
 | 
						|
		_parentData->frequency);
 | 
						|
}
 | 
						|
 | 
						|
AudioPlayerLoader::ReadResult ChildFFMpegLoader::readFromInitialFrame(
 | 
						|
		QByteArray &result,
 | 
						|
		int64 &samplesAdded) {
 | 
						|
	if (!_parentData->frame) {
 | 
						|
		return ReadResult::Wait;
 | 
						|
	}
 | 
						|
	return replaceFrameAndRead(
 | 
						|
		base::take(_parentData->frame),
 | 
						|
		result,
 | 
						|
		samplesAdded);
 | 
						|
}
 | 
						|
 | 
						|
AudioPlayerLoader::ReadResult ChildFFMpegLoader::readMore(
 | 
						|
	QByteArray & result,
 | 
						|
	int64 & samplesAdded) {
 | 
						|
	const auto initialFrameResult = readFromInitialFrame(
 | 
						|
		result,
 | 
						|
		samplesAdded);
 | 
						|
	if (initialFrameResult != ReadResult::Wait) {
 | 
						|
		return initialFrameResult;
 | 
						|
	}
 | 
						|
 | 
						|
	const auto readResult = readFromReadyContext(
 | 
						|
		_parentData->codec.get(),
 | 
						|
		result,
 | 
						|
		samplesAdded);
 | 
						|
	if (readResult != ReadResult::Wait) {
 | 
						|
		return readResult;
 | 
						|
	}
 | 
						|
 | 
						|
	if (_queue.empty()) {
 | 
						|
		return _eofReached ? ReadResult::EndOfFile : ReadResult::Wait;
 | 
						|
	}
 | 
						|
 | 
						|
	auto packet = std::move(_queue.front());
 | 
						|
	_queue.pop_front();
 | 
						|
 | 
						|
	_eofReached = packet.empty();
 | 
						|
	if (_eofReached) {
 | 
						|
		avcodec_send_packet(_parentData->codec.get(), nullptr); // drain
 | 
						|
		return ReadResult::Ok;
 | 
						|
	}
 | 
						|
 | 
						|
	auto res = avcodec_send_packet(
 | 
						|
		_parentData->codec.get(),
 | 
						|
		&packet.fields());
 | 
						|
	if (res < 0) {
 | 
						|
		char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
 | 
						|
		LOG(("Audio Error: Unable to avcodec_send_packet() file '%1', "
 | 
						|
			"data size '%2', error %3, %4"
 | 
						|
			).arg(_file.name()
 | 
						|
			).arg(_data.size()
 | 
						|
			).arg(res
 | 
						|
			).arg(av_make_error_string(err, sizeof(err), res)));
 | 
						|
		// There is a sample voice message where skipping such packet
 | 
						|
		// results in a crash (read_access to nullptr) in swr_convert().
 | 
						|
		if (res == AVERROR_INVALIDDATA) {
 | 
						|
			return ReadResult::NotYet; // try to skip bad packet
 | 
						|
		}
 | 
						|
		return ReadResult::Error;
 | 
						|
	}
 | 
						|
	return ReadResult::Ok;
 | 
						|
}
 | 
						|
 | 
						|
void ChildFFMpegLoader::enqueuePackets(
 | 
						|
		std::deque<FFmpeg::Packet> &&packets) {
 | 
						|
	if (_queue.empty()) {
 | 
						|
		_queue = std::move(packets);
 | 
						|
	} else {
 | 
						|
		_queue.insert(
 | 
						|
			end(_queue),
 | 
						|
			std::make_move_iterator(packets.begin()),
 | 
						|
			std::make_move_iterator(packets.end()));
 | 
						|
	}
 | 
						|
	packets.clear();
 | 
						|
}
 | 
						|
 | 
						|
void ChildFFMpegLoader::setForceToBuffer(bool force) {
 | 
						|
	_forceToBuffer = force;
 | 
						|
}
 | 
						|
 | 
						|
bool ChildFFMpegLoader::forceToBuffer() const {
 | 
						|
	return _forceToBuffer;
 | 
						|
}
 | 
						|
 | 
						|
ChildFFMpegLoader::~ChildFFMpegLoader() = default;
 | 
						|
 | 
						|
} // namespace Media
 |