RDK Documentation (Open Sourced RDK Components)
isobmffprocessor.cpp
Go to the documentation of this file.
1 /*
2  * If not stated otherwise in this file or this component's license file the
3  * following copyright and licenses apply:
4  *
5  * Copyright 2019 RDK Management
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18 */
19 
20 /**
21 * @file isobmffprocessor.cpp
22 * @brief Source file for ISO Base Media File Format Segment Processor
23 */
24 
25 #include "isobmffprocessor.h"
26 #include <pthread.h>
27 #include <assert.h>
28 
29 static const char *IsoBmffProcessorTypeName[] =
30 {
31  "video", "audio"
32 };
33 
34 /**
35  * @brief IsoBmffProcessor constructor
36  */
38  : p_aamp(aamp), type(trackType), peerProcessor(peerBmffProcessor), basePTS(0),
39  processPTSComplete(false), timeScale(0), initSegment(),
40  playRate(1.0f), abortAll(false), m_mutex(), m_cond(),
41  initSegmentProcessComplete(false),
42  mLogObj(logObj)
43 {
44  AAMPLOG_WARN("IsoBmffProcessor:: Created IsoBmffProcessor(%p) for type:%d and peerProcessor(%p)", this, type, peerBmffProcessor);
45  if (peerProcessor)
46  {
47  peerProcessor->setPeerProcessor(this);
48  }
49  pthread_mutex_init(&m_mutex, NULL);
50  pthread_cond_init(&m_cond, NULL);
51 
52  // Sometimes AAMP pushes an encrypted init segment first to force decryptor plugin selection
53  initSegment.reserve(2);
54 }
55 
56 /**
57  * @brief IsoBmffProcessor destructor
58  */
60 {
62  pthread_mutex_destroy(&m_mutex);
63  pthread_cond_destroy(&m_cond);
64 }
65 
66 /**
67  * @brief Process and send ISOBMFF fragment
68  */
69 bool IsoBmffProcessor::sendSegment(char *segment, size_t& size, double position, double duration, bool discontinuous, bool &ptsError)
70 {
71  ptsError = false;
72  bool ret = true;
73 
74  AAMPLOG_TRACE("IsoBmffProcessor:: [%s] sending segment at pos:%f dur:%f", IsoBmffProcessorTypeName[type], position, duration);
75 
76  // Logic for Audio Track
77  if (type == eBMFFPROCESSOR_TYPE_AUDIO)
78  {
79  if (!processPTSComplete)
80  {
81  IsoBmffBuffer buffer(mLogObj);
82  buffer.setBuffer((uint8_t *)segment, size);
83  buffer.parseBuffer();
84 
85  if (buffer.isInitSegment())
86  {
87  cacheInitSegment(segment, size);
88  ret = false;
89  }
90  else
91  {
92  // Wait for video to parse PTS
93  pthread_mutex_lock(&m_mutex);
94  if (!processPTSComplete)
95  {
96  AAMPLOG_INFO("IsoBmffProcessor:: [%s] Going into wait for PTS processing to complete", IsoBmffProcessorTypeName[type]);
97  pthread_cond_wait(&m_cond, &m_mutex);
98  }
99  if (abortAll)
100  {
101  ret = false;
102  }
103  pthread_mutex_unlock(&m_mutex);
104  }
105  }
106  if (ret && !initSegmentProcessComplete)
107  {
108  if (processPTSComplete)
109  {
110  double pos = ((double)basePTS / (double)timeScale);
111  if (!initSegment.empty())
112  {
113  pushInitSegment(pos);
114  }
115  else
116  {
117  // We have no cached init fragment, maybe audio download was delayed very much
118  // Push this fragment with calculated PTS
119  p_aamp->SendStreamCopy((MediaType)type, segment, size, pos, pos, duration);
120  ret = false;
121  }
122  initSegmentProcessComplete = true;
123  }
124  }
125  }
126 
127 
128  // Logic for Video Track
129  // For trickplay, restamping is done in qtdemux. We can avoid
130  // pts parsing logic
131  if (ret && !processPTSComplete && playRate == AAMP_NORMAL_PLAY_RATE)
132  {
133  // We need to parse PTS from first buffer
134  IsoBmffBuffer buffer(mLogObj);
135  buffer.setBuffer((uint8_t *)segment, size);
136  buffer.parseBuffer();
137 
138  if (buffer.isInitSegment())
139  {
140  uint32_t tScale = 0;
141  if (buffer.getTimeScale(tScale))
142  {
143  timeScale = tScale;
144  AAMPLOG_INFO("IsoBmffProcessor:: [%s] TimeScale (%u) set", IsoBmffProcessorTypeName[type], timeScale);
145  }
146 
147  cacheInitSegment(segment, size);
148  ret = false;
149  }
150  else
151  {
152  // Init segment was parsed and stored previously. Find the base PTS now
153  uint64_t fPts = 0;
154  if (buffer.getFirstPTS(fPts))
155  {
156  basePTS = fPts;
157  processPTSComplete = true;
158  AAMPLOG_WARN("IsoBmffProcessor:: [%s] Base PTS (%lld) set", IsoBmffProcessorTypeName[type], basePTS);
159  }
160  else
161  {
162  AAMPLOG_ERR("IsoBmffProcessor:: [%s] Failed to process pts from buffer at pos:%f and dur:%f", IsoBmffProcessorTypeName[type], position, duration);
163  }
164 
165  pthread_mutex_lock(&m_mutex);
166  if (abortAll)
167  {
168  ret = false;
169  }
170  pthread_mutex_unlock(&m_mutex);
171 
172  if (ret && processPTSComplete)
173  {
174  if (timeScale == 0)
175  {
176  if (initSegment.empty())
177  {
178  AAMPLOG_WARN("IsoBmffProcessor:: [%s] Init segment missing during PTS processing!", IsoBmffProcessorTypeName[type]);
180  ret = false;
181  }
182  else
183  {
184  AAMPLOG_WARN("IsoBmffProcessor:: [%s] MDHD/MVHD boxes are missing in init segment!", IsoBmffProcessorTypeName[type]);
185  uint32_t tScale = 0;
186  if (buffer.getTimeScale(tScale))
187  {
188  timeScale = tScale;
189  AAMPLOG_INFO("IsoBmffProcessor:: [%s] TimeScale (%u) set", IsoBmffProcessorTypeName[type], timeScale);
190  }
191  if (timeScale == 0)
192  {
193  AAMPLOG_ERR("IsoBmffProcessor:: [%s] TimeScale value missing in init segment and mp4 fragment, setting to a default of 1!", IsoBmffProcessorTypeName[type]);
194  timeScale = 1; // to avoid div-by-zero errors later. MDHD and MVHD are mandatory boxes, but lets relax for now
195  }
196 
197  }
198  }
199 
200  if (ret)
201  {
202  double pos = ((double)basePTS / (double)timeScale);
203  // If AAMP override hack is enabled for this platform, then we need to pass the basePTS value to
204  // PrivateInstanceAAMP since PTS will be restamped in qtdemux. This ensures proper pts value is sent in progress event.
205 #ifdef ENABLE_AAMP_QTDEMUX_OVERRIDE
206  p_aamp->NotifyFirstVideoPTS(basePTS, timeScale);
207  // Here, basePTS might not be based on a 90KHz clock, whereas gst videosink might be.
208  // So PTS value sent via progress event might not be accurate.
209  p_aamp->NotifyVideoBasePTS(basePTS, timeScale);
210 #endif
211  if (type == eBMFFPROCESSOR_TYPE_VIDEO)
212  {
213  // Send flushing seek to gstreamer pipeline.
214  // For new tune, this will not work, so send pts as fragment position
215  p_aamp->FlushStreamSink(pos, playRate);
216  }
217 
218  if (peerProcessor)
219  {
220  peerProcessor->setBasePTS(basePTS, timeScale);
221  }
222 
223  pushInitSegment(pos);
224  initSegmentProcessComplete = true;
225  }
226  }
227  }
228  }
229 
230  if (ret)
231  {
232  p_aamp->ProcessID3Metadata(segment, size, (MediaType)type);
233  p_aamp->SendStreamCopy((MediaType)type, segment, size, position, position, duration);
234  }
235  return true;
236 }
237 
238 /**
239  * @brief Abort all operations
240  */
242 {
243  pthread_mutex_lock(&m_mutex);
244  abortAll = true;
245  pthread_cond_signal(&m_cond);
246  pthread_mutex_unlock(&m_mutex);
247 }
248 
249 /**
250  * @brief Reset all variables
251  */
253 {
255 
256  pthread_mutex_lock(&m_mutex);
257  basePTS = 0;
258  timeScale = 0;
259  processPTSComplete = false;
260  abortAll = false;
261  initSegmentProcessComplete = false;
262  pthread_mutex_unlock(&m_mutex);
263 }
264 
265 /**
266  * @brief Set playback rate
267  */
268 void IsoBmffProcessor::setRate(double rate, PlayMode mode)
269 {
270  playRate = rate;
271 }
272 
273 /**
274  * @brief Set base PTS and TimeScale value
275  */
276 void IsoBmffProcessor::setBasePTS(uint64_t pts, uint32_t tScale)
277 {
278  AAMPLOG_WARN("[%s] Base PTS (%lld) and TimeScale (%u) set", IsoBmffProcessorTypeName[type], pts, tScale);
279  pthread_mutex_lock(&m_mutex);
280  basePTS = pts;
281  timeScale = tScale;
282  processPTSComplete = true;
283  pthread_cond_signal(&m_cond);
284  pthread_mutex_unlock(&m_mutex);
285 }
286 
287 /**
288  * @brief Cache init fragment internally
289  */
290 void IsoBmffProcessor::cacheInitSegment(char *segment, size_t size)
291 {
292  // Save init segment for later. Init segment will be pushed once basePTS is calculated
293  AAMPLOG_INFO("IsoBmffProcessor::[%s] Caching init fragment", IsoBmffProcessorTypeName[type]);
294  GrowableBuffer *buffer = new GrowableBuffer();
295  memset(buffer, 0x00, sizeof(GrowableBuffer));
296  aamp_AppendBytes(buffer, segment, size);
297  initSegment.push_back(buffer);
298 }
299 
300 
301 /**
302  * @brief Push init fragment cached earlier
303  */
305 {
306  // Push init segment now, duration = 0
307  AAMPLOG_WARN("IsoBmffProcessor:: [%s] Push init fragment", IsoBmffProcessorTypeName[type]);
308  if (initSegment.size() > 0)
309  {
310  for (auto it = initSegment.begin(); it != initSegment.end();)
311  {
312  GrowableBuffer *buf = *it;
313  p_aamp->SendStreamTransfer((MediaType)type, buf, position, position, 0);
314  aamp_Free(buf);
315  SAFE_DELETE(buf);
316  it = initSegment.erase(it);
317  }
318  }
319 }
320 
321 /**
322  * @brief Clear init fragment cached earlier
323  */
325 {
326  if (initSegment.size() > 0)
327  {
328  for (auto it = initSegment.begin(); it != initSegment.end();)
329  {
330  GrowableBuffer *buf = *it;
331  aamp_Free(buf);
332  SAFE_DELETE(buf);
333  it = initSegment.erase(it);
334  }
335  }
336 }
337 
aamp_Free
void aamp_Free(void *ptr)
wrapper for g_free, used for segment allocation
Definition: AampMemoryUtils.cpp:56
IsoBmffProcessor::clearInitSegment
void clearInitSegment()
Clear init fragment cached earlier.
Definition: isobmffprocessor.cpp:324
AAMP_TUNE_MP4_INIT_FRAGMENT_MISSING
@ AAMP_TUNE_MP4_INIT_FRAGMENT_MISSING
Definition: AampEvent.h:146
IsoBmffProcessor::reset
void reset() override
Reset all variables.
Definition: isobmffprocessor.cpp:252
PrivateInstanceAAMP::ProcessID3Metadata
void ProcessID3Metadata(char *segment, size_t size, MediaType type, uint64_t timestampOffset=0)
Process the ID3 metadata from segment.
Definition: priv_aamp.cpp:11501
IsoBmffProcessor::~IsoBmffProcessor
~IsoBmffProcessor()
IsoBmffProcessor destructor.
Definition: isobmffprocessor.cpp:59
PrivateInstanceAAMP::SendStreamTransfer
void SendStreamTransfer(MediaType mediaType, GrowableBuffer *buffer, double fpts, double fdts, double fDuration, bool initFragment=0)
API to send audio/video stream into the sink.
Definition: priv_aamp.cpp:7018
IsoBmffProcessor::pushInitSegment
void pushInitSegment(double position)
Push init fragment cached earlier.
Definition: isobmffprocessor.cpp:304
IsoBmffProcessor::setRate
void setRate(double rate, PlayMode mode) override
Set playback rate.
Definition: isobmffprocessor.cpp:268
AampLogManager
AampLogManager Class.
Definition: AampLogManager.h:150
IsoBmffProcessor::setBasePTS
void setBasePTS(uint64_t pts, uint32_t tScale)
Set base PTS and TimeScale value.
Definition: isobmffprocessor.cpp:276
IsoBmffProcessor::sendSegment
bool sendSegment(char *segment, size_t &size, double position, double duration, bool discontinuous, bool &ptsError) override
Process and send ISOBMFF fragment.
Definition: isobmffprocessor.cpp:69
MediaType
MediaType
Media types.
Definition: AampMediaType.h:37
IsoBmffProcessorType
IsoBmffProcessorType
ISOBMFF Processor types.
Definition: isobmffprocessor.h:37
IsoBmffProcessor
Class for ISO BMFF Fragment Processor.
Definition: isobmffprocessor.h:47
IsoBmffBuffer
Class for ISO BMFF Buffer.
Definition: isobmffbuffer.h:39
IsoBmffProcessor::abort
void abort() override
Abort all operations.
Definition: isobmffprocessor.cpp:241
PrivateInstanceAAMP::SendStreamCopy
void SendStreamCopy(MediaType mediaType, const void *ptr, size_t len, double fpts, double fdts, double fDuration)
API to send audio/video stream into the sink.
Definition: priv_aamp.cpp:7010
AAMPLOG_TRACE
#define AAMPLOG_TRACE(FORMAT,...)
AAMP logging defines, this can be enabled through setLogLevel() as per the need.
Definition: AampLogManager.h:83
GrowableBuffer
Structure of GrowableBuffer.
Definition: AampMemoryUtils.h:39
PrivateInstanceAAMP
Class representing the AAMP player's private instance, which is not exposed to outside world.
Definition: priv_aamp.h:640
PrivateInstanceAAMP::NotifyFirstVideoPTS
void NotifyFirstVideoPTS(unsigned long long pts, unsigned long timeScale=90000)
Receives first video PTS of the current playback.
Definition: priv_aamp.cpp:9168
PrivateInstanceAAMP::SendErrorEvent
void SendErrorEvent(AAMPTuneFailure tuneFailure, const char *description=NULL, bool isRetryEnabled=true, int32_t secManagerClassCode=-1, int32_t secManagerReasonCode=-1, int32_t secClientBusinessStatus=-1)
Handles errors and sends events to application if required. For download failures,...
Definition: priv_aamp.cpp:2454
IsoBmffProcessor::cacheInitSegment
void cacheInitSegment(char *segment, size_t size)
Cache init fragment internally.
Definition: isobmffprocessor.cpp:290
aamp_AppendBytes
void aamp_AppendBytes(struct GrowableBuffer *buffer, const void *ptr, size_t len)
append data to GrowableBuffer ADT
Definition: AampMemoryUtils.cpp:108
IsoBmffProcessor::IsoBmffProcessor
IsoBmffProcessor(class PrivateInstanceAAMP *aamp, AampLogManager *logObj=NULL, IsoBmffProcessorType trackType=eBMFFPROCESSOR_TYPE_VIDEO, IsoBmffProcessor *peerBmffProcessor=NULL)
IsoBmffProcessor constructor.
Definition: isobmffprocessor.cpp:37
PrivateInstanceAAMP::FlushStreamSink
void FlushStreamSink(double position, double rate)
Sending a flushing seek to stream sink with given position.
Definition: priv_aamp.cpp:9271
PrivateInstanceAAMP::NotifyVideoBasePTS
void NotifyVideoBasePTS(unsigned long long basepts, unsigned long timeScale=90000)
Notifies base PTS of the HLS video playback.
Definition: priv_aamp.cpp:9179
IsoBmffProcessor::setPeerProcessor
void setPeerProcessor(IsoBmffProcessor *processor)
Set peer instance of IsoBmffProcessor.
Definition: isobmffprocessor.h:128
isobmffprocessor.h
Header file for ISO Base Media File Format Fragment Processor.