| 120 | } |
| 121 | |
| 122 | VideoStream::VideoStream(FileSystem::InputStream stream, std::optional<FileSystem::InputStream> separateAudio) |
| 123 | : _stream(std::move(stream)), _ringBuffer(1024 * 4096), _texture(nullptr, SDL_DestroyTexture), _videoCodecContext(), _audioCodecContext(), _avFrame(), |
| 124 | _avBuffer(), _swsContext(), _sourceRect(), _targetRect(), _microsPerFrame(0), _videoTimestamp(0) { |
| 125 | _avBuffer = static_cast<unsigned char *>(av_malloc(DecodeBufferSize)); // AVIO is going to free this automagically... because why not? |
| 126 | memset(_avBuffer, 0, DecodeBufferSize); |
| 127 | |
| 128 | _avioContext = avio_alloc_context( |
| 129 | _avBuffer, DecodeBufferSize, 0, this, |
| 130 | [](void *opaque, uint8_t *buffer, const int size) { return static_cast<VideoStream *>(opaque)->videoStreamRead(buffer, size); }, nullptr, |
| 131 | [](void *opaque, const int64_t offset, const int whence) { return static_cast<VideoStream *>(opaque)->videoStreamSeek(offset, whence); }); |
| 132 | |
| 133 | _avFormatContext = avformat_alloc_context(); |
| 134 | _avFormatContext->pb = _avioContext; |
| 135 | _avFormatContext->flags |= AVFMT_FLAG_CUSTOM_IO; |
| 136 | |
| 137 | int avError; |
| 138 | if ((avError = avformat_open_input(&_avFormatContext, "", nullptr, nullptr)) < 0) |
| 139 | throw std::runtime_error(absl::StrCat("Failed to open AV format context: ", avErrorCodeToString(avError))); |
| 140 | |
| 141 | if ((avError = avformat_find_stream_info(_avFormatContext, nullptr)) < 0) |
| 142 | throw std::runtime_error(absl::StrCat("Failed to find stream info: ", avErrorCodeToString(avError))); |
| 143 | |
| 144 | for (auto i = 0; i < _avFormatContext->nb_streams; i++) { |
| 145 | if (_avFormatContext->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) |
| 146 | continue; |
| 147 | |
| 148 | _videoStreamIdx = i; |
| 149 | break; |
| 150 | } |
| 151 | |
| 152 | if (_videoStreamIdx == -1) |
| 153 | throw std::runtime_error("Could not locate video stream."); |
| 154 | |
| 155 | for (auto i = 0; i < _avFormatContext->nb_streams; i++) { |
| 156 | if (_avFormatContext->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) |
| 157 | continue; |
| 158 | |
| 159 | _audioStreamIdx = i; |
| 160 | break; |
| 161 | } |
| 162 | |
| 163 | _microsPerFrame = static_cast<uint64_t>(1000000 / static_cast<float>(_avFormatContext->streams[_videoStreamIdx]->r_frame_rate.num) / |
| 164 | static_cast<float>(_avFormatContext->streams[_videoStreamIdx]->r_frame_rate.den)); |
| 165 | |
| 166 | const auto videoCodecPar = _avFormatContext->streams[_videoStreamIdx]->codecpar; |
| 167 | const auto videoDecoder = avcodec_find_decoder(videoCodecPar->codec_id); |
| 168 | |
| 169 | if (videoDecoder == nullptr) |
| 170 | throw std::runtime_error("Missing video codec."); |
| 171 | |
| 172 | _videoCodecContext = avcodec_alloc_context3(videoDecoder); |
| 173 | if ((avError = avcodec_parameters_to_context(_videoCodecContext, videoCodecPar)) < 0) |
| 174 | throw std::runtime_error(absl::StrCat("Failed to apply parameters to video context: ", avErrorCodeToString(avError))); |
| 175 | |
| 176 | if ((avError = avcodec_open2(_videoCodecContext, videoDecoder, nullptr)) < 0) |
| 177 | throw std::runtime_error(absl::StrCat("Failed to open video context: ", avErrorCodeToString(avError))); |
| 178 | |
| 179 | if (_audioStreamIdx >= 0) { |
nothing calls this directly
no test coverage detected