1 /**
2  *  Packet management
3  *  Copyright (C) 2008 Andreas Öman
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 
20 #include "tvheadend.h"
21 #include "packet.h"
22 #include "string.h"
23 #include "atomic.h"
24 #include "memoryinfo.h"
25 
26 #ifndef PKTBUF_DATA_ALIGN
27 #define PKTBUF_DATA_ALIGN 64
28 #endif
29 
30 memoryinfo_t pkt_memoryinfo = { .my_name = "Packets" };
31 memoryinfo_t pktbuf_memoryinfo = { .my_name = "Packet buffers" };
32 memoryinfo_t pktref_memoryinfo = { .my_name = "Packet references" };
33 
34 /*
35  *
36  */
37 static void
pkt_destroy(th_pkt_t * pkt)38 pkt_destroy(th_pkt_t *pkt)
39 {
40   if (pkt) {
41     pktbuf_ref_dec(pkt->pkt_payload);
42     pktbuf_ref_dec(pkt->pkt_meta);
43 
44     free(pkt);
45     memoryinfo_free(&pkt_memoryinfo, sizeof(*pkt));
46   }
47 }
48 
49 
50 /**
51  * Allocate a new packet and give it a refcount of one (which caller is
52  * suppoed to take care of)
53  */
54 th_pkt_t *
pkt_alloc(streaming_component_type_t type,const void * data,size_t datalen,int64_t pts,int64_t dts)55 pkt_alloc(streaming_component_type_t type, const void *data, size_t datalen,
56           int64_t pts, int64_t dts)
57 {
58   th_pkt_t *pkt;
59 
60   pkt = calloc(1, sizeof(th_pkt_t));
61   if (pkt) {
62     pkt->pkt_type = type;
63     if(datalen)
64       pkt->pkt_payload = pktbuf_alloc(data, datalen);
65     pkt->pkt_dts = dts;
66     pkt->pkt_pts = pts;
67     pkt->pkt_refcount = 1;
68     memoryinfo_alloc(&pkt_memoryinfo, sizeof(*pkt));
69   }
70   return pkt;
71 }
72 
73 
74 /**
75  *
76  */
77 th_pkt_t *
pkt_copy_shallow(th_pkt_t * pkt)78 pkt_copy_shallow(th_pkt_t *pkt)
79 {
80   th_pkt_t *n = malloc(sizeof(th_pkt_t));
81 
82   if (n) {
83     *n = *pkt;
84 
85     n->pkt_refcount = 1;
86 
87     pktbuf_ref_inc(n->pkt_meta);
88     pktbuf_ref_inc(n->pkt_payload);
89 
90     memoryinfo_alloc(&pkt_memoryinfo, sizeof(*pkt));
91   }
92 
93   return n;
94 }
95 
96 
97 /**
98  *
99  */
100 th_pkt_t *
pkt_copy_nodata(th_pkt_t * pkt)101 pkt_copy_nodata(th_pkt_t *pkt)
102 {
103   th_pkt_t *n = malloc(sizeof(th_pkt_t));
104 
105   if (n) {
106     *n = *pkt;
107 
108     n->pkt_refcount = 1;
109 
110     n->pkt_meta = n->pkt_payload = NULL;
111 
112     memoryinfo_alloc(&pkt_memoryinfo, sizeof(*pkt));
113   }
114 
115   return n;
116 }
117 
118 
119 /**
120  *
121  */
122 void
pkt_ref_dec(th_pkt_t * pkt)123 pkt_ref_dec(th_pkt_t *pkt)
124 {
125   if((atomic_add(&pkt->pkt_refcount, -1)) == 1)
126     pkt_destroy(pkt);
127 }
128 
129 
130 /**
131  *
132  */
133 void
pkt_ref_inc(th_pkt_t * pkt)134 pkt_ref_inc(th_pkt_t *pkt)
135 {
136   atomic_add(&pkt->pkt_refcount, 1);
137 }
138 
139 /**
140  *
141  */
142 void
pkt_ref_inc_poly(th_pkt_t * pkt,int n)143 pkt_ref_inc_poly(th_pkt_t *pkt, int n)
144 {
145   atomic_add(&pkt->pkt_refcount, n);
146 }
147 
148 /**
149  *
150  */
151 void
pkt_trace_(const char * file,int line,int subsys,th_pkt_t * pkt,const char * fmt,...)152 pkt_trace_(const char *file, int line, int subsys, th_pkt_t *pkt,
153            const char *fmt, ...)
154 {
155   char buf[512], _dts[22], _pts[22], _type[2];
156   va_list args;
157 
158   va_start(args, fmt);
159   if (SCT_ISVIDEO(pkt->pkt_type) && pkt->v.pkt_frametype) {
160     _type[0] = pkt_frametype_to_char(pkt->v.pkt_frametype);
161     _type[1] = '\0';
162   } else {
163     _type[0] = '\0';
164   }
165   snprintf(buf, sizeof(buf),
166            "%s%spkt stream %d %s%s%s"
167            " dts %s pts %s"
168            " dur %d len %zu err %i%s",
169            fmt ? fmt : "",
170            fmt ? " (" : "",
171            pkt->pkt_componentindex,
172            streaming_component_type2txt(pkt->pkt_type),
173            _type[0] ? " type " : "", _type,
174            pts_to_string(pkt->pkt_dts, _dts),
175            pts_to_string(pkt->pkt_pts, _pts),
176            pkt->pkt_duration,
177            pktbuf_len(pkt->pkt_payload),
178            pkt->pkt_err,
179            fmt ? ")" : "");
180   tvhlogv(file, line, LOG_TRACE, subsys, buf, &args);
181   va_end(args);
182 }
183 
184 /**
185  *
186  */
187 void
pktref_clear_queue(struct th_pktref_queue * q)188 pktref_clear_queue(struct th_pktref_queue *q)
189 {
190   th_pktref_t *pr;
191 
192   if (q) {
193     while((pr = TAILQ_FIRST(q)) != NULL) {
194       TAILQ_REMOVE(q, pr, pr_link);
195       pkt_ref_dec(pr->pr_pkt);
196       free(pr);
197       memoryinfo_free(&pktref_memoryinfo, sizeof(*pr));
198     }
199   }
200 }
201 
202 
203 /**
204  * Reference count is transfered to queue
205  */
206 void
pktref_enqueue(struct th_pktref_queue * q,th_pkt_t * pkt)207 pktref_enqueue(struct th_pktref_queue *q, th_pkt_t *pkt)
208 {
209   th_pktref_t *pr = malloc(sizeof(th_pktref_t));
210   if (pr) {
211     pr->pr_pkt = pkt;
212     TAILQ_INSERT_TAIL(q, pr, pr_link);
213     memoryinfo_alloc(&pktref_memoryinfo, sizeof(*pr));
214   }
215 }
216 
217 
218 /**
219  *
220  */
221 void
pktref_remove(struct th_pktref_queue * q,th_pktref_t * pr)222 pktref_remove(struct th_pktref_queue *q, th_pktref_t *pr)
223 {
224   if (pr) {
225     if (q)
226       TAILQ_REMOVE(q, pr, pr_link);
227     pkt_ref_dec(pr->pr_pkt);
228     free(pr);
229     memoryinfo_free(&pktref_memoryinfo, sizeof(*pr));
230   }
231 }
232 
233 /**
234  *
235  */
236 th_pkt_t *
pktref_get_first(struct th_pktref_queue * q)237 pktref_get_first(struct th_pktref_queue *q)
238 {
239   th_pktref_t *pr;
240   th_pkt_t *pkt;
241 
242   pr = TAILQ_FIRST(q);
243   if (pr) {
244     pkt = pr->pr_pkt;
245     TAILQ_REMOVE(q, pr, pr_link);
246     free(pr);
247     memoryinfo_free(&pktref_memoryinfo, sizeof(*pr));
248     return pkt;
249   }
250   return NULL;
251 }
252 
253 /**
254  *
255  */
256 void
pktref_insert_head(struct th_pktref_queue * q,th_pkt_t * pkt)257 pktref_insert_head(struct th_pktref_queue *q, th_pkt_t *pkt)
258 {
259   th_pktref_t *pr;
260 
261   pr = pktref_create(pkt);
262   TAILQ_INSERT_HEAD(q, pr, pr_link);
263 }
264 
265 /**
266  *
267  */
268 th_pktref_t *
pktref_create(th_pkt_t * pkt)269 pktref_create(th_pkt_t *pkt)
270 {
271   th_pktref_t *pr = malloc(sizeof(th_pktref_t));
272   if (pr) {
273     pr->pr_pkt = pkt;
274     memoryinfo_alloc(&pktref_memoryinfo, sizeof(*pr));
275   }
276   return pr;
277 }
278 
279 /*
280  *
281  */
282 
283 void
pktbuf_destroy(pktbuf_t * pb)284 pktbuf_destroy(pktbuf_t *pb)
285 {
286   if (pb) {
287     memoryinfo_free(&pktbuf_memoryinfo, sizeof(*pb) + pb->pb_size);
288     free(pb->pb_data);
289     free(pb);
290   }
291 }
292 
293 void
pktbuf_ref_dec(pktbuf_t * pb)294 pktbuf_ref_dec(pktbuf_t *pb)
295 {
296   if (pb) {
297     if((atomic_add(&pb->pb_refcount, -1)) == 1) {
298       memoryinfo_free(&pktbuf_memoryinfo, sizeof(*pb) + pb->pb_size);
299       free(pb->pb_data);
300       free(pb);
301     }
302   }
303 }
304 
305 pktbuf_t *
pktbuf_ref_inc(pktbuf_t * pb)306 pktbuf_ref_inc(pktbuf_t *pb)
307 {
308   if (pb) {
309     atomic_add(&pb->pb_refcount, 1);
310     return pb;
311   }
312   return NULL;
313 }
314 
315 pktbuf_t *
pktbuf_alloc(const void * data,size_t size)316 pktbuf_alloc(const void *data, size_t size)
317 {
318   pktbuf_t *pb = malloc(sizeof(pktbuf_t));
319 
320   if (pb == NULL) return NULL;
321   pb->pb_refcount = 1;
322   pb->pb_size = size;
323   pb->pb_err = 0;
324   if(size > 0) {
325     pb->pb_data = malloc(size);
326     if (pb->pb_data != NULL) {
327       if (data != NULL)
328         memcpy(pb->pb_data, data, size);
329     } else {
330       pb->pb_size = 0;
331     }
332   } else {
333     pb->pb_data = NULL;
334   }
335   memoryinfo_alloc(&pktbuf_memoryinfo, sizeof(*pb) + pb->pb_size);
336   return pb;
337 }
338 
339 pktbuf_t *
pktbuf_make(void * data,size_t size)340 pktbuf_make(void *data, size_t size)
341 {
342   pktbuf_t *pb = malloc(sizeof(pktbuf_t));
343   if (pb) {
344     pb->pb_refcount = 1;
345     pb->pb_size = size;
346     pb->pb_data = data;
347     memoryinfo_alloc(&pktbuf_memoryinfo, sizeof(*pb) + pb->pb_size);
348   }
349   return pb;
350 }
351 
352 pktbuf_t *
pktbuf_append(pktbuf_t * pb,const void * data,size_t size)353 pktbuf_append(pktbuf_t *pb, const void *data, size_t size)
354 {
355   void *ndata;
356   if (pb == NULL)
357     return pktbuf_alloc(data, size);
358   ndata = realloc(pb->pb_data, pb->pb_size + size);
359   if (ndata) {
360     pb->pb_data = ndata;
361     memcpy(ndata + pb->pb_size, data, size);
362     pb->pb_size += size;
363     memoryinfo_append(&pktbuf_memoryinfo, size);
364   }
365   return pb;
366 }
367 
368 /*
369  *
370  */
371 
pts_to_string(int64_t pts,char * buf)372 const char *pts_to_string(int64_t pts, char *buf)
373 {
374   if (pts == PTS_UNSET)
375     return strcpy(buf, "<unset>");
376   snprintf(buf, 22, "%"PRId64, pts);
377   return buf;
378 }
379