123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- // Copyright 2014 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "media/cast/test/fake_media_source.h"
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/files/scoped_file.h"
- #include "base/logging.h"
- #include "base/rand_util.h"
- #include "base/strings/string_number_conversions.h"
- #include "build/build_config.h"
- #include "media/base/audio_buffer.h"
- #include "media/base/audio_bus.h"
- #include "media/base/audio_fifo.h"
- #include "media/base/audio_timestamp_helper.h"
- #include "media/base/media.h"
- #include "media/base/video_frame.h"
- #include "media/base/video_util.h"
- #include "media/cast/cast_sender.h"
- #include "media/cast/test/utility/audio_utility.h"
- #include "media/cast/test/utility/video_utility.h"
- #include "ui/gfx/geometry/size.h"
- #if BUILDFLAG(IS_WIN)
- #include <direct.h>
- #endif // BUILDFLAG(IS_WIN)
- #include "media/ffmpeg/ffmpeg_common.h"
- #include "media/ffmpeg/ffmpeg_decoding_loop.h"
- #include "media/ffmpeg/ffmpeg_deleters.h"
- #include "media/filters/ffmpeg_glue.h"
- #include "media/filters/in_memory_url_protocol.h"
- namespace {
- static const int kSoundFrequency = 440; // Frequency of sinusoid wave.
- static const float kSoundVolume = 0.10f;
- static const int kAudioFrameMs = 10; // Each audio frame is exactly 10ms.
- static const int kAudioPacketsPerSecond = 1000 / kAudioFrameMs;
- // Bounds for variable frame size mode.
- static const int kMinFakeFrameWidth = 60;
- static const int kMinFakeFrameHeight = 34;
- static const int kStartingFakeFrameWidth = 854;
- static const int kStartingFakeFrameHeight = 480;
- static const int kMaxFakeFrameWidth = 1280;
- static const int kMaxFakeFrameHeight = 720;
- static const int kMaxFrameSizeChangeMillis = 5000;
- void AVFreeFrame(AVFrame* frame) {
- av_frame_free(&frame);
- }
- base::TimeDelta PtsToTimeDelta(int64_t pts, const AVRational& time_base) {
- return pts * base::Seconds(1) * time_base.num / time_base.den;
- }
- int64_t TimeDeltaToPts(base::TimeDelta delta, const AVRational& time_base) {
- return static_cast<int64_t>(
- delta.InSecondsF() * time_base.den / time_base.num + 0.5 /* rounding */);
- }
- } // namespace
- namespace media {
- namespace cast {
- FakeMediaSource::FakeMediaSource(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner,
- const base::TickClock* clock,
- const FrameSenderConfig& audio_config,
- const FrameSenderConfig& video_config,
- bool keep_frames)
- : task_runner_(task_runner),
- output_audio_params_(AudioParameters::AUDIO_PCM_LINEAR,
- media::GuessChannelLayout(audio_config.channels),
- audio_config.rtp_timebase,
- audio_config.rtp_timebase / kAudioPacketsPerSecond),
- video_config_(video_config),
- keep_frames_(keep_frames),
- variable_frame_size_mode_(false),
- synthetic_count_(0),
- clock_(clock),
- audio_frame_count_(0),
- video_frame_count_(0),
- av_format_context_(nullptr),
- audio_stream_index_(-1),
- playback_rate_(1.0),
- video_stream_index_(-1),
- video_frame_rate_numerator_(video_config.max_frame_rate),
- video_frame_rate_denominator_(1),
- audio_algo_(&media_log_),
- video_first_pts_(0),
- video_first_pts_set_(false) {
- CHECK(output_audio_params_.IsValid());
- audio_bus_factory_ = std::make_unique<TestAudioBusFactory>(
- audio_config.channels, audio_config.rtp_timebase, kSoundFrequency,
- kSoundVolume);
- }
- FakeMediaSource::~FakeMediaSource() = default;
- void FakeMediaSource::SetSourceFile(const base::FilePath& video_file,
- int final_fps) {
- DCHECK(!video_file.empty());
- LOG(INFO) << "Source: " << video_file.value();
- if (!file_data_.Initialize(video_file)) {
- LOG(ERROR) << "Cannot load file.";
- return;
- }
- protocol_ = std::make_unique<InMemoryUrlProtocol>(file_data_.data(),
- file_data_.length(), false);
- glue_ = std::make_unique<FFmpegGlue>(protocol_.get());
- if (!glue_->OpenContext()) {
- LOG(ERROR) << "Cannot open file.";
- return;
- }
- // AVFormatContext is owned by the glue.
- av_format_context_ = glue_->format_context();
- if (avformat_find_stream_info(av_format_context_, NULL) < 0) {
- LOG(ERROR) << "Cannot find stream information.";
- return;
- }
- // Prepare FFmpeg decoders.
- for (unsigned int i = 0; i < av_format_context_->nb_streams; ++i) {
- AVStream* av_stream = av_format_context_->streams[i];
- std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext> av_codec_context(
- AVStreamToAVCodecContext(av_stream));
- if (!av_codec_context) {
- LOG(ERROR) << "Cannot get a codec context for the codec: "
- << av_stream->codecpar->codec_id;
- continue;
- }
- const AVCodec* av_codec = avcodec_find_decoder(av_codec_context->codec_id);
- if (!av_codec) {
- LOG(ERROR) << "Cannot find decoder for the codec: "
- << av_codec_context->codec_id;
- continue;
- }
- // Number of threads for decoding.
- av_codec_context->thread_count = 2;
- av_codec_context->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
- av_codec_context->request_sample_fmt = AV_SAMPLE_FMT_S16;
- if (avcodec_open2(av_codec_context.get(), av_codec, nullptr) < 0) {
- LOG(ERROR) << "Cannot open AVCodecContext for the codec: "
- << av_codec_context->codec_id;
- return;
- }
- if (av_codec->type == AVMEDIA_TYPE_AUDIO) {
- if (av_codec_context->sample_fmt == AV_SAMPLE_FMT_S16P) {
- LOG(ERROR) << "Audio format not supported.";
- continue;
- }
- ChannelLayout layout = ChannelLayoutToChromeChannelLayout(
- av_codec_context->channel_layout,
- av_codec_context->channels);
- if (layout == CHANNEL_LAYOUT_UNSUPPORTED) {
- LOG(ERROR) << "Unsupported audio channels layout.";
- continue;
- }
- if (audio_stream_index_ != -1) {
- LOG(WARNING) << "Found multiple audio streams.";
- }
- audio_stream_index_ = static_cast<int>(i);
- av_audio_context_ = std::move(av_codec_context);
- source_audio_params_.Reset(
- AudioParameters::AUDIO_PCM_LINEAR, layout,
- av_audio_context_->sample_rate,
- av_audio_context_->sample_rate / kAudioPacketsPerSecond);
- source_audio_params_.set_channels_for_discrete(
- av_audio_context_->channels);
- CHECK(source_audio_params_.IsValid());
- LOG(INFO) << "Source file has audio.";
- audio_decoding_loop_ =
- std::make_unique<FFmpegDecodingLoop>(av_audio_context_.get());
- } else if (av_codec->type == AVMEDIA_TYPE_VIDEO) {
- VideoPixelFormat format =
- AVPixelFormatToVideoPixelFormat(av_codec_context->pix_fmt);
- if (format != PIXEL_FORMAT_I420) {
- LOG(ERROR) << "Cannot handle non YV12 video format: " << format;
- continue;
- }
- if (video_stream_index_ != -1) {
- LOG(WARNING) << "Found multiple video streams.";
- }
- video_stream_index_ = static_cast<int>(i);
- av_video_context_ = std::move(av_codec_context);
- video_decoding_loop_ =
- std::make_unique<FFmpegDecodingLoop>(av_video_context_.get());
- if (final_fps > 0) {
- // If video is played at a manual speed audio needs to match.
- playback_rate_ = 1.0 * final_fps *
- av_stream->r_frame_rate.den / av_stream->r_frame_rate.num;
- video_frame_rate_numerator_ = final_fps;
- video_frame_rate_denominator_ = 1;
- } else {
- playback_rate_ = 1.0;
- video_frame_rate_numerator_ = av_stream->r_frame_rate.num;
- video_frame_rate_denominator_ = av_stream->r_frame_rate.den;
- }
- LOG(INFO) << "Source file has video.";
- } else {
- LOG(ERROR) << "Unknown stream type; ignore.";
- }
- }
- Rewind();
- }
- void FakeMediaSource::SetVariableFrameSizeMode(bool enabled) {
- variable_frame_size_mode_ = enabled;
- }
- void FakeMediaSource::Start(scoped_refptr<AudioFrameInput> audio_frame_input,
- scoped_refptr<VideoFrameInput> video_frame_input) {
- audio_frame_input_ = audio_frame_input;
- video_frame_input_ = video_frame_input;
- LOG(INFO) << "Max Frame rate: " << video_config_.max_frame_rate;
- LOG(INFO) << "Source Frame rate: "
- << video_frame_rate_numerator_ << "/"
- << video_frame_rate_denominator_ << " fps.";
- LOG(INFO) << "Audio playback rate: " << playback_rate_;
- if (start_time_.is_null())
- start_time_ = clock_->NowTicks();
- if (!is_transcoding_audio() && !is_transcoding_video()) {
- // Send fake patterns.
- task_runner_->PostTask(FROM_HERE,
- base::BindOnce(&FakeMediaSource::SendNextFakeFrame,
- weak_factory_.GetWeakPtr()));
- return;
- }
- // Send transcoding streams.
- bool is_encrypted = false;
- audio_algo_.Initialize(source_audio_params_, is_encrypted);
- audio_algo_.FlushBuffers();
- audio_fifo_input_bus_ = AudioBus::Create(
- source_audio_params_.channels(),
- source_audio_params_.frames_per_buffer());
- // Audio FIFO can carry all data fron AudioRendererAlgorithm.
- audio_fifo_ = std::make_unique<AudioFifo>(source_audio_params_.channels(),
- audio_algo_.QueueCapacity());
- audio_converter_ = std::make_unique<media::AudioConverter>(
- source_audio_params_, output_audio_params_, true);
- audio_converter_->AddInput(this);
- task_runner_->PostTask(FROM_HERE,
- base::BindOnce(&FakeMediaSource::SendNextFrame,
- weak_factory_.GetWeakPtr()));
- }
- void FakeMediaSource::SendNextFakeFrame() {
- UpdateNextFrameSize();
- scoped_refptr<VideoFrame> video_frame =
- VideoFrame::CreateBlackFrame(current_frame_size_);
- PopulateVideoFrame(video_frame.get(), synthetic_count_);
- ++synthetic_count_;
- const base::TimeTicks now = clock_->NowTicks();
- base::TimeDelta video_time = VideoFrameTime(++video_frame_count_);
- video_frame->set_timestamp(video_time);
- if (keep_frames_)
- inserted_video_frame_queue_.push(video_frame);
- video_frame_input_->InsertRawVideoFrame(video_frame,
- start_time_ + video_time);
- // Send just enough audio data to match next video frame's time.
- base::TimeDelta audio_time = AudioFrameTime(audio_frame_count_);
- while (audio_time < video_time) {
- if (is_transcoding_audio()) {
- Decode(true);
- CHECK(!audio_bus_queue_.empty()) << "No audio decoded.";
- std::unique_ptr<AudioBus> bus(audio_bus_queue_.front());
- audio_bus_queue_.pop();
- audio_frame_input_->InsertAudio(std::move(bus), start_time_ + audio_time);
- } else {
- audio_frame_input_->InsertAudio(
- audio_bus_factory_->NextAudioBus(base::Milliseconds(kAudioFrameMs)),
- start_time_ + audio_time);
- }
- audio_time = AudioFrameTime(++audio_frame_count_);
- }
- // This is the time since FakeMediaSource was started.
- const base::TimeDelta elapsed_time = now - start_time_;
- // Handle the case when frame generation cannot keep up.
- // Move the time ahead to match the next frame.
- while (video_time < elapsed_time) {
- LOG(WARNING) << "Skipping one frame.";
- video_time = VideoFrameTime(++video_frame_count_);
- }
- task_runner_->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&FakeMediaSource::SendNextFakeFrame,
- weak_factory_.GetWeakPtr()),
- video_time - elapsed_time);
- }
- void FakeMediaSource::UpdateNextFrameSize() {
- if (variable_frame_size_mode_) {
- bool update_size_change_time = false;
- if (current_frame_size_.IsEmpty()) {
- current_frame_size_ = gfx::Size(kStartingFakeFrameWidth,
- kStartingFakeFrameHeight);
- update_size_change_time = true;
- } else if (clock_->NowTicks() >= next_frame_size_change_time_) {
- current_frame_size_ = gfx::Size(
- base::RandInt(kMinFakeFrameWidth, kMaxFakeFrameWidth),
- base::RandInt(kMinFakeFrameHeight, kMaxFakeFrameHeight));
- update_size_change_time = true;
- }
- if (update_size_change_time) {
- next_frame_size_change_time_ =
- clock_->NowTicks() +
- base::Milliseconds(base::RandDouble() * kMaxFrameSizeChangeMillis);
- }
- } else {
- current_frame_size_ = gfx::Size(kStartingFakeFrameWidth,
- kStartingFakeFrameHeight);
- next_frame_size_change_time_ = base::TimeTicks();
- }
- }
- bool FakeMediaSource::SendNextTranscodedVideo(base::TimeDelta elapsed_time) {
- if (!is_transcoding_video())
- return false;
- Decode(false);
- if (video_frame_queue_.empty())
- return false;
- const scoped_refptr<VideoFrame> video_frame = video_frame_queue_.front();
- if (elapsed_time < video_frame->timestamp())
- return false;
- video_frame_queue_.pop();
- // Use the timestamp from the file if we're transcoding.
- video_frame->set_timestamp(ScaleTimestamp(video_frame->timestamp()));
- if (keep_frames_)
- inserted_video_frame_queue_.push(video_frame);
- video_frame_input_->InsertRawVideoFrame(
- video_frame, start_time_ + video_frame->timestamp());
- // Make sure queue is not empty.
- Decode(false);
- return true;
- }
- bool FakeMediaSource::SendNextTranscodedAudio(base::TimeDelta elapsed_time) {
- if (!is_transcoding_audio())
- return false;
- Decode(true);
- if (audio_bus_queue_.empty())
- return false;
- base::TimeDelta audio_time = audio_sent_ts_->GetTimestamp();
- if (elapsed_time < audio_time)
- return false;
- std::unique_ptr<AudioBus> bus(audio_bus_queue_.front());
- audio_bus_queue_.pop();
- audio_sent_ts_->AddFrames(bus->frames());
- audio_frame_input_->InsertAudio(std::move(bus), start_time_ + audio_time);
- // Make sure queue is not empty.
- Decode(true);
- return true;
- }
- void FakeMediaSource::SendNextFrame() {
- // Send as much as possible. Audio is sent according to
- // system time.
- while (SendNextTranscodedAudio(clock_->NowTicks() - start_time_)) {
- }
- // Video is sync'ed to audio.
- while (SendNextTranscodedVideo(audio_sent_ts_->GetTimestamp())) {
- }
- if (audio_bus_queue_.empty() && video_frame_queue_.empty()) {
- // Both queues are empty can only mean that we have reached
- // the end of the stream.
- LOG(INFO) << "Rewind.";
- Rewind();
- }
- // Send next send.
- task_runner_->PostDelayedTask(FROM_HERE,
- base::BindOnce(&FakeMediaSource::SendNextFrame,
- weak_factory_.GetWeakPtr()),
- base::Milliseconds(kAudioFrameMs));
- }
- base::TimeDelta FakeMediaSource::VideoFrameTime(int frame_number) {
- return frame_number * base::Seconds(1) * video_frame_rate_denominator_ /
- video_frame_rate_numerator_;
- }
- base::TimeDelta FakeMediaSource::ScaleTimestamp(base::TimeDelta timestamp) {
- return timestamp / playback_rate_;
- }
- base::TimeDelta FakeMediaSource::AudioFrameTime(int frame_number) {
- return frame_number * base::Milliseconds(kAudioFrameMs);
- }
- void FakeMediaSource::Rewind() {
- CHECK(av_seek_frame(av_format_context_, -1, 0, AVSEEK_FLAG_BACKWARD) >= 0)
- << "Failed to rewind to the beginning.";
- }
- ScopedAVPacket FakeMediaSource::DemuxOnePacket(bool* audio) {
- auto packet = ScopedAVPacket::Allocate();
- if (av_read_frame(av_format_context_, packet.get()) < 0) {
- VLOG(1) << "Failed to read one AVPacket.";
- return {};
- }
- int stream_index = static_cast<int>(packet->stream_index);
- if (stream_index == audio_stream_index_) {
- *audio = true;
- } else if (stream_index == video_stream_index_) {
- *audio = false;
- } else {
- // Ignore unknown packet.
- LOG(INFO) << "Unknown packet.";
- return {};
- }
- return packet;
- }
- void FakeMediaSource::DecodeAudio(ScopedAVPacket packet) {
- auto result = audio_decoding_loop_->DecodePacket(
- packet.get(), base::BindRepeating(&FakeMediaSource::OnNewAudioFrame,
- base::Unretained(this)));
- CHECK_EQ(result, FFmpegDecodingLoop::DecodeStatus::kOkay)
- << "Failed to decode audio.";
- const int frames_needed_to_scale =
- playback_rate_ * av_audio_context_->sample_rate / kAudioPacketsPerSecond;
- while (frames_needed_to_scale <= audio_algo_.BufferedFrames()) {
- if (!audio_algo_.FillBuffer(audio_fifo_input_bus_.get(), 0,
- audio_fifo_input_bus_->frames(),
- playback_rate_)) {
- // Nothing can be scaled. Decode some more.
- return;
- }
- // Prevent overflow of audio data in the FIFO.
- if (audio_fifo_input_bus_->frames() + audio_fifo_->frames() <=
- audio_fifo_->max_frames()) {
- audio_fifo_->Push(audio_fifo_input_bus_.get());
- } else {
- LOG(WARNING) << "Audio FIFO full; dropping samples.";
- }
- // Make sure there's enough data to resample audio.
- if (audio_fifo_->frames() <
- 2 * source_audio_params_.sample_rate() / kAudioPacketsPerSecond) {
- continue;
- }
- std::unique_ptr<media::AudioBus> resampled_bus(media::AudioBus::Create(
- output_audio_params_.channels(),
- output_audio_params_.sample_rate() / kAudioPacketsPerSecond));
- audio_converter_->Convert(resampled_bus.get());
- audio_bus_queue_.push(resampled_bus.release());
- }
- }
- bool FakeMediaSource::OnNewAudioFrame(AVFrame* frame) {
- int frames_read = frame->nb_samples;
- if (frames_read < 0)
- return false;
- if (!audio_sent_ts_) {
- // Initialize the base time to the first packet in the file. This is set to
- // the frequency we send to the receiver. Not the frequency of the source
- // file. This is because we increment the frame count by samples we sent.
- audio_sent_ts_ = std::make_unique<AudioTimestampHelper>(
- output_audio_params_.sample_rate());
- // For some files this is an invalid value.
- base::TimeDelta base_ts;
- audio_sent_ts_->SetBaseTimestamp(base_ts);
- }
- scoped_refptr<AudioBuffer> buffer = AudioBuffer::CopyFrom(
- AVSampleFormatToSampleFormat(av_audio_context_->sample_fmt,
- av_audio_context_->codec_id),
- ChannelLayoutToChromeChannelLayout(av_audio_context_->channel_layout,
- av_audio_context_->channels),
- av_audio_context_->channels, av_audio_context_->sample_rate, frames_read,
- &frame->data[0],
- PtsToTimeDelta(frame->pts, av_audio_stream()->time_base));
- audio_algo_.EnqueueBuffer(buffer);
- return true;
- }
- void FakeMediaSource::DecodeVideo(ScopedAVPacket packet) {
- auto result = video_decoding_loop_->DecodePacket(
- packet.get(), base::BindRepeating(&FakeMediaSource::OnNewVideoFrame,
- base::Unretained(this)));
- CHECK_EQ(result, FFmpegDecodingLoop::DecodeStatus::kOkay)
- << "Failed to decode video.";
- }
- bool FakeMediaSource::OnNewVideoFrame(AVFrame* frame) {
- gfx::Size size(av_video_context_->width, av_video_context_->height);
- if (!video_first_pts_set_) {
- video_first_pts_ = frame->pts;
- video_first_pts_set_ = true;
- }
- const AVRational& time_base = av_video_stream()->time_base;
- base::TimeDelta timestamp =
- PtsToTimeDelta(frame->pts - video_first_pts_, time_base);
- if (timestamp < last_video_frame_timestamp_) {
- // Stream has rewound. Rebase |video_first_pts_|.
- const AVRational& frame_rate = av_video_stream()->r_frame_rate;
- timestamp = last_video_frame_timestamp_ +
- (base::Seconds(1) * frame_rate.den / frame_rate.num);
- const int64_t adjustment_pts = TimeDeltaToPts(timestamp, time_base);
- video_first_pts_ = frame->pts - adjustment_pts;
- }
- AVFrame* shallow_copy = av_frame_clone(frame);
- scoped_refptr<media::VideoFrame> video_frame =
- VideoFrame::WrapExternalYuvData(
- media::PIXEL_FORMAT_I420, size, gfx::Rect(size), size,
- shallow_copy->linesize[0], shallow_copy->linesize[1],
- shallow_copy->linesize[2], shallow_copy->data[0],
- shallow_copy->data[1], shallow_copy->data[2], timestamp);
- if (!video_frame)
- return false;
- video_frame_queue_.push(video_frame);
- video_frame_queue_.back()->AddDestructionObserver(
- base::BindOnce(&AVFreeFrame, shallow_copy));
- last_video_frame_timestamp_ = timestamp;
- return true;
- }
- void FakeMediaSource::Decode(bool decode_audio) {
- // Read the stream until one video frame can be decoded.
- while (true) {
- if (decode_audio && !audio_bus_queue_.empty())
- return;
- if (!decode_audio && !video_frame_queue_.empty())
- return;
- bool audio_packet = false;
- ScopedAVPacket packet = DemuxOnePacket(&audio_packet);
- if (!packet) {
- VLOG(1) << "End of stream.";
- return;
- }
- if (audio_packet)
- DecodeAudio(std::move(packet));
- else
- DecodeVideo(std::move(packet));
- }
- }
- double FakeMediaSource::ProvideInput(media::AudioBus* output_bus,
- uint32_t frames_delayed) {
- if (audio_fifo_->frames() >= output_bus->frames()) {
- audio_fifo_->Consume(output_bus, 0, output_bus->frames());
- return 1.0;
- } else {
- LOG(WARNING) << "Not enough audio data for resampling.";
- output_bus->Zero();
- return 0.0;
- }
- }
- scoped_refptr<media::VideoFrame>
- FakeMediaSource::PopOldestInsertedVideoFrame() {
- CHECK(!inserted_video_frame_queue_.empty());
- scoped_refptr<media::VideoFrame> video_frame =
- inserted_video_frame_queue_.front();
- inserted_video_frame_queue_.pop();
- return video_frame;
- }
- AVStream* FakeMediaSource::av_audio_stream() {
- return av_format_context_->streams[audio_stream_index_];
- }
- AVStream* FakeMediaSource::av_video_stream() {
- return av_format_context_->streams[video_stream_index_];
- }
- } // namespace cast
- } // namespace media
|