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