AVPKit
Packet.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 <com/avpkit/ferry/Logger.h>
21 #include <com/avpkit/ferry/Buffer.h>
22 #include <com/avpkit/core/Packet.h>
23 
24 // for memset
25 #include <cstring>
26 #include <stdexcept>
27 
28 VS_LOG_SETUP(VS_CPP_PACKAGE);
29 
30 namespace com { namespace avpkit { namespace core
31  {
32 
33  using namespace com::avpkit::ferry;
34 
35  Packet :: Packet()
36  {
37  mPacket = (AVPacket*)av_malloc(sizeof(AVPacket));
38  if (!mPacket)
39  throw std::bad_alloc();
40 
41  if (mPacket)
42  {
43  // initialize because ffmpeg doesn't
44  av_init_packet(mPacket);
45  mPacket->data = NULL;
46  mPacket->size = 0;
47  }
48  mIsComplete = false;
49  }
50 
51  Packet :: ~Packet()
52  {
53  if (mPacket) {
54  reset();
55  av_free(mPacket);
56  }
57  mPacket = 0;
58  mBuffer = 0;
59  mTimeBase = 0;
60  }
61 
62  int64_t
64  {
65  return (mPacket ? mPacket->pts : (int64_t)-1);
66  }
67 
68  void
69  Packet :: setPts(int64_t aPts)
70  {
71  if (mPacket) mPacket->pts = aPts;
72  }
73 
74  int64_t
76  {
77  return (mPacket ? mPacket->dts : (int64_t)-1);
78  }
79 
80  void
81  Packet :: setDts(int64_t aDts)
82  {
83  if (mPacket) mPacket->dts = aDts;
84  }
85 
86  int32_t
88  {
89  return (mPacket ? mPacket->size: (int32_t)-1);
90  }
91  int32_t
93  {
94  return (mBuffer ? mBuffer->getBufferSize() : -1);
95  }
96  int32_t
98  {
99  return (mPacket ? mPacket->stream_index: (int32_t)-1);
100  }
101  int32_t
103  {
104  return (mPacket ? mPacket->flags: (int32_t)-1);
105  }
106  bool
108  {
109  return (mPacket ? mPacket->flags & AV_PKT_FLAG_KEY : false);
110  }
111 
112  void
113  Packet :: setKeyPacket(bool bKeyPacket)
114  {
115  if (mPacket)
116  {
117  if (bKeyPacket)
118  mPacket->flags |= AV_PKT_FLAG_KEY;
119  else
120  mPacket->flags = 0;
121  }
122  }
123 
124  void
125  Packet :: setFlags(int32_t flags)
126  {
127  if (mPacket)
128  mPacket->flags = flags;
129  }
130 
131  void
132  Packet :: setComplete(bool complete, int32_t size)
133  {
134  mIsComplete = complete;
135  if (mIsComplete)
136  {
137  if (mPacket)
138  mPacket->size = size;
139  }
140  }
141 
142  void
143  Packet :: setStreamIndex(int32_t streamIndex)
144  {
145  if (mPacket)
146  mPacket->stream_index = streamIndex;
147  }
148  int64_t
150  {
151  return (mPacket ? mPacket->duration: (int64_t)-1);
152  }
153 
154  void
155  Packet :: setDuration(int64_t duration)
156  {
157  if (mPacket) mPacket->duration = duration;
158  }
159 
160  int64_t
162  {
163  return (mPacket ? mPacket->pos: (int64_t)-1);
164  }
165 
166  void
167  Packet :: setPosition(int64_t position)
168  {
169  if (mPacket) mPacket->pos = position;
170  }
171 
174  {
175  return mBuffer.get();
176  }
177 
178  void
179  Packet :: wrapAVPacket(AVPacket* pkt)
180  {
181  // WE ALWAYS COPY the data; Let Ffmpeg do what it wants
182  // to with it's own memory.
183  VS_ASSERT(mPacket, "No packet?");
184 
185  // Make sure we have a buffer at least as large as this packet
186  // This overwrites data, which we'll patch below.
187  (void) this->allocateNewPayload(pkt->size);
188 
189  // Copy the contents of the new packet into data.
190  if (pkt->data && pkt->size)
191  memcpy(mPacket->data, pkt->data, pkt->size);
192 
193  // Keep a copy of this, because we're going to nuke
194  // it temporarily.
195  uint8_t* data_buf = mPacket->data;
196 // void (*orig_destruct)(struct AVPacket *) = mPacket->destruct;
197 
198  // copy all data members, including data and size,
199  // but we'll overwrite those next.
200  *mPacket = *pkt;
201  mPacket->buf = NULL;
202  mPacket->data = data_buf;
203 // mPacket->destruct = orig_destruct;
204  av_copy_packet_side_data(mPacket, pkt);
205  // And assume we're now complete.
206  setComplete(true, mPacket->size);
207 
208  }
209 
210  void
212  {
213  if (mPacket) {
214  av_packet_unref(mPacket);
215  av_init_packet(mPacket);
216  }
217  setComplete(false, 0);
218  // Don't reset the buffer though; we can potentially reuse it.
219  }
220 
221  Packet*
222  Packet :: make (int32_t payloadSize)
223  {
224  Packet *retval= 0;
225  try {
226  retval = Packet::make();
227  if (retval->allocateNewPayload(payloadSize) < 0)
228  {
229  throw std::bad_alloc();
230  }
231  }
232  catch (std::bad_alloc & e)
233  {
234  VS_REF_RELEASE(retval);
235  throw e;
236  }
237 
238  return retval;
239  }
240 
241  Packet*
243  {
244  Packet *retval= NULL;
245  retval = Packet::make();
246  if (retval)
247  {
248  retval->wrapBuffer(buffer);
249  }
250  return retval;
251  }
252 
253  Packet*
254  Packet :: make (Packet *packet, bool copyData)
255  {
256  Packet* retval= NULL;
257  com::avpkit::ferry::IBuffer *buffer= NULL;
258  IRational* timeBase = NULL;
259  try
260  {
261  if (!packet)
262  throw std::runtime_error("need packet to copy");
263 
264  if (copyData)
265  {
266  int32_t numBytes = packet->getSize();
267  retval = make(numBytes);
268  if (!retval || !retval->mPacket || !retval->mPacket->data)
269  throw std::bad_alloc();
270  if (numBytes > 0 && packet->mPacket->data)
271  memcpy(retval->mPacket->data, packet->mPacket->data,
272  numBytes);
273  } else {
274  buffer=packet->getData();
275  retval = make(buffer);
276  if (!retval)
277  throw std::bad_alloc();
278  }
279  // Keep a copy of this, because we're going to nuke
280  // it temporarily.
281  uint8_t* data_buf = retval->mPacket->data;
282 // void (*orig_destruct)(struct AVPacket *) = retval->mPacket->destruct;
283 
284  // copy all data members, including data and size,
285  // but we'll overwrite those next.
286  *(retval->mPacket) = *(packet->mPacket);
287  retval->mPacket->buf = NULL;
288  retval->mPacket->data = data_buf;
289 // retval->mPacket->destruct = orig_destruct;
290  av_copy_packet_side_data(retval->mPacket, packet->mPacket);
291  // separate here to catch addRef()
292  timeBase = packet->getTimeBase();
293  retval->setTimeBase(timeBase);
294 
295  retval->setComplete(retval->mPacket->size > 0,
296  retval->mPacket->size);
297  }
298  catch (std::exception &e)
299  {
300  VS_REF_RELEASE(retval);
301  }
302  VS_REF_RELEASE(buffer);
303  VS_REF_RELEASE(timeBase);
304 
305  return retval;
306  }
307 
308  int32_t
309  Packet :: allocateNewPayload(int32_t payloadSize)
310  {
311  int32_t retval = -1;
312  reset();
313  uint8_t* payload = 0;
314 
315  // Some FFMPEG encoders will read past the end of a
316  // buffer, so you need to allocate extra; yuck.
317  if (!mBuffer || mBuffer->getBufferSize() < payloadSize)
318  {
319  // buffer isn't big enough; we need to make a new one.
320  payload = (uint8_t*) av_malloc(payloadSize+AV_INPUT_BUFFER_PADDING_SIZE);
321  if (!payload)
322  throw std::bad_alloc();
323 
324  // we don't use the JVM for packets because Ffmpeg is REAL squirly about that
325  mBuffer = Buffer::make(0, payload,
326  payloadSize,
327  Packet::freeAVBuffer, 0);
328  if (!mBuffer) {
329  av_free(payload);
330  throw std::bad_alloc();
331  }
332  // and memset the padding area.
333  memset(payload + payloadSize,
334  0,
335  AV_INPUT_BUFFER_PADDING_SIZE);
336  } else {
337  payload = (uint8_t*)mBuffer->getBytes(0, payloadSize);
338  }
339  VS_ASSERT(mPacket, "Should already have a packet");
340  VS_ASSERT(mBuffer, "Should have allocated a buffer");
341  VS_ASSERT(payload, "Should have allocated a payload");
342  if (mBuffer && mPacket)
343  {
344  mPacket->data = payload;
345 
346  // And start out at zero.
347  mPacket->size = 0;
348  this->setComplete(false, 0);
349 
350  retval = 0;
351  }
352  return retval;
353  }
354 
355  void
357  {
358  wrapBuffer(buffer);
359  }
360 
361  void
362  Packet :: wrapBuffer(IBuffer *buffer)
363  {
364  if (buffer != mBuffer.value())
365  {
366  reset();
367  // and acquire this buffer.
368  mBuffer.reset(buffer, true);
369  }
370  if (mBuffer)
371  {
372  // and patch up our AVPacket
373  VS_ASSERT(mPacket, "No AVPacket");
374  if (mPacket)
375  {
376  mPacket->size = mBuffer->getBufferSize();
377  mPacket->data = (uint8_t*)mBuffer->getBytes(0, mPacket->size);
378  // And assume we're now complete.
379  setComplete(true, mPacket->size);
380  }
381  }
382  }
383  bool
385  {
386  return mIsComplete && mPacket->data;
387  }
388 
389  void
390  Packet :: freeAVBuffer(void * buf, void * closure)
391  {
392  // We know that FFMPEG allocated this with av_malloc, but
393  // that might change in future versions; so this is
394  // inherently somewhat dangerous.
395  (void) closure;
396  av_free(buf);
397  }
398  }}}
static IPacket * make()
Allocate a new packet.
Definition: IPacket.cpp:37
virtual void setFlags(int32_t flags)
Set any internal flags.
Definition: Packet.cpp:125
virtual int32_t allocateNewPayload(int32_t payloadSize)
Discard the current payload and allocate a new payload.
Definition: Packet.cpp:309
virtual int64_t getPts()
Get the Presentation Time Stamp (PTS) for this packet.
Definition: Packet.cpp:63
virtual void setKeyPacket(bool keyPacket)
Set if this is a key packet.
Definition: Packet.cpp:113
virtual void setDuration(int64_t duration)
Set the duration.
Definition: Packet.cpp:155
virtual com::avpkit::ferry::IBuffer * getData()
Get any underlying raw data available for this object.
Definition: Packet.cpp:173
virtual void setPosition(int64_t position)
Set the position.
Definition: Packet.cpp:167
virtual int32_t getMaxSize()
Get the maximum size (in bytes) of payload this packet can hold.
Definition: Packet.cpp:92
virtual bool isKeyPacket()
Does this packet contain Key data? i.e.
Definition: Packet.cpp:107
virtual void setStreamIndex(int32_t streamIndex)
Set the stream index for this packet.
Definition: Packet.cpp:143
virtual void reset()
Clear out any data in this packet, but leaves the buffer available for reuse.
Definition: Packet.cpp:211
virtual int32_t getSize()
Get the size in bytes of the payload currently in this packet.
Definition: Packet.cpp:87
virtual void setPts(int64_t pts)
Set a new Presentation Time Stamp (PTS) for this packet.
Definition: Packet.cpp:69
virtual void setData(com::avpkit::ferry::IBuffer *buffer)
Sets the underlying buffer used by this object.
Definition: Packet.cpp:356
virtual void setDts(int64_t dts)
Set a new Decompression Time Stamp (DTS) for this packet.
Definition: Packet.cpp:81
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 void setComplete(bool complete, int32_t size)
Set if this packet is complete, and what the total size of the data should be assumed to be.
Definition: Packet.cpp:132
virtual int32_t getStreamIndex()
Get the container-specific index for the stream this packet is part of.
Definition: Packet.cpp:97
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
static VS_API_FERRY Buffer * make(RefCounted *requestor, int32_t bufferSize)
Allocate a new buffer of at least bufferSize.
Definition: Buffer.cpp:91
Allows Java code to get data from a native buffers, and optionally modify native memory directly.
Definition: IBuffer.h:54
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...