AVPKit
Stream.cpp
1 /*******************************************************************************
2  * Copyright (c) 2024, 2026, Olivier Ayache. All rights reserved.
3  *
4  * This file is part of AVPKit.
5  *
6  * AVPKit is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * AVPKit is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with AVPKit. If not, see <http://www.gnu.org/licenses/>.
18  *******************************************************************************/
19 
20 #include <cstring>
21 #include <stdexcept>
22 
23 #include <com/avpkit/ferry/Logger.h>
24 #include <com/avpkit/ferry/RefPointer.h>
25 
26 #include <com/avpkit/core/Global.h>
27 #include <com/avpkit/core/Stream.h>
28 #include <com/avpkit/core/Rational.h>
29 #include <com/avpkit/core/StreamCoder.h>
30 #include <com/avpkit/core/Container.h>
31 #include <com/avpkit/core/MetaData.h>
32 #include <com/avpkit/core/Packet.h>
33 #include <com/avpkit/core/IIndexEntry.h>
34 
35 #include "FfmpegIncludes.h"
36 
37 VS_LOG_SETUP(VS_CPP_PACKAGE);
38 
39 namespace com { namespace avpkit { namespace core
40 {
41 
42  Stream :: Stream()
43  {
44  mStream = 0;
45  mBsfContext = 0;
46  mDirection = INBOUND;
47  mCoder = 0;
48  mContainer = 0;
49  mLastDts = Global::NO_PTS;
50  }
51 
52  Stream :: ~Stream()
53  {
54  reset();
55  }
56 
57  void
58  Stream :: reset()
59  {
60  VS_ASSERT("should be a valid object", getCurrentRefCount() >= 1);
61  mMetaData.reset();
62  // As of recent (March 2011) builds of FFmpeg, Stream objects
63  // are cleaned up by the new avformat_free_context method in the
64  // Container, so the outbound check for freeing memory is no
65  // longer required.
66  VS_REF_RELEASE(mCoder);
67  mStream = 0;
68  // We don't keep a reference to the container to avoid a ref-count loop
69  // and so we don't release.
70  mContainer = 0;
71  if (mBsfContext) {
72  av_bsf_free(&mBsfContext);
73  }
74  }
75  Stream*
76  Stream :: make(Container *container, AVStream * aStream, Direction direction,const AVCodec* avCodec)
77  {
78  // note: make will acquire this for us.
79  Stream *newStream = 0;
80  if (aStream) {
81  try {
82  newStream = make();
83  newStream->mStream = aStream;
84  newStream->mDirection = direction;
85 
86  newStream->mCoder = StreamCoder::make(
87  direction == INBOUND?IStreamCoder::DECODING : IStreamCoder::ENCODING,
88  direction == INBOUND?aStream->codecpar : NULL,
89  avCodec,
90  newStream);
91  newStream->mContainer = container;
92  } catch (std::bad_alloc & e) {
93  VS_REF_RELEASE(newStream);
94  throw e;
95  }
96  }
97  return newStream;
98  }
99 
100  int
102  {
103  return (mStream ? mStream->index : -1);
104  }
105 
106  int
108  {
109  return (mStream ? mStream->id : -1);
110  }
111 
112  IStreamCoder *
114  {
115  StreamCoder *retval = 0;
116 
117  // acquire a reference for the caller
118  retval = mCoder;
119  VS_REF_ACQUIRE(retval);
120 
121  return retval;
122  }
123 
124  IRational *
126  {
127  IRational * result = 0;
128  if (mStream)
129  {
130  AVRational f = av_guess_frame_rate(NULL, mStream, NULL);
131  result = Rational::make(&f);
132  }
133  return result;
134  }
135 
136  IRational *
138  {
139  IRational * result = 0;
140  if (mStream)
141  {
142  result = Rational::make(&mStream->time_base);
143  }
144  return result;
145  }
146  void
147  Stream :: setTimeBase(IRational* src)
148  {
149  if (mStream && src)
150  {
151  mStream->time_base.den = src->getDenominator();
152  mStream->time_base.num = src->getNumerator();
153  }
154  }
155  void
156  Stream :: setFrameRate(IRational* src)
157  {
158  if (mStream && src)
159  {
160  mStream->r_frame_rate.den = src->getDenominator();
161  mStream->r_frame_rate.num = src->getNumerator();
162  }
163  }
164 
165  int64_t
167  {
168  return (mStream ? mStream->start_time : Global::NO_PTS);
169  }
170 
171  int64_t
173  {
174  return (mStream ? mStream->duration : Global::NO_PTS);
175  }
176 
177  int64_t
179  {
180  return (mStream ? mStream->cur_dts : Global::NO_PTS);
181  }
182 
183  int64_t
185  {
186  return (mStream ? mStream->nb_frames : 0);
187  }
188  int
190  {
191  return (mStream ? mStream->nb_index_entries : 0);
192  }
193  int
194  Stream :: containerClosed(Container *)
195  {
196  // let the coder know we're closed.
197  if (mCoder)
198  mCoder->streamClosed(this);
199  reset();
200  return 0;
201  }
202 
203  int32_t
205  {
206  int retval = 0;
207  retval = RefCounted::acquire();
208  VS_LOG_TRACE("Acquired %p: %d", this, retval);
209  return retval;
210  }
211 
212  int32_t
214  {
215  int retval = 0;
216  retval = RefCounted::release();
217  VS_LOG_TRACE("Released %p: %d", this, retval);
218  return retval;
219  }
220 
221  IRational*
223  {
224  IRational* retval = 0;
225  if (mStream)
226  {
227  retval = IRational::make(
228  mStream->sample_aspect_ratio.num,
229  mStream->sample_aspect_ratio.den);
230  }
231  return retval;
232  }
233 
234  void
236  {
237  if (aNewValue && mStream)
238  {
239  mStream->sample_aspect_ratio.num =
240  aNewValue->getNumerator();
241  mStream->sample_aspect_ratio.den =
242  aNewValue->getDenominator();
243  }
244  return;
245  }
246 
247  const char*
249  {
250  const char*retval = 0;
252  if (metaData) {
253  retval = metaData->getValue("language", IMetaData::METADATA_NONE);
254  }
255  return retval;
256  }
257 
258  void
259  Stream :: setLanguage(const char* aNewValue)
260  {
262  if (metaData) {
263  metaData->setValue("language", aNewValue);
264  }
265  return;
266  }
267 
268  IContainer*
270  {
271  // add ref for caller
272  VS_REF_ACQUIRE(mContainer);
273  return mContainer;
274  }
275 
276  int32_t
278  {
279  return setStreamCoder(coder, true);
280  }
281  int32_t
282  Stream :: setStreamCoder(IStreamCoder *aCoder, bool assumeOnlyStream)
283  {
284  int32_t retval = -1;
285  try
286  {
287  if (mCoder && mCoder->isOpen())
288  throw std::runtime_error("cannot call setStreamCoder when current coder is open");
289 
290  if (!aCoder)
291  throw std::runtime_error("cannot set to a null stream coder");
292 
293  StreamCoder *coder = dynamic_cast<StreamCoder*>(aCoder);
294  if (!coder)
295  throw std::runtime_error("IStreamCoder is not of expected underlying C++ type");
296 
297  // Close the old stream coder
298  if (mCoder)
299  {
300  mCoder->streamClosed(this);
301  }
302 
303  if (coder->setStream(this, assumeOnlyStream) < 0)
304  throw std::runtime_error("IStreamCoder doesn't like this stream");
305 
306  VS_REF_RELEASE(mCoder);
307  mCoder = coder;
308  VS_REF_ACQUIRE(mCoder);
309  retval = 0;
310  }
311  catch (std::exception & e)
312  {
313  VS_LOG_ERROR("Error: %s", e.what());
314  retval = -1;
315  }
316  return retval;
317  }
318 
321  {
322  if (mStream) {
323  return (IStream::ParseType)mStream->need_parsing;
324  } else {
325  return IStream::PARSE_NONE;
326  }
327  }
328 
329  void
331  {
332  if (mStream) {
333  mStream->need_parsing = (enum AVStreamParseType)type;
334  }
335  }
336 
337  int
339  {
340 
341  int ret = -1;
342  if (mStream) {
343  if (!mBsfContext) {
344  const AVBitStreamFilter* filter = av_bsf_get_by_name(name);
345  if (filter) {
346  if (ret = av_bsf_alloc(filter, &mBsfContext) == 0) {
347  if (ret = avcodec_parameters_copy(mBsfContext->par_in, mStream->codecpar) >= 0) {
348  mBsfContext->time_base_in = mStream->time_base;
349  if (ret = av_bsf_init(mBsfContext) < 0) {
350  VS_LOG_ERROR("BSF INIT FAIL");
351  av_bsf_free(&mBsfContext);
352  return ret;
353  }
354  VS_LOG_ERROR("BSF OK");
355  }
356  }
357  }
358 
359  } else {
360  VS_LOG_ERROR("Bistream filter already set on this stream");
361  }
362  }
363 
364  return ret;
365  }
366 
367  IMetaData*
369  {
370  if (!mMetaData && mStream)
371  {
372  if (mDirection == IStream::OUTBOUND)
373  mMetaData = MetaData::make(&mStream->metadata);
374  else
375  // make a read-only copy so when libav deletes the
376  // input version we don't delete our copy
377  mMetaData = MetaData::make(mStream->metadata);
378  }
379  return mMetaData.get();
380  }
381 
382  void
384  {
385  MetaData* data = dynamic_cast<MetaData*>(getMetaData());
386  if (data) {
387  data->copy(copy);
388  // release for the get above
389  data->release();
390  }
391  return;
392  }
393 
394  int32_t
396  {
397  if (!packet)
398  return -1;
399 
400 // VS_LOG_DEBUG("input: duration: %lld; dts: %lld; pts: %lld;",
401 // packet->getDuration(), packet->getDts(), packet->getPts());
402 
403  // Always just reset this; cheaper than checking if it's
404  // already set
405  packet->setStreamIndex(this->getIndex());
406 
409  if (!thisBase || !packetBase)
410  return -1;
411  if (thisBase->compareTo(packetBase.value()) == 0) {
412 // VS_LOG_DEBUG("Same timebase: %d/%d vs %d/%d",
413 // thisBase->getNumerator(), thisBase->getDenominator(),
414 // packetBase->getNumerator(), packetBase->getDenominator());
415  // it's already got the right time values
416  return 0;
417  }
418 
419  int64_t duration = packet->getDuration();
420  int64_t dts = packet->getDts();
421  int64_t pts = packet->getPts();
422 
423  if (duration >= 0)
424  duration = thisBase->rescale(duration, packetBase.value(),
425  IRational::ROUND_DOWN);
426 
427  if (pts != Global::NO_PTS)
428  {
429  pts = thisBase->rescale(pts, packetBase.value(), IRational::ROUND_DOWN);
430  }
431  if (dts != Global::NO_PTS)
432  {
433  dts = thisBase->rescale(dts, packetBase.value(), IRational::ROUND_DOWN);
434  if (mLastDts != Global::NO_PTS && dts == mLastDts)
435  {
436  // adjust for rounding; we never want to insert a frame that
437  // is not monotonically increasing. Note we only do this if
438  // we're off by one; that's because we ROUND_DOWN and then assume
439  // that can be off by at most one. If we're off by more than one
440  // then it's really an error on the person muxing to this stream.
441  dts = mLastDts+1;
442  // and round up pts
443  if (pts != Global::NO_PTS)
444  ++pts;
445  // and if after all that adjusting, pts is less than dts
446  // let dts win.
447  if (pts == Global::NO_PTS || pts < dts)
448  pts = dts;
449  }
450  mLastDts = dts;
451  }
452 
453 // VS_LOG_DEBUG("output: duration: %lld; dts: %lld; pts: %lld;",
454 // duration, dts, pts);
455  packet->setDuration(duration);
456  packet->setPts(pts);
457  packet->setDts(dts);
458  packet->setTimeBase(thisBase.value());
459 // VS_LOG_DEBUG("Reset timebase: %d/%d",
460 // thisBase->getNumerator(), thisBase->getDenominator());
461  return 0;
462  }
463 
464  IIndexEntry*
465  Stream :: findTimeStampEntryInIndex(int64_t wantedTimeStamp, int32_t flags)
466  {
467  int32_t index = findTimeStampPositionInIndex(wantedTimeStamp, flags);
468  // getIndexEntry will check for a negative index and return null if so
469  return getIndexEntry(index);
470  }
471  int32_t
472  Stream :: findTimeStampPositionInIndex(int64_t wantedTimeStamp,
473  int32_t flags)
474  {
475  int retval = -1;
476  if (mStream) {
477  retval = av_index_search_timestamp(
478  mStream,
479  wantedTimeStamp,
480  flags);
481  }
482  return retval;
483  }
484 
485  IIndexEntry*
486  Stream :: getIndexEntry(int32_t index)
487  {
488  IIndexEntry* retval = 0;
489  if (mStream->index_entries
490  && index >= 0
491  && index < mStream->nb_index_entries)
492  {
493  AVIndexEntry* entry = &(mStream->index_entries[index]);
494  if (entry) {
495  retval = IIndexEntry::make(
496  entry->pos,
497  entry->timestamp,
498  entry->flags,
499  entry->size,
500  entry->min_distance
501  );
502  }
503  }
504  return retval;
505  }
506  int32_t
508  {
509  if (!entry)
510  return -1;
511  if (!mStream)
512  return -1;
513  return av_add_index_entry(mStream,
514  entry->getPosition(),
515  entry->getTimeStamp(),
516  entry->getSize(),
517  entry->getMinDistance(),
518  entry->getFlags());
519  }
520 
521  void
522  Stream :: setId(int32_t aId)
523  {
524  if (!mStream)
525  return;
526  mStream->id = aId;
527  }
528 }}}
static const int64_t NO_PTS
A value that means no time stamp is set for a given object.
Definition: Global.h:50
A file (or network data source) that contains one or more IStream objects of audio and video data.
Definition: IContainer.h:100
An index entry for a IStream.
Definition: IIndexEntry.h:65
virtual int32_t getMinDistance()=0
Minimum number of index entries between this index entry and the last keyframe in the index,...
virtual int32_t getSize()=0
The size of bytes of the frame this index entry points to.
virtual int64_t getPosition()=0
The position in bytes of the frame corresponding to this index entry in the IContainer.
virtual int64_t getTimeStamp()=0
The actual time stamp, in units of IStream#getTimeBase(), of the frame this entry points to.
virtual int32_t getFlags()=0
Flags set for this entry.
static IIndexEntry * make(int64_t position, int64_t timeStamp, int32_t flags, int32_t size, int32_t minDistance)
Create a new IIndexEntry with the specified values.
Definition: IIndexEntry.cpp:44
virtual void setTimeBase(IRational *aBase)=0
Set the time base that time stamps of this object are represented in.
virtual IRational * getTimeBase()=0
Get the time base that time stamps of this object are represented in.
Get MetaData about a IContainer or IStream.
Definition: IMetaData.h:51
@ METADATA_NONE
For getValue(String) case-insensitive match of key.
Definition: IMetaData.h:60
static IMetaData * make()
Create a new IMetaData bag of properties with no values set.
Definition: IMetaData.cpp:36
Represents an encoded piece of data that can be placed in an IContainer for a given IStream of data.
Definition: IPacket.h:50
virtual void setStreamIndex(int32_t streamIndex)=0
Set the stream index for this packet.
virtual int64_t getDuration()=0
Return the duration of this packet, in units of getTimeBase().
virtual void setDts(int64_t aDts)=0
Set a new Decompression Time Stamp (DTS) for this packet.
virtual int64_t getDts()=0
Get the Decompression Time Stamp (DTS) for this packet.
virtual void setPts(int64_t aPts)=0
Set a new Presentation Time Stamp (PTS) for this packet.
virtual void setDuration(int64_t duration)=0
Set the duration.
virtual int64_t getPts()=0
Get the Presentation Time Stamp (PTS) for this packet.
This class wraps represents a Rational number for the AVPKit.
Definition: IRational.h:43
static IRational * make()
Get a new rational that will be set to 0/0.
Definition: IRational.cpp:79
virtual int32_t getDenominator()=0
Get the denominator for this rational.
virtual int32_t getNumerator()=0
Get the numerator for this rational.
The work horse of the AVPKit: Takes IPacket data from an IContainer (representing an IStream) and an ...
Definition: IStreamCoder.h:45
ParseType
What types of parsing can we do on a call to IContainer#readNextPacket(IPacket).
Definition: IStream.h:222
int32_t copy(IMetaData *copy)
Destroys the current data, and copies all data from copy.
Definition: MetaData.cpp:164
static StreamCoder * make(Direction direction)
This method creates a StreamCoder that is not tied to any container or stream.
virtual bool isOpen()
Returns true if this IStreamCoder is currently open.
virtual int32_t acquire()
Internal Only.
Definition: Stream.cpp:204
virtual IContainer * getContainer()
Get the underlying container for this stream, or null if AVPKit doesn't know.
Definition: Stream.cpp:269
virtual int64_t getDuration()
Return the duration, in getTimeBase() units, of this stream, or Global#NO_PTS if unknown.
Definition: Stream.cpp:172
virtual IRational * getTimeBase()
The time base in which all timestamps (e.g.
Definition: Stream.cpp:137
virtual int getNumIndexEntries()
Get the number of index entries in this stream.
Definition: Stream.cpp:189
virtual int getId()
Return a container format specific id for this stream.
Definition: Stream.cpp:107
virtual IRational * getFrameRate()
Get the (sometimes estimated) frame rate of this container.
Definition: Stream.cpp:125
virtual IMetaData * getMetaData()
Get the IMetaData for this object, or null if none.
Definition: Stream.cpp:368
virtual IStream::ParseType getParseType()
Get how the decoding codec should parse data from this stream.
Definition: Stream.cpp:320
virtual int64_t getCurrentDts()
The current Decompression Time Stamp that will be used on this stream, in getTimeBase() units.
Definition: Stream.cpp:178
virtual IIndexEntry * findTimeStampEntryInIndex(int64_t wantedTimeStamp, int32_t flags)
Search for the given time stamp in the key-frame index for this IStream.
Definition: Stream.cpp:465
virtual int32_t stampOutputPacket(IPacket *packet)
Takes a packet destined for this stream, and stamps the stream index, and converts the time stamp to ...
Definition: Stream.cpp:395
virtual void setLanguage(const char *language)
Set the 4-character language setting for this stream.
Definition: Stream.cpp:259
virtual int getIndex()
Get the relative position this stream has in the hosting IContainer object.
Definition: Stream.cpp:101
virtual int64_t getStartTime()
Return the start time, in getTimeBase() units, when this stream started.
Definition: Stream.cpp:166
virtual int setBitstreamFilter(const char *name)
Set a bitstream filter on this stream.
Definition: Stream.cpp:338
virtual void setMetaData(IMetaData *metaData)
Set the IMetaData on this object, overriding any previous meta data.
Definition: Stream.cpp:383
virtual IStreamCoder * getStreamCoder()
Get the StreamCoder than can manipulate this stream.
Definition: Stream.cpp:113
virtual int32_t setStreamCoder(IStreamCoder *coder)
Sets the stream coder to use for this stream.
Definition: Stream.cpp:277
virtual const char * getLanguage()
Get the 4-character language setting for this stream.
Definition: Stream.cpp:248
virtual int32_t findTimeStampPositionInIndex(int64_t wantedTimeStamp, int32_t flags)
Search for the given time stamp in the key-frame index for this IStream.
Definition: Stream.cpp:472
virtual int32_t addIndexEntry(IIndexEntry *entry)
Adds an index entry into the stream's sorted index list.
Definition: Stream.cpp:507
void setId(int32_t id)
Set the format-specific stream id.
Definition: Stream.cpp:522
virtual IRational * getSampleAspectRatio()
Added in 1.17.
Definition: Stream.cpp:222
virtual int64_t getNumFrames()
Returns the number of encoded frames if known.
Definition: Stream.cpp:184
virtual void setParseType(ParseType type)
Set the parse type the decoding codec should use.
Definition: Stream.cpp:330
virtual int32_t release()
Internal Only.
Definition: Stream.cpp:213
virtual IIndexEntry * getIndexEntry(int32_t position)
Get the IIndexEntry at the given position in this IStream object's index.
Definition: Stream.cpp:486
virtual void setSampleAspectRatio(IRational *newRatio)
Sets the sample aspect ratio.
Definition: Stream.cpp:235
virtual int32_t acquire()
Internal Only.
Definition: RefCounted.cpp:63
virtual int32_t release()
Internal Only.
Definition: RefCounted.cpp:70
virtual int32_t getCurrentRefCount()
Return the current reference count on this object.
Definition: RefCounted.cpp:80
This class is only useful from C++.
Definition: RefPointer.h:47
T * value()
Return the managed pointer without calling RefCounted::acquire() on it.
Definition: RefPointer.h:226
WARNING: Do not use logging in this class, and do not set any static file variables to values other t...