AVPKit
Container.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 <stdexcept>
21 
22 // for strncpy
23 #include <cstring>
24 
25 //#define attribute_deprecated
26 
27 #include <com/avpkit/ferry/JNIHelper.h>
28 #include <com/avpkit/ferry/Logger.h>
29 #include <com/avpkit/core/Container.h>
30 #include <com/avpkit/core/ContainerFormat.h>
31 #include <com/avpkit/core/Stream.h>
32 #include <com/avpkit/core/Packet.h>
33 #include <com/avpkit/core/Global.h>
34 #include <com/avpkit/core/Property.h>
35 #include <com/avpkit/core/MetaData.h>
36 #include <com/avpkit/ferry/IBuffer.h>
37 #include <com/avpkit/core/io/URLProtocolManager.h>
38 VS_LOG_SETUP(VS_CPP_PACKAGE);
39 
40 #define AVPKIT_CHECK_INTERRUPT(retval) do { \
41  if ((retval) < 0) { \
42  JNIHelper* helper = JNIHelper::getHelper(); \
43  if (helper && helper->isInterrupted()) \
44  (retval) = AVERROR(EINTR); \
45  } \
46 } while(0)
47 
48 using namespace com::avpkit::ferry;
49 using namespace com::avpkit::core::io;
50 
53 int
54 Container_url_read(void*h, unsigned char* buf, int size)
55 {
56  int retval = -1;
57  try {
59  if (handler)
60  retval = handler->url_read(buf,size);
61  } catch (...)
62  {
63  retval = -1;
64  }
65  VS_LOG_TRACE("URLProtocolHandler[%p]->url_read(%p, %d) ==> %d", h, buf, size, retval);
66  return retval;
67 }
68 int
69 Container_url_write(void*h, unsigned char* buf, int size)
70 {
71  int retval = -1;
72  try {
74  if (handler)
75  retval = handler->url_write(buf,size);
76  } catch (...)
77  {
78  retval = -1;
79  }
80  VS_LOG_TRACE("URLProtocolHandler[%p]->url_write(%p, %d) ==> %d", h, buf, size, retval);
81  return retval;
82 }
83 int64_t
84 Container_url_seek(void*h, int64_t position, int whence)
85 {
86  int64_t retval = -1;
87  try {
89  if (handler)
90  retval = handler->url_seek(position,whence);
91  } catch (...)
92  {
93  retval = -1;
94  }
95  VS_LOG_TRACE("URLProtocolHandler[%p]->url_seek(%p, %lld) ==> %d", h, position, whence, retval);
96  return retval;
97 }
98 
99 int
100 Container_io_open(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options)
101 {
102  //TODO: verify s->opaque and if flags <4 and >0
103  int retval = -1;
104  try {
106  if (factory) {
107  URLProtocolHandler* handler = factory->getHandler(url, flags);
108  if (handler) {
109  handler->url_open(url, flags);
110  // alloc the input buffer to 4k buffer
111  int bufferLength = 4096;
112  uint8_t* buffer = (uint8_t*) av_malloc(bufferLength);
113  if (!buffer)
114  throw std::bad_alloc();
115 
116  // we will allocate ourselves an io context; ownership of buffer passes here.
117  *pb = avio_alloc_context(
118  buffer,
119  bufferLength,
120  flags == 1 ? 0 : 1,
121  handler,
122  Container_url_read,
123  Container_url_write,
124  handler->url_seekflags(url, 0) == URLProtocolHandler::SK_NOT_SEEKABLE ? NULL : Container_url_seek); //Container_url_seek is necessary for some media (mov format)
125  if (!*pb) {
126  av_freep(&buffer);
127  return AVERROR(ENOMEM);
128  }
129  return 0;
130  }
131  }
132  } catch (...) {
133  retval = -1;
134  }
135  return retval;
136 }
137 
138 void
139 Container_io_close(struct AVFormatContext *s, AVIOContext *pb)
140 {
141  try {
142  avio_flush(pb);
143  if (pb->opaque) {
144  URLProtocolHandler* handler = (URLProtocolHandler*)pb->opaque;
145  handler->url_close();
146  }
147  } catch (...)
148  {
149  }
150  if (pb->opaque)
151  delete pb->opaque;
152  av_freep(&pb->buffer);
153  avio_context_free(&pb);
154 }
155 
156 namespace com { namespace avpkit { namespace core
157 {
158 
159  Container :: Container()
160  {
161  VS_LOG_TRACE("Making container: %p", this);
162  mPacket = av_packet_alloc();
163  mBsfPacket = av_packet_alloc();
164  mFormatContext = avformat_alloc_context();
165  if (!mFormatContext)
166  throw std::bad_alloc();
167  // Set up thread interrupt handling.
168  mFormatContext->interrupt_callback.callback = Global::avioInterruptCB;
169  mFormatContext->interrupt_callback.opaque = this;
170  mIsOpened = false;
171  mIsMetaDataQueried=false;
172  mNeedTrailerWrite = false;
173  mNumStreams = 0;
174  mInputBufferLength = 0;
175  mReadRetryCount = 1;
176  mCustomIOHandler = 0;
177  }
178 
179  Container :: ~Container()
180  {
181  reset();
182  resetContext();
183  if (mPacket) {
184  av_packet_free(&mPacket);
185  }
186  if (mBsfPacket) {
187  av_packet_free(&mBsfPacket);
188  }
189  VS_LOG_TRACE("Destroyed container: %p", this);
190  }
191 
192  void
193  Container :: resetContext()
194  {
195  if (mFormatContext) {
196  if (mCustomIOHandler) {
197  if (mFormatContext->pb)
198  av_freep(&mFormatContext->pb->buffer);
199  av_freep(&mFormatContext->pb);
200  }
201  if (mFormatContext->opaque) {
202  delete mFormatContext->opaque;
203  mFormatContext->opaque = NULL;
204  }
205  avformat_free_context(mFormatContext);
206  mFormatContext = 0;
207  }
208  }
209  void
210  Container :: reset()
211  {
212  mMetaData.reset();
213  if (mIsOpened)
214  {
215  VS_LOG_DEBUG("Closing dangling Container");
216  (void) this->close(true);
217  }
218  if (mCustomIOHandler) {
219  if (mFormatContext) {
220  if(mFormatContext->pb)
221  av_freep(&mFormatContext->pb->buffer);
222  av_freep(&mFormatContext->pb);
223  }
224  delete mCustomIOHandler;
225  }
226  mCustomIOHandler = 0;
227  }
228 
229  AVFormatContext *
230  Container :: getFormatContext()
231  {
232  return mFormatContext;
233  }
234 
235  int32_t
236  Container :: setInputBufferLength(int32_t size)
237  {
238  int32_t retval = -1;
239  if (mIsOpened)
240  {
241  VS_LOG_WARN("Attempting to set input buffer length while file is opened; ignoring");
242  }
243  else
244  {
245  mInputBufferLength = size;
246  retval = size;
247  }
248  return retval;
249  }
250 
251  int32_t
252  Container :: getInputBufferLength()
253  {
254  return mInputBufferLength;
255  }
256 
257  bool
258  Container :: isOpened()
259  {
260  return mIsOpened;
261  }
262 
263  bool
264  Container :: isHeaderWritten()
265  {
266  return (mIsOpened && mNeedTrailerWrite);
267  }
268 
269  int32_t
270  Container :: open(const char *url,Type type,
271  IContainerFormat *pContainerFormat)
272  {
273  return open(url, type, pContainerFormat, false, true);
274  }
275 
276  int32_t
277  Container :: open(const char *url,Type type,
278  IContainerFormat *pContainerFormat,
279  bool aStreamsCanBeAddedDynamically,
280  bool aLookForAllStreams)
281  {
282  return open(url, type, pContainerFormat,
283  aStreamsCanBeAddedDynamically, aLookForAllStreams,
284  NULL, NULL);
285  }
286  int32_t
287  Container :: open(const char *url,Type type,
288  IContainerFormat *pContainerFormat,
289  bool aStreamsCanBeAddedDynamically,
290  bool aLookForAllStreams,
291  IMetaData* aOptions,
292  IMetaData* aUnsetOptions)
293  {
294  AVDictionary *tmp= NULL;
295 
296  int32_t retval = -1;
297 
298  // reset if an open is called before a close.
299  reset();
300  if (!mFormatContext)
301  {
302  // always reset to a new one
303  mFormatContext = avformat_alloc_context();
304  if (!mFormatContext)
305  throw std::bad_alloc();
306  }
307 
308  if (pContainerFormat)
309  setFormat(pContainerFormat);
310 
311  // Set up thread interrupt handling.
312  mFormatContext->interrupt_callback.callback = Global::avioInterruptCB;
313  mFormatContext->interrupt_callback.opaque = this;
314 
315  // Let's check for custom IO
316  mCustomIOHandler = URLProtocolManager::findHandler(
317  url,
318  type == WRITE ? URLProtocolHandler::URL_WRONLY_MODE : URLProtocolHandler::URL_RDONLY_MODE,
319  0);
320  if (mCustomIOHandler) {
321  if (mInputBufferLength <= 0)
322  // default to 4k
323  mInputBufferLength = 4096;
324  // free and realloc the input buffer length
325  uint8_t* buffer = (uint8_t*)av_malloc(mInputBufferLength);
326  if (!buffer)
327  throw std::bad_alloc();
328 
329  // we will allocate ourselves an io context; ownership of buffer passes here.
330  mFormatContext->pb = avio_alloc_context(
331  buffer,
332  mInputBufferLength,
333  type == WRITE ? 1 : 0,
334  mCustomIOHandler,
335  Container_url_read,
336  Container_url_write,
337  mCustomIOHandler->url_seekflags(url,0) == URLProtocolHandler::SK_NOT_SEEKABLE ? NULL : Container_url_seek);//Container_url_seek is necessary for some media (mov format)
338  if (!mFormatContext->pb)
339  av_freep(&buffer);
340  }
341  // set up our options
342  if (aOptions) {
343  MetaData* options = static_cast<MetaData*>(aOptions);
344  if (!options)
345  throw std::runtime_error("um, this shouldn't ever happen");
346  // make a copy of the data returned
347  av_dict_copy(&tmp, options->getDictionary(), 0);
348  }
349  if (url && *url)
350  {
351  if (type == WRITE)
352  {
353  retval = openOutputURL(url,
354  aStreamsCanBeAddedDynamically, &tmp);
355  } else if (type == READ)
356  {
357  retval = openInputURL(url,
358  aStreamsCanBeAddedDynamically, aLookForAllStreams, &tmp);
359  }
360  else
361  {
362  VS_ASSERT(false, "Invalid type for open");
363  retval = -1;
364  }
365  }
366  AVPKIT_CHECK_INTERRUPT(retval);
367  if (aUnsetOptions) {
368  MetaData* unsetOptions = static_cast<MetaData*>(aUnsetOptions);
369  if (!unsetOptions)
370  throw std::runtime_error("a little part of me just died inside");
371  unsetOptions->copy(tmp);
372  }
373  if (tmp)
374  av_dict_free(&tmp);
375  return retval;
376  }
377 
378  int32_t
379  Container :: setCustomIOProtocol(io::URLProtocolHandlerFactory* factory)
380  {
381  mFormatContext->opaque = (void*)factory;
382  mFormatContext->io_open = ::Container_io_open;
383  mFormatContext->io_close = ::Container_io_close;
384  return 0;
385  }
386 
387  IStreamCoder::CodecStandardsCompliance Container::getStandardsCompliance()
388  {
389  return mFormatContext ? (IStreamCoder::CodecStandardsCompliance) mFormatContext->strict_std_compliance : IStreamCoder::COMPLIANCE_NORMAL;
390  }
391 
392 
393  int32_t Container::setStandardsCompliance(IStreamCoder::CodecStandardsCompliance compliance)
394  {
395  if (mFormatContext)
396  {
397  mFormatContext->strict_std_compliance = compliance;
398  return 0;
399  }
400  return -1;
401  }
402 
403 
405  Container :: getContainerFormat()
406  {
407  ContainerFormat *retval = ContainerFormat::make();
408  if (retval)
409  {
410  if (mFormatContext)
411  {
412  if (mFormatContext->iformat)
413  retval->setInputFormat(mFormatContext->iformat);
414  if (mFormatContext->oformat)
415  retval->setOutputFormat(mFormatContext->oformat);
416  }
417  }
418  return retval;
419  }
420 
421  int32_t
422  Container:: setupAllInputStreams()
423  {
424  // do nothing if we're already all set up.
425  if (mNumStreams == mFormatContext->nb_streams)
426  return 0;
427 
428  int32_t retval = -1;
429  // loop through and find the first non-zero time base
430  AVRational *goodTimebase = 0;
431  for(uint32_t i = 0;i < mFormatContext->nb_streams;i++){
432  AVStream *avStream = mFormatContext->streams[i];
433  if(avStream && avStream->time_base.num && avStream->time_base.den){
434  goodTimebase = &avStream->time_base;
435  break;
436  }
437  }
438 
439  // Only look for new streams
440  for (uint32_t i =mNumStreams ; i < mFormatContext->nb_streams; i++)
441  {
442  AVStream *avStream = mFormatContext->streams[i];
443  if (avStream)
444  {
445  if (goodTimebase && (!avStream->time_base.num || !avStream->time_base.den))
446  {
447  avStream->time_base = *goodTimebase;
448  }
449 
451  Stream::make(this, avStream,
452  (this->getType() == READ ?
453  IStream::INBOUND : IStream::OUTBOUND
454  ), 0)
455  );
456 
457  if (stream)
458  {
459  if (stream->value())
460  {
461  mStreams.push_back(stream);
462  mNumStreams++;
463  } else {
464  VS_LOG_ERROR("Couldn't make a stream %d", i);
465  delete stream;
466  }
467  stream = 0;
468  }
469  else
470  {
471  VS_LOG_ERROR("Could not make Stream %d", i);
472  retval = -1;
473  }
474  } else {
475  VS_LOG_ERROR("no FFMPEG allocated stream: %d", i);
476  retval = -1;
477  }
478  }
479  return retval;
480  }
481 
482  int32_t
483  Container :: openInputURL(const char *url,
484  bool aStreamsCanBeAddedDynamically,
485  bool aLookForAllStreams,
486  AVDictionary** options)
487  {
488  int32_t retval = -1;
489  AVInputFormat *inputFormat = NULL;
490 
491  try {
492  // We have preallocated the format
493  if (mFormat)
494  {
495  inputFormat = mFormat->getInputFormat();
496  }
497 
498  if (mCustomIOHandler) {
499  retval = mCustomIOHandler->url_open(url, URLProtocolHandler::URL_RDONLY_MODE);
500  } else {
501  // rely on the avformat_open_input call to do the right thing
502  retval = 0;
503  }
504 
505  if (retval >= 0)
506  retval = avformat_open_input(
507  &mFormatContext,
508  url,
509  inputFormat,
510  options
511  );
512  if (retval >= 0) {
513  if (!mFormatContext && mFormatContext->iformat) {
514  // we didn't know the format before, but we sure as hell know it now
515  // so we set it back.
516  RefPointer<ContainerFormat> format = ContainerFormat::make();
517  if (format)
518  {
519  format->setInputFormat(mFormatContext->iformat);
520  setFormat(format.value());
521  }
522  }
523  mIsOpened = true;
524  if (aStreamsCanBeAddedDynamically)
525  {
526  mFormatContext->ctx_flags |= AVFMTCTX_NOHEADER;
527  }
528 
529  if (aLookForAllStreams)
530  {
531  retval = queryStreamMetaData();
532  }
533  } else {
534  VS_LOG_DEBUG("Could not open input url: %s", url);
535  }
536  } catch (std::exception &e) {
537  if (retval >= 0)
538  retval = -1;
539  }
540  return retval;
541  }
542 
543  int32_t
544  Container :: openOutputURL(const char* url,
545  bool aStreamsCanBeAddedDynamically,
546  AVDictionary** options)
547  {
548  int32_t retval = -1;
549  AVOutputFormat *outputFormat = NULL;
550 
551  try {
552  if (mFormat)
553  // ask for it from the container
554  outputFormat = mFormat->getOutputFormat();
555 
556  if (!outputFormat) {
557  // guess it.
558  outputFormat = av_guess_format(NULL, url, NULL);
559  RefPointer<ContainerFormat> format = ContainerFormat::make();
560  if (!format)
561  throw std::bad_alloc();
562 
563  format->setOutputFormat(outputFormat);
564  setFormat(format.value());
565  }
566 
567  if (!outputFormat)
568  throw std::runtime_error("could not find output format");
569 
570  if (aStreamsCanBeAddedDynamically)
571  {
572  mFormatContext->ctx_flags |= AVFMTCTX_NOHEADER;
573  }
574  mFormatContext->oformat = outputFormat;
575  // now because we can guess at the last moment, we need to set up
576  // private options
577  {
578  AVFormatContext *s = mFormatContext;
579  if (!s->priv_data && s->oformat->priv_data_size > 0) {
580  s->priv_data = av_mallocz(s->oformat->priv_data_size);
581  if (!s->priv_data)
582  throw std::bad_alloc();
583  if (s->oformat->priv_class) {
584  *(const AVClass**)s->priv_data= s->oformat->priv_class;
585  av_opt_set_defaults(s->priv_data);
586  }
587  }
588  }
589 
590  // set options
591  retval = av_opt_set_dict(mFormatContext, options);
592  if (retval < 0)
593  throw std::runtime_error("could not set options");
594 
595  if (!(mFormatContext->oformat->flags & AVFMT_NOFILE)) {
596  if (mCustomIOHandler) {
597  retval = mCustomIOHandler->url_open(url, URLProtocolHandler::URL_WRONLY_MODE);
598  } else {
599  retval = avio_open2(&mFormatContext->pb,
600  url, AVIO_FLAG_READ_WRITE,
601  &mFormatContext->interrupt_callback,
602  options
603  );
604  }
605  }
606  if (retval < 0)
607  throw std::runtime_error("could not open file");
608 
609  mIsOpened = true;
610  strncpy(mFormatContext->filename, url, sizeof(mFormatContext->filename)-1);
611  // force a null termination
612  mFormatContext->filename[sizeof(mFormatContext->filename)-1] = 0;
613  } catch (std::exception & e) {
614  VS_LOG_ERROR("URL: %s; Error: %s", url, e.what());
615  if (retval >= 0)
616  retval = -1;
617  }
618  return retval;
619  }
620 
621  IContainer::Type
622  Container :: getType()
623  {
624  return (!mFormatContext ? READ :
625  (mFormatContext->oformat ? WRITE: READ));
626  }
627 
628  int32_t
629  Container :: getNumStreams()
630  {
631  int32_t retval = 0;
632  if (mFormatContext)
633  {
634  if (mFormatContext->nb_streams != mNumStreams)
635  setupAllInputStreams();
636  retval = mFormatContext->nb_streams;
637  }
638  AVPKIT_CHECK_INTERRUPT(retval);
639  return retval;
640  }
641 
642  int32_t
643  Container :: close()
644  {
645  return close(false);
646  }
647 
648  int32_t
649  Container :: close(bool dangling)
650  {
651  int32_t retval = -1;
652  mMetaData.reset();
653 
654  if (mFormatContext && mIsOpened)
655  {
656  VS_ASSERT(mNumStreams == mStreams.size(),
657  "unexpected number of streams");
658 
659  if (mNeedTrailerWrite)
660  {
661  if (dangling)
662  // don't actually write the trailer when dangling; we could
663  // block on that, which could occur inside a finalizer thread
664  // or other unexpected thread
665  VS_LOG_ERROR("Disposing of dangling container but could not write trailer");
666  else {
667  VS_LOG_DEBUG("Writing dangling trailer");
668  (void) this->writeTrailer();
669  }
670  mNeedTrailerWrite = false;
671  }
672  mOpenCoders.clear();
673 
674  while(mStreams.size() > 0)
675  {
676  RefPointer<Stream> * stream=mStreams.back();
677 
678  VS_ASSERT(stream && *stream, "no stream?");
679  if (stream && *stream) {
680  (*stream)->containerClosed(this);
681  delete stream;
682  }
683  mStreams.pop_back();
684  }
685  mNumStreams = 0;
686 
687  // we need to remember the avio context
688  AVIOContext* pb = mFormatContext->pb;
689 
690  if (this->getType() == READ)
691  avformat_close_input(&mFormatContext);
692 
693  if (mCustomIOHandler) {
694  retval = mCustomIOHandler->url_close();
695  if (!mFormatContext) {
696  if (pb)
697  av_freep(&pb->buffer);
698  av_free(pb);
699  }
700  } else if (this->getType() != READ)
701  retval = avio_close(pb);
702  else
703  retval = 0;
704 
705  resetContext();
706  mIsOpened = false;
707  mIsMetaDataQueried=false;
708  }
709  AVPKIT_CHECK_INTERRUPT(retval);
710  return retval;
711  }
712 
713  Stream *
714  Container :: getStream(int32_t position)
715  {
716  Stream *retval = 0;
717  if (mFormatContext)
718  {
719  if (mFormatContext->nb_streams != mNumStreams)
720  setupAllInputStreams();
721 
722  if (position < mNumStreams)
723  {
724  // will acquire for caller.
725  retval = (*mStreams.at(position)).get();
726  }
727  }
728  return retval;
729  }
730 
731  int32_t
732  Container :: readNextPacket(IPacket * ipkt)
733  {
734  int32_t retval = -1;
735  Packet* pkt = static_cast<Packet*>(ipkt);
736  if (mFormatContext && pkt)
737  {
738 
739  bool useBsf = false;
740 
741 // pkt->reset();
742  int32_t numReads=0;
743  do
744  {
745  retval = av_read_frame(mFormatContext,
746  mPacket);
747  ++numReads;
748 
749  if (retval == 0) {
750  Stream* stream = this->getStream(mPacket->stream_index);
751  AVBSFContext* bsfContext = stream->getAVBsfContext();
752  VS_REF_RELEASE(stream);
753  if (bsfContext) {
754  useBsf = true;
755  if (retval = av_bsf_send_packet(bsfContext, mPacket) == 0) {
756  retval = av_bsf_receive_packet(bsfContext, mBsfPacket);
757  }
758  }
759  }
760  }
761  while (retval == AVERROR(EAGAIN) &&
762  (mReadRetryCount < 0 || numReads <= mReadRetryCount));
763 
764  if (retval >= 0)
765  if (!useBsf) {
766  pkt->wrapAVPacket(mPacket);
767  } else {
768  pkt->wrapAVPacket(mBsfPacket);
769  }
770  av_packet_unref(mPacket);
771  av_packet_unref(mBsfPacket);
772 
773 // // Get a pointer to the wrapped packet
774 // packet = pkt->getAVPacket();
775  // and dump it's contents
776  VS_LOG_TRACE("read-packet: %lld, %lld, %d, %d, %d, %lld, %lld: %p",
777  pkt->getDts(),
778  pkt->getPts(),
779  pkt->getFlags(),
780  pkt->getStreamIndex(),
781  pkt->getSize(),
782  pkt->getDuration(),
783  pkt->getPosition(),
784  pkt->getAVPacket()->data);
785 
786  // and let's try to set the packet time base if known
787  if (pkt->getStreamIndex() >= 0)
788  {
789  RefPointer<IStream> stream = this->getStream(pkt->getStreamIndex());
790  if (stream)
791  {
792  RefPointer<IRational> streamBase = stream->getTimeBase();
793  if (streamBase)
794  {
795  pkt->setTimeBase(streamBase.value());
796  }
797  }
798  }
799  }
800  AVPKIT_CHECK_INTERRUPT(retval);
801  return retval;
802  }
803  int32_t
804  Container :: writePacket(IPacket *ipkt)
805  {
806  return writePacket(ipkt, true);
807  }
808  int32_t
809  Container :: writePacket(IPacket *ipkt, bool forceInterleave)
810  {
811  int32_t retval = -1;
812  Packet *pkt = static_cast<Packet*>(ipkt);
813  try
814  {
815  if (this->getType() != WRITE)
816  throw std::runtime_error("cannot write packet to read only container");
817 
818  if (!mFormatContext)
819  throw std::logic_error("no format context");
820 
821  if (!pkt)
822  throw std::runtime_error("cannot write missing packet");
823 
824  if (!pkt->isComplete())
825  throw std::runtime_error("cannot write incomplete packet");
826 
827 // if (!pkt->getSize())
828 // throw std::runtime_error("cannot write empty packet");
829 
830  if (!mNeedTrailerWrite)
831  throw std::runtime_error("container has not written header yet");
832 
833  int32_t pktIndex = pkt->getStreamIndex();
834 
835  if ((uint32_t)pktIndex >= mNumStreams)
836  throw std::runtime_error("packet being written to stream that doesn't exist");
837 
838  RefPointer<Stream> *streamPtr = mStreams.at(pktIndex);
839  if (!streamPtr || !*streamPtr)
840  throw std::runtime_error("no stream set up for this packet");
841 
842  Stream* stream = streamPtr->value();
843 
844  // Create a new packet that wraps the input data; this
845  // just copies meta-data
846  RefPointer<Packet> outPacket = Packet::make(pkt, false);
847  // Stamp it with the stream data
848  if (stream->stampOutputPacket(outPacket.value()) <0)
849  throw std::runtime_error("could not stamp output packet");
850 
851  AVPacket *packet = 0;
852  packet = outPacket->getAVPacket();
853  if (!packet || !packet->data)
854  throw std::runtime_error("no data in packet");
855 
856  /*
857  VS_LOG_DEBUG("write-packet: %lld, %lld, %d, %d, %d, %lld, %lld: %p",
858  pkt->getDts(),
859  pkt->getPts(),
860  pkt->getFlags(),
861  pkt->getStreamIndex(),
862  pkt->getSize(),
863  pkt->getDuration(),
864  pkt->getPosition(),
865  packet->data);
866  */
867 
868  if (forceInterleave)
869  retval = av_interleaved_write_frame(mFormatContext, packet);
870  else
871  retval = av_write_frame(mFormatContext, packet);
872  }
873  catch (std::exception & e)
874  {
875  VS_LOG_ERROR("Error: %s", e.what());
876  retval = -1;
877  }
878  AVPKIT_CHECK_INTERRUPT(retval);
879  return retval;
880  }
881 
882  int32_t
883  Container :: writeHeader()
884  {
885  int32_t retval = -1;
886  try {
887  if (this->getType() != WRITE)
888  throw std::runtime_error("cannot write packet to read only container");
889 
890  if (!mFormatContext)
891  throw std::runtime_error("no format context allocated");
892 
893 #ifdef VS_DEBUG
894  // for shits and giggles, dump the ffmpeg output
895  // dump_format(mFormatContext, 0, (mFormatContext ? mFormatContext->filename :0), 1);
896 #endif // VS_DEBUG
897 
898  // now we're going to walk through and record each open stream coder.
899  // this is needed to catch a potential error on writeTrailer().
900  mOpenCoders.clear();
901  int numStreams = getNumStreams();
902  if (numStreams < 0 &&
903  !(mFormatContext->ctx_flags & AVFMTCTX_NOHEADER))
904  throw std::runtime_error("no streams added to container");
905 
906  if (numStreams == 0)
907  {
908  RefPointer<IContainerFormat> format = getContainerFormat();
909  if (format)
910  {
911  const char *shortName = format->getOutputFormatShortName();
912  if (shortName && !strcmp(shortName, "mp3"))
913  throw std::runtime_error("no streams added to mp3 container");
914  }
915  }
916  for(int i = 0; i < numStreams; i++)
917  {
918  RefPointer<IStream> stream = this->getStream(i);
919  if (stream)
920  {
921  RefPointer<IStreamCoder> coder = stream->getStreamCoder();
922  if (coder)
923  {
924  if (coder->isOpen())
925  // add to our open list
926  mOpenCoders.push_back(coder);
927  else
928  VS_LOG_TRACE("writing Header for container, but at least one codec (%d) is not opened first", i);
929  }
930  }
931  }
932  retval = avformat_write_header(mFormatContext,0);
933  if (retval < 0)
934  throw std::runtime_error("could not write header for container");
935 
936  // force a flush.
937  if (!(mFormatContext->oformat->flags & AVFMT_NOFILE)) {
938  avio_flush(mFormatContext->pb);
939  }
940  // and remember that a writeTrailer is needed
941  mNeedTrailerWrite = true;
942  }
943  catch (std::exception & e)
944  {
945  VS_LOG_ERROR("Error: %s", e.what());
946  retval = -1;
947  }
948  AVPKIT_CHECK_INTERRUPT(retval);
949  return retval;
950  }
951  int32_t
952  Container :: writeTrailer()
953  {
954  int32_t retval = -1;
955  try
956  {
957  if (this->getType() != WRITE)
958  throw std::runtime_error("cannot write packet to read only container");
959 
960  if (!mFormatContext)
961  throw std::runtime_error("no format context allocated");
962 
963  if (mNeedTrailerWrite)
964  {
965  while(mOpenCoders.size()>0)
966  {
967  RefPointer<IStreamCoder> coder = mOpenCoders.front();
968  mOpenCoders.pop_front();
969  if (!coder->isOpen())
970  {
971  mOpenCoders.clear();
972  throw std::runtime_error("attempt to write trailer, but at least one used codec already closed");
973  }
974  }
975  retval = av_write_trailer(mFormatContext);
976  if (retval == 0)
977  {
978  if (!(mFormatContext->oformat->flags & AVFMT_NOFILE)) {
979  avio_flush(mFormatContext->pb);
980  }
981  }
982  } else {
983  VS_LOG_WARN("writeTrailer() with no matching call to writeHeader()");
984  }
985  }
986  catch (std::exception & e)
987  {
988  VS_LOG_ERROR("Error: %s", e.what());
989  retval = -1;
990  }
991  // regardless of whether or not the write trailer succeeded, since
992  // an attempt has occurred, we shouldn't call it twice.
993  mNeedTrailerWrite = false;
994  AVPKIT_CHECK_INTERRUPT(retval);
995  return retval;
996  }
997 
998  int32_t
999  Container :: queryStreamMetaData()
1000  {
1001  int retval = -1;
1002  if (mIsOpened)
1003  {
1004  if (!mIsMetaDataQueried)
1005  {
1006  retval = avformat_find_stream_info(mFormatContext, NULL);
1007  // for shits and giggles, dump the ffmpeg output
1008  //av_dump_format(mFormatContext, 0, (mFormatContext ? mFormatContext->filename :0), 0);
1009  mIsMetaDataQueried = true;
1010  } else {
1011  retval = 0;
1012  }
1013 
1014  if (retval >= 0 && mFormatContext->nb_streams > 0)
1015  {
1016  setupAllInputStreams();
1017  } else {
1018  VS_LOG_WARN("Could not find streams in input container");
1019  }
1020  }
1021  else
1022  {
1023  VS_LOG_WARN("Attempt to queryStreamMetaData but container is not open");
1024  }
1025  AVPKIT_CHECK_INTERRUPT(retval);
1026  return retval;
1027  }
1028 
1029  int32_t
1030  Container :: seekKeyFrame(int32_t streamIndex, int64_t timestamp, int32_t flags)
1031  {
1032  int32_t retval = -1;
1033 
1034  if (mIsOpened)
1035  {
1036  if (streamIndex >= (int32_t)mNumStreams)
1037  VS_LOG_WARN("Attempt to seek on streamIndex %d but only %d streams known about in container",
1038  streamIndex, mNumStreams);
1039  else
1040  retval = av_seek_frame(mFormatContext, streamIndex, timestamp, flags);
1041  }
1042  else
1043  {
1044  VS_LOG_WARN("Attempt to seekKeyFrame but container is not open");
1045  }
1046  AVPKIT_CHECK_INTERRUPT(retval);
1047  return retval;
1048  }
1049 
1050  int32_t
1051  Container :: seekKeyFrame(int32_t streamIndex, int64_t minTimeStamp,
1052  int64_t targetTimeStamp, int64_t maxTimeStamp, int32_t flags)
1053  {
1054  int32_t retval = -1;
1055 
1056  if (mIsOpened)
1057  {
1058  if (streamIndex >= (int32_t)mNumStreams)
1059  VS_LOG_WARN("Attempt to seek on streamIndex %d but only %d streams known about in container",
1060  streamIndex, mNumStreams);
1061  else
1062  retval = avformat_seek_file(mFormatContext, streamIndex,
1063  minTimeStamp,
1064  targetTimeStamp,
1065  maxTimeStamp,
1066  flags);
1067  }
1068  else
1069  {
1070  VS_LOG_WARN("Attempt to seekKeyFrame but container is not open");
1071  }
1072  AVPKIT_CHECK_INTERRUPT(retval);
1073  return retval;
1074  }
1075 
1076  int64_t
1077  Container :: getDuration()
1078  {
1079  int64_t retval = Global::NO_PTS;
1080  queryStreamMetaData();
1081  if (mFormatContext)
1082  retval = mFormatContext->duration;
1083  return retval;
1084  }
1085 
1086  int64_t
1087  Container :: getStartTime()
1088  {
1089  int64_t retval = Global::NO_PTS;
1090  queryStreamMetaData();
1091  if (mFormatContext)
1092  retval = mFormatContext->start_time;
1093  return retval;
1094  }
1095 
1096  int64_t
1097  Container :: getFileSize()
1098  {
1099  int64_t retval = -1;
1100  queryStreamMetaData();
1101  if (mFormatContext) {
1102  if (mFormatContext->iformat && mFormatContext->iformat->flags & AVFMT_NOFILE)
1103  retval = 0;
1104  else {
1105  retval = avio_size(mFormatContext->pb);
1106  retval = FFMAX(0, retval);
1107  }
1108  }
1109  return retval;
1110  }
1111 
1112  int32_t
1113  Container :: getBitRate()
1114  {
1115  int32_t retval = -1;
1116  queryStreamMetaData();
1117  if (mFormatContext)
1118  retval = mFormatContext->bit_rate;
1119  return retval;
1120  }
1121 
1122  int32_t
1123  Container :: setPreload(int32_t)
1124  {
1125  VS_LOG_WARN("Deprecated and will be removed; does nothing now.");
1126  return -1;
1127  }
1128 
1129  int32_t
1130  Container :: getPreload()
1131  {
1132  VS_LOG_WARN("Deprecated and will be removed; does nothing now.");
1133  return -1;
1134  }
1135 
1136  int32_t
1137  Container :: setMaxDelay(int32_t maxdelay)
1138  {
1139  int32_t retval = -1;
1140  if (mIsOpened || !mFormatContext)
1141  {
1142  VS_LOG_WARN("Attempting to set max delay while file is opened; ignoring");
1143  }
1144  else
1145  {
1146  mFormatContext->max_delay = maxdelay;
1147  retval = maxdelay;
1148  }
1149  return retval;
1150  }
1151 
1152  int32_t
1153  Container :: getMaxDelay()
1154  {
1155  int32_t retval = -1;
1156  if (mFormatContext)
1157  retval = mFormatContext->max_delay;
1158  return retval;
1159  }
1160 
1161  int32_t
1162  Container :: getNumProperties()
1163  {
1164  return Property::getNumProperties(mFormatContext);
1165  }
1166 
1167  IProperty*
1168  Container :: getPropertyMetaData(int32_t propertyNo)
1169  {
1170  return Property::getPropertyMetaData(mFormatContext, propertyNo);
1171  }
1172 
1173  IProperty*
1174  Container :: getPropertyMetaData(const char *name)
1175  {
1176  return Property::getPropertyMetaData(mFormatContext, name);
1177  }
1178 
1179  int32_t
1180  Container :: setProperty(IMetaData* valuesToSet, IMetaData* valuesNotFound)
1181  {
1182  return Property::setProperty(mFormatContext, valuesToSet, valuesNotFound);
1183  }
1184 
1185  int32_t
1186  Container :: setProperty(const char* aName, const char *aValue)
1187  {
1188  return Property::setProperty(mFormatContext, aName, aValue);
1189  }
1190 
1191  int32_t
1192  Container :: setProperty(const char* aName, double aValue)
1193  {
1194  return Property::setProperty(mFormatContext, aName, aValue);
1195  }
1196 
1197  int32_t
1198  Container :: setProperty(const char* aName, int64_t aValue)
1199  {
1200  return Property::setProperty(mFormatContext, aName, aValue);
1201  }
1202 
1203  int32_t
1204  Container :: setProperty(const char* aName, bool aValue)
1205  {
1206  return Property::setProperty(mFormatContext, aName, aValue);
1207  }
1208 
1209 
1210  int32_t
1211  Container :: setProperty(const char* aName, IRational *aValue)
1212  {
1213  return Property::setProperty(mFormatContext, aName, aValue);
1214  }
1215 
1216 
1217  char*
1218  Container :: getPropertyAsString(const char *aName)
1219  {
1220  return Property::getPropertyAsString(mFormatContext, aName);
1221  }
1222 
1223  double
1224  Container :: getPropertyAsDouble(const char *aName)
1225  {
1226  return Property::getPropertyAsDouble(mFormatContext, aName);
1227  }
1228 
1229  int64_t
1230  Container :: getPropertyAsLong(const char *aName)
1231  {
1232  return Property::getPropertyAsLong(mFormatContext, aName);
1233  }
1234 
1235  IRational*
1236  Container :: getPropertyAsRational(const char *aName)
1237  {
1238  return Property::getPropertyAsRational(mFormatContext, aName);
1239  }
1240 
1241  bool
1242  Container :: getPropertyAsBoolean(const char *aName)
1243  {
1244  return Property::getPropertyAsBoolean(mFormatContext, aName);
1245  }
1246 
1247  int32_t
1248  Container :: getFlags()
1249  {
1250  int32_t flags = (mFormatContext ? mFormatContext->flags : 0);
1251  // remove custom io if set
1252  flags &= ~(AVFMT_FLAG_CUSTOM_IO);
1253  return flags;
1254  }
1255 
1256  void
1257  Container :: setFlags(int32_t newFlags)
1258  {
1259  if (mFormatContext) {
1260  mFormatContext->flags = newFlags;
1261  // force custom io
1262  mFormatContext->flags |= AVFMT_FLAG_CUSTOM_IO;
1263  }
1264  }
1265 
1266  bool
1267  Container :: getFlag(IContainer::Flags flag)
1268  {
1269  bool result = false;
1270  if (mFormatContext)
1271  result = mFormatContext->flags& flag;
1272  return result;
1273  }
1274 
1275  void
1276  Container :: setFlag(IContainer::Flags flag, bool value)
1277  {
1278  if (mFormatContext)
1279  {
1280  if (value)
1281  {
1282  mFormatContext->flags |= flag;
1283  }
1284  else
1285  {
1286  mFormatContext->flags &= (~flag);
1287  }
1288  }
1289 
1290  }
1291 
1292  const char*
1293  Container :: getURL()
1294  {
1295  return mFormatContext && *mFormatContext->filename ? mFormatContext->filename : 0;
1296  }
1297 
1298  int32_t
1299  Container :: flushPackets()
1300  {
1301  int32_t retval = -1;
1302  try
1303  {
1304  if (this->getType() != WRITE)
1305  throw std::runtime_error("cannot write packet to read only container");
1306 
1307  if (!mFormatContext)
1308  throw std::runtime_error("no format context allocated");
1309 
1310  // Do the flush
1311  if (!(mFormatContext->oformat->flags & AVFMT_NOFILE)) {
1312  avio_flush(mFormatContext->pb);
1313  }
1314  retval = 0;
1315  }
1316  catch (std::exception & e)
1317  {
1318  VS_LOG_ERROR("Error: %s", e.what());
1319  retval = -1;
1320  }
1321  AVPKIT_CHECK_INTERRUPT(retval);
1322  return retval;
1323  }
1324 
1325  int32_t
1326  Container :: getReadRetryCount()
1327  {
1328  return mReadRetryCount;
1329  }
1330 
1331  void
1332  Container :: setReadRetryCount(int32_t aCount)
1333  {
1334  mReadRetryCount = aCount;
1335  }
1336 
1337  int32_t
1338  Container :: setFormat(IContainerFormat* aFormat)
1339  {
1340  int32_t retval = -1;
1341  ContainerFormat* format = static_cast<ContainerFormat*>(aFormat);
1342  try {
1343  if (!format)
1344  throw std::runtime_error("no format set");
1345 
1346  if (!mFormatContext)
1347  throw std::runtime_error("no underlying AVFormatContext");
1348  if (mFormatContext->iformat || mFormatContext->oformat)
1349  throw std::runtime_error("format already set on this IContainer; cannot be reset");
1350  if (mIsOpened)
1351  throw std::runtime_error("container already opened");
1352 
1353  AVOutputFormat* oformat = format->getOutputFormat();
1354  AVInputFormat* iformat = format->getInputFormat();
1355 
1356  if (!iformat && !oformat)
1357  throw std::runtime_error("no input or output format set");
1358 
1359  // iformat, if set, always wins
1360  if (iformat) {
1361  mFormatContext->iformat = iformat;
1362  mFormatContext->oformat = 0;
1363  } else {
1364  // throw away the old context and use the new to get the correct options set up
1365  resetContext();
1366  mFormatContext = 0;
1367  if (avformat_alloc_output_context2(&mFormatContext, oformat, 0, 0)<0)
1368  throw std::runtime_error("could not allocate output context");
1369  }
1370  mFormat.reset(format, true);
1371  retval = 0;
1372  } catch (std::exception &e)
1373  {
1374  retval = -1;
1375  }
1376  return retval;
1377  }
1378  bool
1379  Container :: canStreamsBeAddedDynamically()
1380  {
1381  if (mFormatContext)
1382  return mFormatContext->ctx_flags & AVFMTCTX_NOHEADER;
1383  return false;
1384  }
1385 
1386  IMetaData*
1387  Container :: getMetaData()
1388  {
1389  if (!mMetaData && mFormatContext)
1390  {
1391  if (this->getType() == WRITE)
1392  mMetaData = MetaData::make(&mFormatContext->metadata);
1393  else
1394  // make a read-only copy so when libav deletes the
1395  // input version we don't delete our copy
1396  mMetaData = MetaData::make(mFormatContext->metadata);
1397  }
1398  return mMetaData.get();
1399  }
1400  void
1401  Container :: setMetaData(IMetaData * copy)
1402  {
1403  MetaData* data = static_cast<MetaData*>(getMetaData());
1404  if (data) {
1405  data->copy(copy);
1406  // release for the get above
1407  data->release();
1408  }
1409  return;
1410  }
1411 
1412  int32_t
1413  Container:: createSDPData(com::avpkit::ferry::IBuffer* buffer)
1414  {
1415  if (!mFormatContext)
1416  return -1;
1417  if (!buffer)
1418  return -1;
1419 
1420  int32_t bufSize = buffer->getBufferSize();
1421  if (bufSize <= 0)
1422  return -1;
1423 
1424  char* bytes = static_cast<char*>(buffer->getBytes(0, bufSize));
1425  if (!bytes)
1426  return -1;
1427 
1428  bytes[0] = 0;
1429  // null terminate the buffer to ensure strlen below doesn't
1430  // overrun
1431  bytes[bufSize-1]=0;
1432  int32_t ret = av_sdp_create(&mFormatContext, 1, bytes, bufSize-1);
1433 
1434  if (ret < 0)
1435  {
1436  VS_LOG_INFO("Could not create SDP file: %d", ret);
1437  return ret;
1438  }
1439  // Otherwise, we have to figure out the length, including the
1440  // terminating null
1441  ret = strlen(bytes)+1;
1442  return ret;
1443  }
1444 
1445  int32_t
1446  Container :: setForcedAudioCodec(ICodec::ID id)
1447  {
1448  int32_t retval=-1;
1449  if (mFormatContext && id != ICodec::AV_CODEC_ID_NONE)
1450  {
1451  RefPointer<ICodec> codec = ICodec::findDecodingCodec(id);
1452  if (codec && codec->getType() == ICodec::CODEC_TYPE_AUDIO)
1453  mFormatContext->audio_codec_id = (enum AVCodecID) id;
1454  }
1455  return retval;
1456  }
1457 
1458  int32_t
1459  Container :: setForcedVideoCodec(ICodec::ID id)
1460  {
1461  int32_t retval=-1;
1462  if (mFormatContext && id != ICodec::AV_CODEC_ID_NONE)
1463  {
1464  RefPointer<ICodec> codec = ICodec::findDecodingCodec(id);
1465  if (codec && codec->getType() == ICodec::CODEC_TYPE_VIDEO)
1466  mFormatContext->video_codec_id = (enum AVCodecID) id;
1467  }
1468  return retval;
1469  }
1470 
1471  int32_t
1472  Container :: setForcedSubtitleCodec(ICodec::ID id)
1473  {
1474  int32_t retval=-1;
1475  if (mFormatContext && id != ICodec::AV_CODEC_ID_NONE)
1476  {
1477  RefPointer<ICodec> codec = ICodec::findDecodingCodec(id);
1478  if (codec && codec->getType() == ICodec::CODEC_TYPE_SUBTITLE)
1479  mFormatContext->subtitle_codec_id = (enum AVCodecID) id;
1480  }
1481  return retval;
1482  }
1483 
1484  Stream*
1485  Container :: addNewStream(ICodec::ID id)
1486  {
1487  Stream* retval=0;
1488  RefPointer<ICodec> codec;
1489  try
1490  {
1491  codec = ICodec::findEncodingCodec(id);
1492 
1493  if (!codec) {
1494  VS_LOG_ERROR("Could not find encoding codec: %d", id);
1495  throw std::runtime_error("Could not find encoding codec");
1496  }
1497  retval = addNewStream(codec.value());
1498  }
1499  catch (std::exception & e)
1500  {
1501  VS_LOG_DEBUG("Error: %s", e.what());
1502  VS_REF_RELEASE(retval);
1503  }
1504  return retval;
1505  }
1506 
1507  Stream*
1508  Container:: addNewStream(ICodec* aCodec)
1509  {
1510  Codec* codec = static_cast<Codec*>(aCodec);
1511  Stream *retval=0;
1512  try
1513  {
1514  const AVCodec* avCodec = codec ? codec->getAVCodec() : 0;
1515 
1516  if (!mFormatContext)
1517  throw std::runtime_error("no format context");
1518 
1519  if (!isOpened())
1520  throw std::runtime_error("attempted to add stream to "
1521  " unopened container");
1522 
1523  if (isHeaderWritten())
1524  throw std::runtime_error("cannot add stream after header is written"
1525  );
1526 
1527  AVStream * stream=0;
1528  stream = avformat_new_stream(mFormatContext, avCodec);
1529 
1530 
1531  if (!stream)
1532  throw std::runtime_error("could not allocate stream");
1533 
1535  Stream::make(this, stream, IStream::OUTBOUND, avCodec)
1536  );
1537  if (!p) throw std::bad_alloc();
1538  if (*p)
1539  {
1540  mStreams.push_back(p);
1541  mNumStreams++;
1542  retval = p->get(); // acquire for caller
1543  }
1544  else
1545  {
1546  delete p;
1547  throw std::bad_alloc();
1548  }
1549  } catch (std::exception & e)
1550  {
1551  VS_LOG_DEBUG("Error: %s", e.what());
1552  VS_REF_RELEASE(retval);
1553  }
1554  return retval;
1555  }
1556 
1557  Stream*
1558  Container :: addNewStream(IStreamCoder* aCoder)
1559  {
1560  Stream *retval=0;
1561  StreamCoder * coder = static_cast<StreamCoder*>(aCoder);
1562  RefPointer<ICodec> codec;
1563  try
1564  {
1565  if (!coder)
1566  throw std::runtime_error("must pass non-null coder");
1567  codec = coder->getCodec();
1568  if (!codec)
1569  throw std::runtime_error("StreamCoder has no attached Codec");
1570 
1571  retval = this->addNewStream(codec.value());
1572  if (retval)
1573  {
1574  if (retval->setStreamCoder(coder) < 0)
1575  throw std::runtime_error("Could not set StreamCoder on stream");
1576  }
1577 
1578  } catch (std::exception & e)
1579  {
1580  VS_LOG_DEBUG("addNewStream Error: %s", e.what());
1581  VS_REF_RELEASE(retval);
1582  }
1583  return retval;
1584  }
1585 
1586  Stream*
1587  Container :: addNewStream(int32_t id)
1588  {
1589  Stream *retval=0;
1590  retval = addNewStream((ICodec*)0);
1591  if (retval)
1592  retval->setId(id);
1593  return retval;
1594  }
1595 
1596  Container*
1597  Container :: make(IContainerFormat* format)
1598  {
1599  Container* retval;
1600  retval = Container::make();
1601  if (retval)
1602  if (retval->setFormat(format) < 0)
1603  VS_REF_RELEASE(retval);
1604  return retval;
1605  }
1606 
1607 }}}
virtual int32_t setInputFormat(const char *shortName)
Sets the input format for this container.
virtual int32_t setOutputFormat(const char *shortName, const char *url, const char *mimeType)
Sets the output format for this container.
virtual int32_t setFormat(IContainerFormat *format)
Set the IContainerFormat to use with this IContainer.
Definition: Container.cpp:1338
A "key" to an IStreamCoder that tells it how to encode or decode data.
Definition: ICodec.h:53
ID
These are the codecs this library currently supports.
Definition: ICodec.h:61
Specifies format information than can be used to configure an IContainer for input or output.
Type
The different types of Containers AVPKit supports.
Definition: IContainer.h:106
Get MetaData about a IContainer or IStream.
Definition: IMetaData.h:51
Represents an encoded piece of data that can be placed in an IContainer for a given IStream of data.
Definition: IPacket.h:50
Represents settable properties that effect how AVPKit objects operate.
Definition: IProperty.h:37
This class wraps represents a Rational number for the AVPKit.
Definition: IRational.h:43
The work horse of the AVPKit: Takes IPacket data from an IContainer (representing an IStream) and an ...
Definition: IStreamCoder.h:45
CodecStandardsCompliance
An enumeration of how strictly Codecs may follow the spec.
Definition: IStreamCoder.h:959
int32_t copy(IMetaData *copy)
Destroys the current data, and copies all data from copy.
Definition: MetaData.cpp:164
AVDictionary * getDictionary()
Returns the AVDictionary for passing to FFmpeg calls.
Definition: MetaData.h:76
virtual int64_t getPts()
Get the Presentation Time Stamp (PTS) for this packet.
Definition: Packet.cpp:63
virtual int32_t getSize()
Get the size in bytes of the payload currently in this packet.
Definition: Packet.cpp:87
virtual int32_t getFlags()
Get any flags set on this packet, as a 4-byte binary-ORed bit-mask.
Definition: Packet.cpp:102
virtual bool isComplete()
Is this packet complete.
Definition: Packet.cpp:384
virtual int32_t getStreamIndex()
Get the container-specific index for the stream this packet is part of.
Definition: Packet.cpp:97
virtual void setTimeBase(IRational *aBase)
Set the time base that time stamps of this object are represented in.
Definition: Packet.h:50
virtual int64_t getPosition()
Return the position (in bytes) of this packet in the stream.
Definition: Packet.cpp:161
virtual int64_t getDts()
Get the Decompression Time Stamp (DTS) for this packet.
Definition: Packet.cpp:75
virtual int64_t getDuration()
Return the duration of this packet, in units of getTimeBase().
Definition: Packet.cpp:149
virtual Codec * getCodec()
The Codec this StreamCoder will use.
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 int32_t setStreamCoder(IStreamCoder *coder)
Sets the stream coder to use for this stream.
Definition: Stream.cpp:277
void setId(int32_t id)
Set the format-specific stream id.
Definition: Stream.cpp:522
A class for managing custom io protocols.
virtual URLProtocolHandler * getHandler(const char *url, int flags)=0
Returns a URLProtocol handler for the given url and flags.
Allows Java code to get data from a native buffers, and optionally modify native memory directly.
Definition: IBuffer.h:54
virtual void * getBytes(int32_t offset, int32_t length)=0
Returns up to length bytes, starting at offset in the underlying buffer we're managing.
virtual int32_t getBufferSize()=0
Get the current maximum number of bytes that can be safely placed in this buffer.
virtual int32_t release()
Internal Only.
Definition: RefCounted.cpp:70
This class is only useful from C++.
Definition: RefPointer.h:47
T * get()
Call RefCounted::acquire() on the managed pointer and return it.
Definition: RefPointer.h:206
T * value()
Return the managed pointer without calling RefCounted::acquire() on it.
Definition: RefPointer.h:226
This package contains the AVPKIT IO library which is used to register callbacks for FFMPEG to get and...
Definition: IO.h:44
This library contains routines used by AVPKit libraries for "ferry"ing Java objects to and from nativ...
WARNING: Do not use logging in this class, and do not set any static file variables to values other t...