介绍
本文档重点介绍AVPlayer通过ffmpeg库支持播放RTSP视频流的代码修改重点。代码修改基于Openharmony5.1版本。
一、播放器player_framework仓增加RTSP协议
代码中搜索查找”http”, 直接增加对应的”rtsp”判断
1. 文件frameworks/js/avplayer/avplayer_napi.cpp:
--- a/frameworks/js/avplayer/avplayer_napi.cpp
+++ b/frameworks/js/avplayer/avplayer_napi.cpp
@@ -1057,7 +1057,7 @@ void AVPlayerNapi::AddSubSource(std::string url)
{
MEDIA_LOGI("input url is %{private}s!", url.c_str());
bool isFd = (url.find("fd://") != std::string::npos) ? true : false;
- bool isNetwork = (url.find("http") != std::string::npos) ? true : false; //删除
+ bool isNetwork = (url.find("http") != std::string::npos || url.find("rtsp") == 0 || url.find("ws") == 0) ? true : false;
if (isNetwork) {
auto task = std::make_shared<TaskHandler<void>>([this, url]() {
MEDIA_LOGI("AddSubtitleNetworkSource Task");
@@ -1166,7 +1166,7 @@ napi_value AVPlayerNapi::JsAddSubtitleAVFileDescriptor(napi_env env, napi_callba
void AVPlayerNapi::SetSource(std::string url)
{
bool isFd = (url.find("fd://") != std::string::npos) ? true : false;
- bool isNetwork = (url.find("http") != std::string::npos) ? true : false; //删除
+ bool isNetwork = (url.find("http") != std::string::npos || url.find("rtsp") == 0 || url.find("ws") == 0)? true : false;
if (isNetwork) {
EnqueueNetworkTask(url);
} else if (isFd) {
2. 文件frameworks/js/player/video_player_napi.cpp
--- a/frameworks/js/player/video_player_napi.cpp
+++ b/frameworks/js/player/video_player_napi.cpp
@@ -263,7 +263,7 @@ napi_value VideoPlayerNapi::SetUrl(napi_env env, napi_callback_info info)
}
ret = jsPlayer->nativePlayer_->SetSource(fd, 0, -1);
- } else if (jsPlayer->url_.find(httpHead) != std::string::npos) {
+ } else if (jsPlayer->url_.find(httpHead) != std::string::npos || jsPlayer->url_.find("rtsp") == 0) {
ret = jsPlayer->nativePlayer_->SetSource(jsPlayer->url_);
}
3. 文件services/engine/histreamer/player/hiplayer_impl.cpp
--- a/services/engine/histreamer/player/hiplayer_impl.cpp
+++ b/services/engine/histreamer/player/hiplayer_impl.cpp
@@ -284,7 +284,7 @@ bool HiPlayerImpl::IsFileUrl(const std::string &url) const
bool HiPlayerImpl::IsNetworkUrl(const std::string &url) const
{
- return url.find("http") == 0 || url.find("https") == 0; // del
+ return url.find("http") == 0 || url.find("https") == 0 || url.find("rtsp") == 0;
}
二、在媒体multimedia_media_foundation仓中添加RTSP类型与相应的插件列表
1. 在媒体文件类型中增加RTSP类型
enum class FileType : int32_t {
UNKNOW = 0,
MP4 = 101,
MPEGTS = 102,
MKV = 103,
FLV = 104,
MPEGPS = 106,
MOV = 107,
AVI = 105,
RM = 108,
AC3 = 109,
AMR = 201,
AAC = 202,
MP3 = 203,
FLAC = 204,
OGG = 205,
M4A = 206,
WAV = 207,
APE = 208,
SRT = 301,
VTT = 302,
RTSP = 401, // add
};
2. 数据源中增加获取Uri的接口函数
struct DataSource {
/// Destructor
virtual ~DataSource() = default;
/**
* @brief Read data from data source.
*
* @param offset Offset of read position
* @param buffer Storage of the read data
* @param expectedLen Expected data size to be read
* @return Execution status return
* @retval OK: Plugin ReadAt succeeded.
* @retval ERROR_NOT_ENOUGH_DATA: Data not enough
* @retval END_OF_STREAM: End of stream
*/
virtual Status ReadAt(int64_t offset, std::shared_ptr<Buffer>& buffer, size_t expectedLen) = 0;
.......
// add
virtual std::string GetUri() = 0;
};
3. 在插件列表中添加RtspSource插件与avdemux_rtsp插件
src/plugin/plugin_list.cpp
PluginDescription RtspSourcePlugin; RtspSourcePlugin.pluginName = "RtspSource"; RtspSourcePlugin.packageName = "RtspSource"; RtspSourcePlugin.pluginType = PluginType::SOURCE; RtspSourcePlugin.cap = "rtsp"; RtspSourcePlugin.rank = DEFAULT_RANK; pluginDescriptionList_.push_back(RtspSourcePlugin); PluginDescription rtspDemuxerPlugin; rtspDemuxerPlugin.pluginName = "avdemux_rtsp"; rtspDemuxerPlugin.packageName = "FFmpegDemuxer"; rtspDemuxerPlugin.pluginType = PluginType::DEMUXER; rtspDemuxerPlugin.cap = ""; rtspDemuxerPlugin.rank = DEFAULT_RANK; pluginDescriptionList_.push_back(rtspDemuxerPlugin);
三、在multimedia_av_codec仓中实现RtspSourcePlugin插件
RtspSourcePlugin插件主要用于表示video播放支持rtsp,不实现具体内容
//rtsp_source_plugin.h file
#include <memory>
#include "plugin/source_plugin.h"
namespace OHOS {
namespace Media {
namespace Plugins {
namespace RtspPlugin {
class RtspSourcePlugin : public SourcePlugin {
public:
explicit RtspSourcePlugin(const std::string& name) noexcept;
virtual ~RtspSourcePlugin();
Status Init() override;
Status Deinit() override;
Status Prepare() override;
Status Reset() override;
Status SetCallback(Callback* cb) override;
// 实现 SourcePlugin 接口
Status SetSource(std::shared_ptr<MediaSource> source) override;
Status Read(std::shared_ptr<Buffer>& buffer, uint64_t offset, size_t expectedLen) override;
Status Read(int32_t streamId, std::shared_ptr<Buffer>& buffer, uint64_t offset, size_t expectedLen) override;
Status GetSize(uint64_t& size) override;
Status SeekTo(uint64_t offset) override { return Status::ERROR_INVALID_OPERATION; }
Status Start() override;
Status Stop() override;
Status GetStreamInfo(std::vector<StreamInfo>& streams) override;
bool IsSeekToTimeSupported() override { return false; }
Seekable GetSeekable() override { return Seekable::UNSEEKABLE; }
};
}
} // namespace Plugin
} // namespace Media
} // namespace OHOS
////////////////////////////////////////////////////////
//rtsp_source_plugin.cpp file
#define HST_LOG_TAG "RtspSourcePlugin"
#include "rtsp_source_plugin.h"
#include "common/log.h"
namespace {
constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_SYSTEM_PLAYER, "RtspSourcePlugin" };
}
namespace OHOS {
namespace Media {
namespace Plugins {
namespace RtspPlugin {
std::shared_ptr<SourcePlugin> RtspSourcePluginCreater(const std::string& name)
{
return std::make_shared<RtspSourcePlugin>(name);
}
Status RtspSourceRegister(std::shared_ptr<Register> reg)
{
SourcePluginDef definition;
definition.name = "RtspSource";
definition.description = "RTSP source plugin based on FFmpeg";
definition.rank = 100; // 100
Capability capability;
capability.AppendFixedKey<std::vector<ProtocolType>>(Tag::MEDIA_PROTOCOL_TYPE,
{ProtocolType::RTSP});
definition.AddInCaps(capability);
definition.SetCreator(RtspSourcePluginCreater);
return reg->AddPlugin(definition);
}
PLUGIN_DEFINITION(RtspSource, LicenseType::APACHE_V2, RtspSourceRegister, [] {});
RtspSourcePlugin::RtspSourcePlugin(const std::string &name) noexcept
: SourcePlugin(std::move(name))
{
MEDIA_LOG_D("RtspSourcePlugin enter.");
}
RtspSourcePlugin::~RtspSourcePlugin()
{
MEDIA_LOG_D("~RtspSourcePlugin enter.");
Stop();
}
Status RtspSourcePlugin::Init()
{
MEDIA_LOG_D("Init enter.");
return Status::OK;
}
Status RtspSourcePlugin::Deinit()
{
MEDIA_LOG_D("Deinit enter.");
Stop();
return Status::OK;
}
Status RtspSourcePlugin::Prepare()
{
MEDIA_LOG_D("Prepare enter.");
return Status::OK;
}
Status RtspSourcePlugin::Reset()
{
MEDIA_LOG_D("Reset enter.");
Stop();
return Status::OK;
}
Status RtspSourcePlugin::Start()
{
MEDIA_LOG_D("Start enter.");
return Status::OK;
}
Status RtspSourcePlugin::Stop()
{
MEDIA_LOG_I("Stop enter.");
return Status::OK;
}
Status RtspSourcePlugin::GetStreamInfo(std::vector<StreamInfo>& streams)
{
return Status::OK;
}
Status RtspSourcePlugin::SetCallback(Callback* cb)
{
MEDIA_LOG_D("SetCallback enter.");
return Status::OK;
}
Status RtspSourcePlugin::SetSource(std::shared_ptr<MediaSource> source)
{
MEDIA_LOG_D("SetSource enter.");
FALSE_RETURN_V(source != nullptr, Status::ERROR_INVALID_OPERATION);
std::string uri = source->GetSourceUri();
if (uri.empty() || uri.find("rtsp://") == std::string::npos) {
MEDIA_LOG_E("Invalid RTSP URL: %{public}s", uri.c_str());
return Status::ERROR_INVALID_OPERATION;
}
return Status::OK;
}
Status RtspSourcePlugin::Read(std::shared_ptr<Buffer>& buffer, uint64_t offset, size_t expectedLen)
{
return Read(0, buffer, offset, expectedLen);
}
Status RtspSourcePlugin::Read(int32_t streamId, std::shared_ptr<Buffer>& buffer, uint64_t offset, size_t expectedLen)
{
return Status::ERROR_WRONG_STATE;
}
Status RtspSourcePlugin::GetSize(uint64_t& size)
{
MEDIA_LOG_D("GetSize enter.");
size = 0;
return Status::OK;
}
}
}
}
}
四、修改multimedia_av_codec仓中FFmpegDemuxerPlugin插件
1. 实现必要的std::string GetUri()接口函数
2. 文件services/media_engine/modules/demuxer/media_demuxer.cpp:
设置数据源时保存uri
Status MediaDemuxer::SetDataSource(const std::shared_ptr<MediaSource> &source)
{
MediaAVCodec::AVCODEC_SYNC_TRACE;
MEDIA_LOG_I("In");
FALSE_RETURN_V_MSG_E(isThreadExit_, Status::ERROR_WRONG_STATE, "Process is running");
source_->SetCallback(this);
auto res = source_->SetSource(source);
FALSE_RETURN_V_MSG_E(res == Status::OK, res, "Plugin set source failed");
uri_ = source->GetSourceUri(); // add
isFlvLiveStream_ = source_->IsFlvLiveStream();
Status ret = source_->GetSize(mediaDataSize_);
FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "Get file size failed");
std::vector<StreamInfo> streams;
source_->GetStreamInfo(streams);
.......
}
3. 修改FFmpegDemuxerPlugin插件支持RTSP协议
1) 新增rtsp协议的AVFormatContext初始化函数
std::shared_ptr<AVFormatContext> FFmpegDemuxerPlugin::InitAVFormatContext()
{
AVFormatContext* formatContext = avformat_alloc_context();
FALSE_RETURN_V_MSG_E(formatContext != nullptr, nullptr, "AVFormatContext is nullptr");
AVDictionary *options = nullptr;
av_dict_set(&options, "rtsp_transport", "tcp", 0);
av_dict_set(&options, "stimeout", "5000000", 0);
av_dict_set(&options, "buffer_size", "1048576", 0);
av_dict_set(&options, "max_delay", "500000", 0);
int ret = ParseHeader(formatContext, pluginImpl_, &options, uri_);
if(ret < 0) {
formatContext = avformat_alloc_context();
MEDIA_LOG_I("rtsp_transport switch to udp");
av_dict_set(&options, "rtsp_transport", "udp", 0);
ret = ParseHeader(formatContext, pluginImpl_, &options, uri_);
}
av_dict_free(&options);
FALSE_RETURN_V_MSG_E(ret >= 0, nullptr, "ParseHeader failed");
std::shared_ptr<AVFormatContext> retFormatContext =
std::shared_ptr<AVFormatContext>(formatContext, [](AVFormatContext *ptr) {
if (ptr) {
avformat_free_context(ptr);
}
});
return retFormatContext;
}
2) 修改rtsp协议
int32_t GetConfidence(std::shared_ptr<AVInputFormat> plugin, const std::string& pluginName,
std::shared_ptr<DataSource> dataSource, size_t &getData)
{
// add
if(pluginName == "avdemux_rtsp" && dataSource->GetUri().find("rtsp://") == 0) {
MEDIA_LOG_I("Confidence is rtsp");
return RANK_MAX;
}
size_t bufferSize = DEFAULT_SNIFF_SIZE;
uint64_t fileSize = 0;
Status getFileSize = dataSource->GetSize(fileSize);
if (getFileSize == Status::OK) {
bufferSize = (bufferSize < fileSize) ? bufferSize : fileSize;
}
std::vector<uint8_t> buff(bufferSize + AVPROBE_PADDING_SIZE); // fix ffmpeg probe crash, refer to tools/probetest.c
auto bufferInfo = std::make_shared<Buffer>();
.....
}
3) 修改SetDataSource函数,根据uri初始化不同的AVFormatContext
Status FFmpegDemuxerPlugin::SetDataSource(const std::shared_ptr<DataSource>& source)
{
std::lock_guard<std::shared_mutex> lock(sharedMutex_);
FALSE_RETURN_V_MSG_E(formatContext_ == nullptr, Status::ERROR_WRONG_STATE, "AVFormatContext is nullptr");
FALSE_RETURN_V_MSG_E(source != nullptr, Status::ERROR_INVALID_PARAMETER, "DataSource is nullptr");
std::string uri = source->GetUri();
uri_ = "";
if (uri.find("rtsp://") == 0) {
uri_ = uri;
}
ioContext_.dataSource = source;
ioContext_.offset = 0;
ioContext_.eos = false;
ioContext_.dumpMode = dumpMode_;
seekable_ = (ioContext_.dataSource->IsDash() || !uri_.empty()) ? Plugins::Seekable::UNSEEKABLE : source->GetSeekable();
if (seekable_ == Plugins::Seekable::SEEKABLE) {
ioContext_.dataSource->GetSize(ioContext_.fileSize);
} else {
ioContext_.fileSize = -1;
}
MEDIA_LOG_I("FileSize: " PUBLIC_LOG_U64 ", seekable: " PUBLIC_LOG_D32 " uri:%{public}s",
ioContext_.fileSize, seekable_, uri_.c_str());
{
std::lock_guard<std::mutex> glock(g_mtx);
pluginImpl_ = g_pluginInputFormat[pluginName_];
}
FALSE_RETURN_V_MSG_E(pluginImpl_ != nullptr, Status::ERROR_UNSUPPORTED_FORMAT, "No match inputformat");
formatContext_ = uri_.empty() ? InitAVFormatContext(&ioContext_) : InitAVFormatContext();
InitParser();
// parse media info
GetMediaInfo();
......
}
4) 依据uri打开数据源
int32_t ParseHeader(AVFormatContext* formatContext, std::shared_ptr<AVInputFormat> pluginImpl, AVDictionary **options, std::string &uri)
{
FALSE_RETURN_V_MSG_E(formatContext && pluginImpl, -1, "AVFormatContext is nullptr");
MediaAVCodec::AVCodecTrace trace("ffmpeg_init");
AVIOContext* avioContext = formatContext->pb;
auto begin = std::chrono::steady_clock::now();
int ret = 0;
if(!uri.empty()) {
ret = avformat_open_input(&formatContext, uri.c_str(), pluginImpl.get(), options);
} else {
ret = avformat_open_input(&formatContext, nullptr, pluginImpl.get(), options);
}
if (ret < 0) {
FreeContext(formatContext, avioContext);
MEDIA_LOG_E("Call avformat_open_input failed by " PUBLIC_LOG_S ", err:" PUBLIC_LOG_S,
pluginImpl->name, AVStrError(ret).c_str());
return ret;
}
auto open = std::chrono::steady_clock::now();
if (FFmpegFormatHelper::GetFileTypeByName(*formatContext) == FileType::FLV || !uri.empty() ) {
formatContext->probesize = LIVE_FLV_PROBE_SIZE;
}
ret = avformat_find_stream_info(formatContext, NULL);
auto parse = std::chrono::steady_clock::now();
int64_t openSpend = static_cast<int64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(open - begin).count());
int64_t parseSpend = static_cast<int64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(parse - open).count());
if ((parseSpend < 0) || (openSpend > INT64_MAX - parseSpend) || (openSpend + parseSpend > INIT_TIME_THRESHOLD)) {
MEDIA_LOG_W("Spend [" PUBLIC_LOG_D64 "/" PUBLIC_LOG_D64 "]", openSpend, parseSpend);
}
if (ret < 0) {
FreeContext(formatContext, avioContext);
MEDIA_LOG_E("Parse stream info failed by " PUBLIC_LOG_S ", err:" PUBLIC_LOG_S,
pluginImpl->name, AVStrError(ret).c_str());
return ret;
}
return 0;
}
5) 支持CombineFrame
bool FFmpegDemuxerPlugin::NeedCombineFrame(uint32_t trackId)
{
FALSE_RETURN_V_MSG_E(formatContext_ != nullptr, false, "AVFormatContext is nullptr");
if (FFmpegFormatHelper::GetFileTypeByName(*formatContext_) == FileType::MPEGTS &&
formatContext_->streams[trackId]->codecpar->codec_id == AV_CODEC_ID_HEVC) {
return true;
}
if (FFmpegFormatHelper::GetFileTypeByName(*formatContext_) == FileType::RTSP &&
(formatContext_->streams[trackId]->codecpar->codec_id == AV_CODEC_ID_H264
|| formatContext_->streams[trackId]->codecpar->codec_id == AV_CODEC_ID_HEVC)) {
return true;
}
return false;
}
4. 其他修改
// add
std::string DataSourceImpl::GetUri()
{
if (stream_) return stream_->GetUri();
return "";
}
bool IsInputFormatSupported(const char* name)
{
MEDIA_LOG_D("Check support " PUBLIC_LOG_S " or not.", name);
if (!strcmp(name, "audio_device") || StartWith(name, "image") ||
!strcmp(name, "mjpeg") || !strcmp(name, "redir") || StartWith(name, "u8") ||
StartWith(name, "u16") || StartWith(name, "u24") ||
StartWith(name, "u32") ||
StartWith(name, "s8") || StartWith(name, "s16") ||
StartWith(name, "s24") ||
StartWith(name, "s32") || StartWith(name, "f32") ||
StartWith(name, "f64") ||
!strcmp(name, "mulaw") || !strcmp(name, "alaw")) {
return false;
}
if (!strcmp(name, "sdp") || !strcmp(name, "applehttp")) { // del "rtsp"
return false;
}
return true;
}
static std::map<std::string, FileType> g_convertFfmpegFileType = {
{"mpegts", FileType::MPEGTS},
{"matroska,webm", FileType::MKV},
{"amr", FileType::AMR},
{"amrnb", FileType::AMR},
{"amrwb", FileType::AMR},
{"aac", FileType::AAC},
{"mp3", FileType::MP3},
{"flac", FileType::FLAC},
{"ogg", FileType::OGG},
{"wav", FileType::WAV},
{"flv", FileType::FLV},
{"avi", FileType::AVI},
{"mpeg", FileType::MPEGPS},
{"rm", FileType::RM},
{"ac3", FileType::AC3},
{"ape", FileType::APE},
{"srt", FileType::SRT},
{"webvtt", FileType::VTT},
{"rtsp", FileType::RTSP}, // add rtsp
};
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...


