/* * Copyright (c) 2023 Fancy Code. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FfmpegReadFile.h" #include "FfmpegBase.h" #include "ILog.h" #include "MediaBase.h" #include "StatusCode.h" #include #include #ifdef __cplusplus extern "C" { #endif #include #include #include #include #ifdef __cplusplus } #endif #include #include #include #include FfmpegReadFile::FfmpegReadFile() : mReadVideoCallback(nullptr), mReadVideoCallbackContext(nullptr), mReadAudioCallback(nullptr), mReadAudioCallbackContext(nullptr) { } StatusCode FfmpegReadFile::StartReadFile(const std::string &path) { InitFfmpeg(); int result = 0; const AVInputFormat *iformat = av_find_input_format(FfmpegBase::InputFormat(mType)); AVFormatContext *pFormatCtx = nullptr; if ((result = avformat_open_input(&pFormatCtx, path.c_str(), iformat, nullptr)) < 0) { char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; LogError("Couldn't open file: %s, result=%s\n", path.c_str(), av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, result)); return CreateStatusCode(STATUS_CODE_NOT_OK); } if (avformat_find_stream_info(pFormatCtx, nullptr) < 0) { LogError("Couldn't find stream information.\n"); avformat_close_input(&pFormatCtx); return CreateStatusCode(STATUS_CODE_NOT_OK); } int mediaStreamIndex = -1; for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == mFFmpegMediaType) { mediaStreamIndex = i; break; } } if (mediaStreamIndex == -1) { LogError("Didn't find a stream.\n"); avformat_close_input(&pFormatCtx); return CreateStatusCode(STATUS_CODE_NOT_OK); } auto taskTimerThread = [=](std::shared_ptr media) { LogInfo("ReadFileThread start.\n"); media->ReadFileThread(pFormatCtx, mediaStreamIndex); }; std::shared_ptr media = std::dynamic_pointer_cast(FfmpegBase::shared_from_this()); mTaskTimerThread = std::thread(taskTimerThread, media); return CreateStatusCode(STATUS_CODE_OK); } StatusCode FfmpegReadFile::StopReadFile(void) { mTaskRuning = false; if (mTaskTimerThread.joinable()) { mTaskTimerThread.join(); } return CreateStatusCode(STATUS_CODE_OK); } StatusCode FfmpegReadFile::SetReadVideoCallback(ReadVideoFileCallback callback, void *context) { mReadVideoCallback = callback; mReadVideoCallbackContext = context; return CreateStatusCode(STATUS_CODE_OK); } StatusCode FfmpegReadFile::SetReadAudioCallback(ReadVideoFileCallback callback, void *context) { mReadAudioCallback = callback; mReadAudioCallbackContext = context; return CreateStatusCode(STATUS_CODE_OK); } void FfmpegReadFile::ReadFileThread(AVFormatContext *pFormatCtx, int mediaStreamIndex) { mTaskRuning = true; if (AVMEDIA_TYPE_VIDEO == mFFmpegMediaType && nullptr == mReadVideoCallback) { LogWarning("ReadVideoCallback is null.\n"); } if (AVMEDIA_TYPE_AUDIO == mFFmpegMediaType && nullptr == mReadVideoCallback) { LogWarning("ReadVideoCallback is null.\n"); } AVPacket packet; unsigned int playTimeMs = 0; // av_new_packet(&packet, AV_INPUT_BUFFER_MIN_SIZE); while (av_read_frame(pFormatCtx, &packet) >= 0) { if (nullptr == mReadVideoCallback) { av_packet_unref(&packet); continue; } if (false == mTaskRuning) { LogInfo("Stop read file thread.\n"); break; } // Checks whether the packet belongs to a video stream. if (packet.stream_index == mediaStreamIndex) { playTimeMs = (packet.duration * pFormatCtx->streams[mediaStreamIndex]->time_base.num * 1000) / pFormatCtx->streams[mediaStreamIndex]->time_base.den; // LogInfo("Frame data address: %p, length: %zu\n", packet.data, packet.size); // LogInfo("Play time ms:%d\n", playTimeMs); ReadFrame(&packet); std::this_thread::sleep_for(std::chrono::milliseconds(playTimeMs)); } // Release the data packet. av_packet_unref(&packet); } av_packet_unref(&packet); avformat_close_input(&pFormatCtx); } void inline FfmpegReadFile::ReadFrame(AVPacket *packet) { if (AVMEDIA_TYPE_VIDEO == mFFmpegMediaType) { mReadVideoCallback(packet->data, packet->size, mReadVideoCallbackContext); } else if (AVMEDIA_TYPE_AUDIO == mFFmpegMediaType) { mReadVideoCallback(packet->data, packet->size, mReadVideoCallbackContext); } }