Openharmony 5.1 AVPlayer扩展支持RTSP协议

内容分享4天前发布
0 0 0

介绍

本文档重点介绍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
};
​
© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...