1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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 General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <pjmedia/format.h>
21 #include <pj/assert.h>
22 #include <pj/errno.h>
23 #include <pj/pool.h>
24 #include <pj/string.h>
25 
26 
27 PJ_DEF(pjmedia_audio_format_detail*)
pjmedia_format_get_audio_format_detail(const pjmedia_format * fmt,pj_bool_t assert_valid)28 pjmedia_format_get_audio_format_detail(const pjmedia_format *fmt,
29 				       pj_bool_t assert_valid)
30 {
31     if (fmt->detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO) {
32 	return (pjmedia_audio_format_detail*) &fmt->det.aud;
33     } else {
34         /* Get rid of unused var compiler warning if pj_assert()
35          * macro does not do anything
36          */
37         PJ_UNUSED_ARG(assert_valid);
38 	pj_assert(!assert_valid || !"Invalid audio format detail");
39 	return NULL;
40     }
41 }
42 
43 
pjmedia_format_copy(pjmedia_format * dst,const pjmedia_format * src)44 PJ_DEF(pjmedia_format*) pjmedia_format_copy(pjmedia_format *dst,
45 					    const pjmedia_format *src)
46 {
47     return (pjmedia_format*)pj_memcpy(dst, src, sizeof(*src));
48 }
49 
50 
51 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
52 
53 
54 static pj_status_t apply_packed_fmt(const pjmedia_video_format_info *fi,
55 	                            pjmedia_video_apply_fmt_param *aparam);
56 
57 static pj_status_t apply_planar_420(const pjmedia_video_format_info *fi,
58 	                            pjmedia_video_apply_fmt_param *aparam);
59 
60 static pj_status_t apply_planar_422(const pjmedia_video_format_info *fi,
61 	                            pjmedia_video_apply_fmt_param *aparam);
62 
63 static pj_status_t apply_planar_444(const pjmedia_video_format_info *fi,
64 	                            pjmedia_video_apply_fmt_param *aparam);
65 
66 static pj_status_t apply_biplanar_420(const pjmedia_video_format_info *fi,
67 				      pjmedia_video_apply_fmt_param *aparam);
68 
69 struct pjmedia_video_format_mgr
70 {
71     unsigned			max_info;
72     unsigned			info_cnt;
73     pjmedia_video_format_info **infos;
74 };
75 
76 static pjmedia_video_format_mgr *video_format_mgr_instance;
77 static pjmedia_video_format_info built_in_vid_fmt_info[] =
78 {
79     {PJMEDIA_FORMAT_RGB24, "RGB24", PJMEDIA_COLOR_MODEL_RGB, 24, 1, &apply_packed_fmt},
80     {PJMEDIA_FORMAT_RGBA,  "RGBA", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt},
81     {PJMEDIA_FORMAT_BGRA,  "BGRA", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt},
82     {PJMEDIA_FORMAT_DIB ,  "DIB ", PJMEDIA_COLOR_MODEL_RGB, 24, 1, &apply_packed_fmt},
83     {PJMEDIA_FORMAT_GBRP,  "GBRP", PJMEDIA_COLOR_MODEL_RGB, 24, 3, &apply_planar_444},
84     {PJMEDIA_FORMAT_AYUV,  "AYUV", PJMEDIA_COLOR_MODEL_YUV, 32, 1, &apply_packed_fmt},
85     {PJMEDIA_FORMAT_YUY2,  "YUY2", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt},
86     {PJMEDIA_FORMAT_UYVY,  "UYVY", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt},
87     {PJMEDIA_FORMAT_YVYU,  "YVYU", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt},
88     {PJMEDIA_FORMAT_I420,  "I420", PJMEDIA_COLOR_MODEL_YUV, 12, 3, &apply_planar_420},
89     {PJMEDIA_FORMAT_YV12,  "YV12", PJMEDIA_COLOR_MODEL_YUV, 12, 3, &apply_planar_420},
90     {PJMEDIA_FORMAT_I422,  "I422", PJMEDIA_COLOR_MODEL_YUV, 16, 3, &apply_planar_422},
91     {PJMEDIA_FORMAT_I420JPEG, "I420JPG", PJMEDIA_COLOR_MODEL_YUV, 12, 3, &apply_planar_420},
92     {PJMEDIA_FORMAT_I422JPEG, "I422JPG", PJMEDIA_COLOR_MODEL_YUV, 16, 3, &apply_planar_422},
93     {PJMEDIA_FORMAT_NV12,  "NV12", PJMEDIA_COLOR_MODEL_YUV, 12, 2, &apply_biplanar_420},
94     {PJMEDIA_FORMAT_NV21,  "NV21", PJMEDIA_COLOR_MODEL_YUV, 12, 2, &apply_biplanar_420},
95 };
96 
pjmedia_format_init_video(pjmedia_format * fmt,pj_uint32_t fmt_id,unsigned width,unsigned height,unsigned fps_num,unsigned fps_denum)97 PJ_DEF(void) pjmedia_format_init_video( pjmedia_format *fmt,
98 					pj_uint32_t fmt_id,
99 					unsigned width,
100 					unsigned height,
101 					unsigned fps_num,
102 					unsigned fps_denum)
103 {
104     pj_assert(fps_denum);
105     fmt->id = fmt_id;
106     fmt->type = PJMEDIA_TYPE_VIDEO;
107     fmt->detail_type = PJMEDIA_FORMAT_DETAIL_VIDEO;
108 
109     fmt->det.vid.size.w = width;
110     fmt->det.vid.size.h = height;
111     fmt->det.vid.fps.num = fps_num;
112     fmt->det.vid.fps.denum = fps_denum;
113     fmt->det.vid.avg_bps = fmt->det.vid.max_bps = 0;
114 
115     if (pjmedia_video_format_mgr_instance()) {
116 	const pjmedia_video_format_info *vfi;
117 	pjmedia_video_apply_fmt_param vafp;
118 	pj_uint32_t bps;
119 
120 	vfi = pjmedia_get_video_format_info(NULL, fmt->id);
121         if (vfi) {
122 	    pj_bzero(&vafp, sizeof(vafp));
123 	    vafp.size = fmt->det.vid.size;
124 	    vfi->apply_fmt(vfi, &vafp);
125 
126 	    bps = (pj_uint32_t)((pj_uint64_t)vafp.framebytes * fps_num * 8 / fps_denum);
127 	    fmt->det.vid.avg_bps = fmt->det.vid.max_bps = bps;
128         }
129     }
130 }
131 
132 PJ_DEF(pjmedia_video_format_detail*)
pjmedia_format_get_video_format_detail(const pjmedia_format * fmt,pj_bool_t assert_valid)133 pjmedia_format_get_video_format_detail(const pjmedia_format *fmt,
134 				       pj_bool_t assert_valid)
135 {
136     if (fmt->detail_type==PJMEDIA_FORMAT_DETAIL_VIDEO) {
137 	return (pjmedia_video_format_detail*)&fmt->det.vid;
138     } else {
139 	pj_assert(!assert_valid || !"Invalid video format detail");
140 	return NULL;
141     }
142 }
143 
144 
apply_packed_fmt(const pjmedia_video_format_info * fi,pjmedia_video_apply_fmt_param * aparam)145 static pj_status_t apply_packed_fmt(const pjmedia_video_format_info *fi,
146 	                            pjmedia_video_apply_fmt_param *aparam)
147 {
148     unsigned i;
149     pj_size_t stride;
150 
151     stride = (pj_size_t)((aparam->size.w*fi->bpp) >> 3);
152 
153     /* Calculate memsize */
154     aparam->framebytes = stride * aparam->size.h;
155 
156     /* Packed formats only use 1 plane */
157     aparam->planes[0] = aparam->buffer;
158     aparam->strides[0] = (int)stride;
159     aparam->plane_bytes[0] = aparam->framebytes;
160 
161     /* Zero unused planes */
162     for (i=1; i<PJMEDIA_MAX_VIDEO_PLANES; ++i) {
163 	aparam->strides[i] = 0;
164 	aparam->planes[i] = NULL;
165     }
166 
167     return PJ_SUCCESS;
168 }
169 
apply_planar_420(const pjmedia_video_format_info * fi,pjmedia_video_apply_fmt_param * aparam)170 static pj_status_t apply_planar_420(const pjmedia_video_format_info *fi,
171 	                             pjmedia_video_apply_fmt_param *aparam)
172 {
173     unsigned i;
174     pj_size_t Y_bytes;
175 
176     PJ_UNUSED_ARG(fi);
177 
178     /* Calculate memsize */
179     Y_bytes = (pj_size_t)(aparam->size.w * aparam->size.h);
180     aparam->framebytes = Y_bytes + (Y_bytes>>1);
181 
182     /* Planar formats use 3 plane */
183     aparam->strides[0] = aparam->size.w;
184     aparam->strides[1] = aparam->strides[2] = (aparam->size.w>>1);
185 
186     aparam->planes[0] = aparam->buffer;
187     aparam->planes[1] = aparam->planes[0] + Y_bytes;
188     aparam->planes[2] = aparam->planes[1] + (Y_bytes>>2);
189 
190     aparam->plane_bytes[0] = Y_bytes;
191     aparam->plane_bytes[1] = aparam->plane_bytes[2] = (Y_bytes>>2);
192 
193     /* Zero unused planes */
194     for (i=3; i<PJMEDIA_MAX_VIDEO_PLANES; ++i) {
195 	aparam->strides[i] = 0;
196 	aparam->planes[i] = NULL;
197         aparam->plane_bytes[i] = 0;
198     }
199 
200     return PJ_SUCCESS;
201 }
202 
apply_planar_422(const pjmedia_video_format_info * fi,pjmedia_video_apply_fmt_param * aparam)203 static pj_status_t apply_planar_422(const pjmedia_video_format_info *fi,
204 	                             pjmedia_video_apply_fmt_param *aparam)
205 {
206     unsigned i;
207     pj_size_t Y_bytes;
208 
209     PJ_UNUSED_ARG(fi);
210 
211     /* Calculate memsize */
212     Y_bytes = (pj_size_t)(aparam->size.w * aparam->size.h);
213     aparam->framebytes = (Y_bytes << 1);
214 
215     /* Planar formats use 3 plane */
216     aparam->strides[0] = aparam->size.w;
217     aparam->strides[1] = aparam->strides[2] = (aparam->size.w>>1);
218 
219     aparam->planes[0] = aparam->buffer;
220     aparam->planes[1] = aparam->planes[0] + Y_bytes;
221     aparam->planes[2] = aparam->planes[1] + (Y_bytes>>1);
222 
223     aparam->plane_bytes[0] = Y_bytes;
224     aparam->plane_bytes[1] = aparam->plane_bytes[2] = (Y_bytes>>1);
225 
226     /* Zero unused planes */
227     for (i=3; i<PJMEDIA_MAX_VIDEO_PLANES; ++i) {
228 	aparam->strides[i] = 0;
229 	aparam->planes[i] = NULL;
230         aparam->plane_bytes[i] = 0;
231     }
232 
233     return PJ_SUCCESS;
234 }
235 
apply_planar_444(const pjmedia_video_format_info * fi,pjmedia_video_apply_fmt_param * aparam)236 static pj_status_t apply_planar_444(const pjmedia_video_format_info *fi,
237 	                            pjmedia_video_apply_fmt_param *aparam)
238 {
239     unsigned i;
240     pj_size_t Y_bytes;
241 
242     PJ_UNUSED_ARG(fi);
243 
244     /* Calculate memsize */
245     Y_bytes = (pj_size_t)(aparam->size.w * aparam->size.h);
246     aparam->framebytes = (Y_bytes * 3);
247 
248     /* Planar formats use 3 plane */
249     aparam->strides[0] = aparam->strides[1] =
250 			 aparam->strides[2] = aparam->size.w;
251 
252     aparam->planes[0] = aparam->buffer;
253     aparam->planes[1] = aparam->planes[0] + Y_bytes;
254     aparam->planes[2] = aparam->planes[1] + Y_bytes;
255 
256     aparam->plane_bytes[0] = aparam->plane_bytes[1] =
257 			     aparam->plane_bytes[2] = Y_bytes;
258 
259     /* Zero unused planes */
260     for (i=3; i<PJMEDIA_MAX_VIDEO_PLANES; ++i) {
261 	aparam->strides[i] = 0;
262 	aparam->planes[i] = NULL;
263         aparam->plane_bytes[i] = 0;
264     }
265 
266     return PJ_SUCCESS;
267 }
268 
apply_biplanar_420(const pjmedia_video_format_info * fi,pjmedia_video_apply_fmt_param * aparam)269 static pj_status_t apply_biplanar_420(const pjmedia_video_format_info *fi,
270 	                              pjmedia_video_apply_fmt_param *aparam)
271 {
272     unsigned i;
273     pj_size_t Y_bytes;
274 
275     PJ_UNUSED_ARG(fi);
276 
277     /* Calculate memsize */
278     Y_bytes = (pj_size_t)(aparam->size.w * aparam->size.h);
279     aparam->framebytes = Y_bytes + (Y_bytes>>1);
280 
281     /* Planar formats use 2 plane */
282     aparam->strides[0] = aparam->size.w;
283     aparam->strides[1] = aparam->size.w;
284 
285     aparam->planes[0] = aparam->buffer;
286     aparam->planes[1] = aparam->planes[0] + Y_bytes;
287 
288     aparam->plane_bytes[0] = Y_bytes;
289     aparam->plane_bytes[1] = (Y_bytes>>1);
290 
291     /* Zero unused planes */
292     for (i=2; i<PJMEDIA_MAX_VIDEO_PLANES; ++i) {
293 	aparam->strides[i] = 0;
294 	aparam->planes[i] = NULL;
295         aparam->plane_bytes[i] = 0;
296     }
297 
298     return PJ_SUCCESS;
299 }
300 
301 
302 PJ_DEF(pj_status_t)
pjmedia_video_format_mgr_create(pj_pool_t * pool,unsigned max_fmt,unsigned options,pjmedia_video_format_mgr ** p_mgr)303 pjmedia_video_format_mgr_create(pj_pool_t *pool,
304 				unsigned max_fmt,
305 				unsigned options,
306 				pjmedia_video_format_mgr **p_mgr)
307 {
308     pjmedia_video_format_mgr *mgr;
309     unsigned i;
310 
311     PJ_ASSERT_RETURN(pool && options==0, PJ_EINVAL);
312 
313     PJ_UNUSED_ARG(options);
314 
315     mgr = PJ_POOL_ALLOC_T(pool, pjmedia_video_format_mgr);
316     mgr->max_info = max_fmt;
317     mgr->info_cnt = 0;
318     mgr->infos = pj_pool_calloc(pool, max_fmt, sizeof(pjmedia_video_format_info *));
319 
320     if (video_format_mgr_instance == NULL)
321 	video_format_mgr_instance = mgr;
322 
323     for (i=0; i<PJ_ARRAY_SIZE(built_in_vid_fmt_info); ++i) {
324 	pjmedia_register_video_format_info(mgr,
325 					   &built_in_vid_fmt_info[i]);
326     }
327 
328     if (p_mgr)
329 	*p_mgr = mgr;
330 
331     return PJ_SUCCESS;
332 }
333 
334 
335 PJ_DEF(const pjmedia_video_format_info*)
pjmedia_get_video_format_info(pjmedia_video_format_mgr * mgr,pj_uint32_t id)336 pjmedia_get_video_format_info(pjmedia_video_format_mgr *mgr,
337 			      pj_uint32_t id)
338 {
339     pjmedia_video_format_info **first;
340     unsigned	 n;
341 
342     if (!mgr)
343 	mgr = pjmedia_video_format_mgr_instance();
344 
345     PJ_ASSERT_RETURN(mgr != NULL, NULL);
346 
347     /* Binary search for the appropriate format id */
348     first = &mgr->infos[0];
349     n = mgr->info_cnt;
350     for (; n > 0; ) {
351 	unsigned half = n / 2;
352 	pjmedia_video_format_info **mid = first + half;
353 
354 	if ((*mid)->id < id) {
355 	    first = ++mid;
356 	    n -= half + 1;
357 	} else if ((*mid)->id==id) {
358 	    return *mid;
359 	} else {
360 	    n = half;
361 	}
362     }
363 
364     return NULL;
365 }
366 
367 
368 PJ_DEF(pj_status_t)
pjmedia_register_video_format_info(pjmedia_video_format_mgr * mgr,pjmedia_video_format_info * info)369 pjmedia_register_video_format_info(pjmedia_video_format_mgr *mgr,
370 				   pjmedia_video_format_info *info)
371 {
372     unsigned i;
373 
374     if (!mgr)
375 	mgr = pjmedia_video_format_mgr_instance();
376 
377     PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVALIDOP);
378 
379     if (mgr->info_cnt >= mgr->max_info)
380 	return PJ_ETOOMANY;
381 
382     /* Insert to the array, sorted */
383     for (i=0; i<mgr->info_cnt; ++i) {
384 	if (mgr->infos[i]->id >= info->id)
385 	    break;
386     }
387 
388     if (i < mgr->info_cnt) {
389 	if (mgr->infos[i]->id == info->id) {
390 	    /* just overwrite */
391 	    mgr->infos[i] = info;
392 	    return PJ_SUCCESS;
393 	}
394 
395 	pj_memmove(&mgr->infos[i+1], &mgr->infos[i],
396 		   (mgr->info_cnt - i) * sizeof(pjmedia_video_format_info*));
397     }
398 
399     mgr->infos[i] = info;
400     mgr->info_cnt++;
401 
402     return PJ_SUCCESS;
403 }
404 
pjmedia_video_format_mgr_instance(void)405 PJ_DEF(pjmedia_video_format_mgr*) pjmedia_video_format_mgr_instance(void)
406 {
407     pj_assert(video_format_mgr_instance != NULL);
408     return video_format_mgr_instance;
409 }
410 
411 PJ_DEF(void)
pjmedia_video_format_mgr_set_instance(pjmedia_video_format_mgr * mgr)412 pjmedia_video_format_mgr_set_instance(pjmedia_video_format_mgr *mgr)
413 {
414     video_format_mgr_instance = mgr;
415 }
416 
417 
pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr * mgr)418 PJ_DEF(void) pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr *mgr)
419 {
420     if (!mgr)
421 	mgr = pjmedia_video_format_mgr_instance();
422 
423     PJ_ASSERT_ON_FAIL(mgr != NULL, return);
424 
425     mgr->info_cnt = 0;
426     if (video_format_mgr_instance == mgr)
427 	video_format_mgr_instance = NULL;
428 }
429 
430 #endif /* PJMEDIA_HAS_VIDEO */
431