1 /*
2  * Copyright (c) 2021-2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #ifdef RECORDER_SUPPORT
16 
17 #define HST_LOG_TAG "OutputSinkFilter"
18 
19 #include "pipeline/filters/sink/output_sink/output_sink_filter.h"
20 #include <cstdio>
21 #include "foundation/cpp_ext/type_traits_ext.h"
22 #include "foundation/log.h"
23 #include "foundation/utils/steady_clock.h"
24 #include "pipeline/factory/filter_factory.h"
25 #include "pipeline/filters/common/plugin_settings.h"
26 #include "pipeline/filters/common/plugin_utils.h"
27 #include "plugin/common/plugin_tags.h"
28 
29 namespace OHOS {
30 namespace Media {
31 namespace Pipeline {
32 static AutoRegisterFilter<OutputSinkFilter> g_registerFilterHelper("builtin.recorder.output_sink");
33 
OutputSinkFilter(std::string name)34 OutputSinkFilter::OutputSinkFilter(std::string name) : FilterBase(std::move(name))
35 {
36     filterType_ = FilterType::OUTPUT_SINK;
37 }
38 
~OutputSinkFilter()39 OutputSinkFilter::~OutputSinkFilter()
40 {
41     if (!bufferEos_) {
42         MEDIA_LOG_E("OutputSink send EVENT_ERROR: No EOS Received");
43     }
44 }
45 
Init(EventReceiver * receiver,FilterCallback * callback)46 void OutputSinkFilter::Init(EventReceiver *receiver, FilterCallback *callback)
47 {
48     FilterBase::Init(receiver, callback);
49     outPorts_.clear();
50 }
Negotiate(const std::string & inPort,const std::shared_ptr<const Plugin::Capability> & upstreamCap,Plugin::Capability & negotiatedCap,const Plugin::Meta & upstreamParams,Plugin::Meta & downstreamParams)51 bool OutputSinkFilter::Negotiate(const std::string& inPort,
52                                  const std::shared_ptr<const Plugin::Capability>& upstreamCap,
53                                  Plugin::Capability& negotiatedCap,
54                                  const Plugin::Meta& upstreamParams,
55                                  Plugin::Meta& downstreamParams)
56 {
57     auto candidatePlugins = FindAvailablePlugins(*upstreamCap, Plugin::PluginType::OUTPUT_SINK);
58     if (candidatePlugins.empty()) {
59         MEDIA_LOG_E("no available output sink plugin");
60         return false;
61     }
62     std::shared_ptr<Plugin::PluginInfo> selectedPluginInfo = nullptr;
63     for (const auto& candidate : candidatePlugins) {
64         const auto& tmp = candidate.first->extra[PLUGIN_INFO_EXTRA_OUTPUT_TYPE];
65         if (!Plugin::Any::IsSameTypeWith<Plugin::ProtocolType>(tmp)) {
66             continue;
67         }
68         if (Plugin::AnyCast<Plugin::ProtocolType>(tmp) == protocolType_) {
69             if (selectedPluginInfo == nullptr) {
70                 selectedPluginInfo = candidate.first;
71                 negotiatedCap = candidate.second;
72             } else if (candidate.first->rank > selectedPluginInfo->rank) {
73                 selectedPluginInfo = candidate.first;
74                 negotiatedCap = candidate.second;
75             }
76         }
77     }
78     if (selectedPluginInfo == nullptr) {
79         MEDIA_LOG_W("no available output sink plugin with output type of " PUBLIC_LOG_D32,
80                     static_cast<int32_t>(protocolType_));
81         return false;
82     }
83     auto res = UpdateAndInitPluginByInfo<Plugin::OutputSink>(plugin_, pluginInfo_, selectedPluginInfo,
84     [](const std::string& name) -> std::shared_ptr<Plugin::OutputSink> {
85         return Plugin::PluginManager::Instance().CreateOutputSinkPlugin(name);
86     });
87     return res;
88 }
89 
Configure(const std::string & inPort,const std::shared_ptr<const Plugin::Meta> & upstreamMeta,Plugin::Meta & upstreamParams,Plugin::Meta & downstreamParams)90 bool OutputSinkFilter::Configure(const std::string& inPort, const std::shared_ptr<const Plugin::Meta>& upstreamMeta,
91                                  Plugin::Meta& upstreamParams, Plugin::Meta& downstreamParams)
92 {
93     PROFILE_BEGIN("Output sink configure begin");
94     if (plugin_ == nullptr || pluginInfo_ == nullptr) {
95         MEDIA_LOG_E("cannot configure decoder when no plugin available");
96         return false;
97     }
98 
99     if (ConfigureToPreparePlugin() != ErrorCode::SUCCESS) {
100         return false;
101     }
102     state_ = FilterState::READY;
103     OnEvent({name_, EventType::EVENT_READY});
104     MEDIA_LOG_I("Output sink send EVENT_READY");
105     PROFILE_END("Output sink configure end");
106     return true;
107 }
108 
SetSink(const MediaSink & sink)109 ErrorCode OutputSinkFilter::SetSink(const MediaSink& sink)
110 {
111     auto protocolType = sink.GetProtocolType();
112     FALSE_RETURN_V(protocolType != Plugin::ProtocolType::UNKNOWN, ErrorCode::ERROR_INVALID_PARAMETER_VALUE);
113     sink_ = sink;
114     protocolType_ = protocolType;
115     return ErrorCode::SUCCESS;
116 }
117 
PushData(const std::string & inPort,const AVBufferPtr & buffer,int64_t offset)118 ErrorCode OutputSinkFilter::PushData(const std::string &inPort, const AVBufferPtr& buffer, int64_t offset)
119 {
120     auto ret = ErrorCode::SUCCESS;
121     if (offset >= 0 && offset != currentPos_) {
122         auto seekable = plugin_->GetSeekable();
123         if (seekable == Plugin::Seekable::UNSEEKABLE || seekable == Plugin::Seekable::INVALID) {
124             MEDIA_LOG_E("plugin " PUBLIC_LOG_S " does not support seekable", pluginInfo_->name.c_str());
125             return ErrorCode::ERROR_INVALID_OPERATION;
126         } else {
127             ret = TranslatePluginStatus(plugin_->SeekTo(offset));
128             if (ret != ErrorCode::SUCCESS) {
129                 MEDIA_LOG_E("plugin " PUBLIC_LOG_S " seek to " PUBLIC_LOG_D64 " failed",
130                             pluginInfo_->name.c_str(), offset);
131                 // should call back to client here
132                 return ErrorCode::ERROR_INVALID_OPERATION;
133             }
134             currentPos_ = offset;
135         }
136     }
137     if (!buffer->IsEmpty()) {
138         ret = TranslatePluginStatus(plugin_->Write(buffer));
139         if (ret != ErrorCode::SUCCESS) {
140             MEDIA_LOG_E("write to plugin failed with error code " PUBLIC_LOG_D32, CppExt::to_underlying(ret));
141             return ret;
142         }
143         currentPos_ += buffer->GetMemory()->GetSize();
144     }
145     if (buffer->flag & BUFFER_FLAG_EOS) {
146         plugin_->Flush();
147         Event event {
148             .srcFilter = name_,
149             .type = EventType::EVENT_COMPLETE,
150         };
151         MEDIA_LOG_D("file sink push data send event_complete");
152         OnEvent(event);
153         bufferEos_ = true;
154     }
155     return ret;
156 }
157 
Prepare()158 ErrorCode OutputSinkFilter::Prepare()
159 {
160     FilterBase::Prepare();
161     if (plugin_) {
162         plugin_->Prepare();
163     }
164     return ErrorCode::SUCCESS;
165 }
166 
Start()167 ErrorCode OutputSinkFilter::Start()
168 {
169     FilterBase::Start();
170     if (plugin_ == nullptr) {
171         MEDIA_LOG_E("no valid plugin");
172         return ErrorCode::ERROR_INVALID_STATE;
173     }
174     FAIL_RETURN_MSG_W(TranslatePluginStatus(plugin_->Start()), "plugin start failed");
175     bufferEos_ = false;
176     return ErrorCode::SUCCESS;
177 }
178 
Stop()179 ErrorCode OutputSinkFilter::Stop()
180 {
181     FilterBase::Stop();
182     currentPos_ = 0;
183     if (plugin_) {
184         if (bufferEos_) {
185             plugin_->Stop();
186         } else {
187             plugin_->Reset();
188         }
189     }
190     return ErrorCode::SUCCESS;
191 }
192 
ConfigureToPreparePlugin()193 ErrorCode OutputSinkFilter::ConfigureToPreparePlugin()
194 {
195     if (plugin_ == nullptr) {
196         MEDIA_LOG_E("no available plugin");
197         return ErrorCode::ERROR_INVALID_STATE;
198     }
199     auto err = TranslatePluginStatus(plugin_->SetSink(sink_));
200     if (err != ErrorCode::SUCCESS) {
201         MEDIA_LOG_E("Output sink configure error");
202         OnEvent({name_, EventType::EVENT_ERROR, err});
203         return err;
204     }
205     FAIL_RETURN_MSG_W(TranslatePluginStatus(plugin_->Prepare()), "plugin prepare failed");
206     return ErrorCode::SUCCESS;
207 }
208 } // Pipeline
209 } // Media
210 } // OHOS
211 #endif // RECORDER_SUPPORT