| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- /*
- 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/streaming/media_streaming_audio_track.h"
- #include "media/streaming/media_streaming_utility.h"
- #include "media/audio/media_audio.h"
- #include "media/audio/media_child_ffmpeg_loader.h"
- #include "media/player/media_player_instance.h"
- namespace Media {
- namespace Streaming {
- AudioTrack::AudioTrack(
- const PlaybackOptions &options,
- Stream &&stream,
- AudioMsgId audioId,
- FnMut<void(const Information &)> ready,
- Fn<void(Error)> error)
- : _options(options)
- , _stream(std::move(stream))
- , _audioId(audioId)
- , _ready(std::move(ready))
- , _error(std::move(error))
- , _playPosition(options.position) {
- Expects(_stream.duration > 1);
- Expects(_stream.duration != kDurationUnavailable); // Not supported.
- Expects(_ready != nullptr);
- Expects(_error != nullptr);
- Expects(_audioId.externalPlayId() != 0);
- }
- int AudioTrack::streamIndex() const {
- // Thread-safe, because _stream.index is immutable.
- return _stream.index;
- }
- AVRational AudioTrack::streamTimeBase() const {
- return _stream.timeBase;
- }
- crl::time AudioTrack::streamDuration() const {
- return _stream.duration;
- }
- void AudioTrack::process(std::vector<FFmpeg::Packet> &&packets) {
- if (packets.empty()) {
- return;
- } else if (packets.front().empty()) {
- Assert(packets.size() == 1);
- _readTillEnd = true;
- }
- for (auto i = begin(packets), e = end(packets); i != e; ++i) {
- if (initialized()) {
- mixerEnqueue(gsl::make_span(&*i, (e - i)));
- break;
- } else if (!tryReadFirstFrame(std::move(*i))) {
- _error(Error::InvalidData);
- break;
- }
- }
- }
- void AudioTrack::waitForData() {
- if (initialized()) {
- mixerForceToBuffer();
- }
- }
- bool AudioTrack::initialized() const {
- return !_ready;
- }
- bool AudioTrack::tryReadFirstFrame(FFmpeg::Packet &&packet) {
- if (ProcessPacket(_stream, std::move(packet)).failed()) {
- return false;
- }
- while (true) {
- if (const auto error = ReadNextFrame(_stream)) {
- if (error.code() == AVERROR_EOF) {
- if (!_initialSkippingFrame) {
- return false;
- }
- // Return the last valid frame if we seek too far.
- _stream.decodedFrame = std::move(_initialSkippingFrame);
- return processFirstFrame();
- } else if (error.code() != AVERROR(EAGAIN) || _readTillEnd) {
- return false;
- } else {
- // Waiting for more packets.
- return true;
- }
- } else if (!fillStateFromFrame()) {
- return false;
- } else if (_startedPosition >= _options.position) {
- return processFirstFrame();
- }
- // Seek was with AVSEEK_FLAG_BACKWARD so first we get old frames.
- // Try skipping frames until one is after the requested position.
- std::swap(_initialSkippingFrame, _stream.decodedFrame);
- if (!_stream.decodedFrame) {
- _stream.decodedFrame = FFmpeg::MakeFramePointer();
- }
- }
- }
- bool AudioTrack::processFirstFrame() {
- if (!FFmpeg::FrameHasData(_stream.decodedFrame.get())) {
- return false;
- }
- mixerInit();
- callReady();
- return true;
- }
- bool AudioTrack::fillStateFromFrame() {
- const auto position = FramePosition(_stream);
- if (position == kTimeUnknown) {
- return false;
- }
- _startedPosition = position;
- return true;
- }
- void AudioTrack::mixerInit() {
- Expects(!initialized());
- auto data = std::make_unique<ExternalSoundData>();
- data->frame = std::move(_stream.decodedFrame);
- data->codec = std::move(_stream.codec);
- data->duration = _stream.duration;
- data->speed = _options.speed;
- Media::Player::mixer()->play(
- _audioId,
- std::move(data),
- _startedPosition);
- }
- void AudioTrack::callReady() {
- Expects(_ready != nullptr);
- auto data = AudioInformation();
- data.state.duration = _stream.duration;
- data.state.position = _startedPosition;
- data.state.receivedTill = _readTillEnd
- ? _stream.duration
- : _startedPosition;
- base::take(_ready)({ VideoInformation(), data });
- }
- void AudioTrack::mixerEnqueue(gsl::span<FFmpeg::Packet> packets) {
- Media::Player::mixer()->feedFromExternal({
- _audioId,
- packets
- });
- }
- void AudioTrack::mixerForceToBuffer() {
- Media::Player::mixer()->forceToBufferExternal(_audioId);
- }
- void AudioTrack::pause(crl::time time) {
- Expects(initialized());
- Media::Player::mixer()->pause(_audioId, true);
- }
- void AudioTrack::resume(crl::time time) {
- Expects(initialized());
- Media::Player::mixer()->resume(_audioId, true);
- }
- void AudioTrack::stop() {
- if (_audioId.externalPlayId()) {
- Media::Player::mixer()->stop(_audioId);
- }
- }
- void AudioTrack::setSpeed(float64 speed) {
- _options.speed = speed;
- Media::Player::mixer()->setSpeedFromExternal(_audioId, speed);
- }
- rpl::producer<> AudioTrack::waitingForData() const {
- return _waitingForData.events();
- }
- rpl::producer<crl::time> AudioTrack::playPosition() {
- Expects(_ready == nullptr);
- if (!_subscription) {
- _subscription = Media::Player::Updated(
- ) | rpl::start_with_next([=](const AudioMsgId &id) {
- using State = Media::Player::State;
- if (id != _audioId) {
- return;
- }
- const auto state = Media::Player::mixer()->currentState(
- _audioId.type());
- if (state.id != _audioId) {
- // #TODO streaming later muted by other
- return;
- } else switch (state.state) {
- case State::Stopped:
- case State::StoppedAtEnd:
- case State::PausedAtEnd:
- _playPosition.reset();
- return;
- case State::StoppedAtError:
- case State::StoppedAtStart:
- _error(Error::InvalidData);
- return;
- case State::Starting:
- case State::Playing:
- case State::Stopping:
- case State::Pausing:
- case State::Resuming:
- if (state.waitingForData) {
- _waitingForData.fire({});
- }
- _playPosition = std::clamp(
- crl::time((state.position * 1000 + (state.frequency / 2))
- / state.frequency),
- crl::time(0),
- _stream.duration - 1);
- return;
- case State::Paused:
- return;
- }
- });
- }
- return _playPosition.value();
- }
- AudioTrack::~AudioTrack() {
- stop();
- }
- } // namespace Streaming
- } // namespace Media
|