blob: 2b9505e254476d24627d03aa00092d5bf654451e
1 | // |
2 | // |
3 | // amlogic 2013 |
4 | // |
5 | // @ Project : tv |
6 | // @ Date : 2013-11 |
7 | // @ Author : |
8 | // |
9 | #define LOG_TAG "CTvSubtitle" |
10 | |
11 | #include "CTvSubtitle.h" |
12 | #include "am_misc.h" |
13 | #include "am_dmx.h" |
14 | |
15 | CTvSubtitle::CTvSubtitle() |
16 | { |
17 | mpObser = NULL; |
18 | } |
19 | |
20 | CTvSubtitle::~CTvSubtitle() |
21 | { |
22 | } |
23 | |
24 | void CTvSubtitle::setObserver(IObserver *pObser) |
25 | { |
26 | isSubOpen = false; |
27 | mpObser = pObser; |
28 | } |
29 | |
30 | void CTvSubtitle::setBuffer(char *share_mem) |
31 | { |
32 | pthread_mutex_lock(&lock); |
33 | buffer = (unsigned char *)share_mem; |
34 | pthread_mutex_unlock(&lock); |
35 | } |
36 | |
37 | void CTvSubtitle::stopDecoder() |
38 | { |
39 | } |
40 | |
41 | /** |
42 | * 开始字幕信息解析showboz sync |
43 | */ |
44 | void CTvSubtitle::startSub() |
45 | { |
46 | } |
47 | |
48 | /** |
49 | * 停止图文/字幕信息解析 |
50 | */ |
51 | void CTvSubtitle::stop() |
52 | { |
53 | } |
54 | |
55 | /** |
56 | * 停止图文/字幕信息解析并清除缓存数据 |
57 | */ |
58 | void CTvSubtitle::clear() |
59 | { |
60 | } |
61 | |
62 | /** |
63 | * 在图文模式下进入下一页 |
64 | */ |
65 | void CTvSubtitle::nextPage() |
66 | { |
67 | } |
68 | |
69 | /** |
70 | * 在图文模式下进入上一页 |
71 | */ |
72 | void CTvSubtitle::previousPage() |
73 | { |
74 | } |
75 | |
76 | /** |
77 | * 在图文模式下跳转到指定页 |
78 | * @param page 要跳转到的页号 |
79 | */ |
80 | void CTvSubtitle::gotoPage(int page __unused) |
81 | { |
82 | } |
83 | |
84 | /** |
85 | * 在图文模式下跳转到home页 |
86 | */ |
87 | void CTvSubtitle::goHome() |
88 | { |
89 | } |
90 | |
91 | /** |
92 | * 在图文模式下根据颜色跳转到指定链接 |
93 | * @param color 颜色,COLOR_RED/COLOR_GREEN/COLOR_YELLOW/COLOR_BLUE |
94 | */ |
95 | void CTvSubtitle::colorLink(int color __unused) |
96 | { |
97 | } |
98 | |
99 | /** |
100 | * 在图文模式下设定搜索字符串 |
101 | * @param pattern 搜索匹配字符串 |
102 | * @param casefold 是否区分大小写 |
103 | */ |
104 | void CTvSubtitle::setSearchPattern(char *pattern __unused, bool casefold __unused) |
105 | { |
106 | } |
107 | |
108 | /** |
109 | * 搜索下一页 |
110 | */ |
111 | void CTvSubtitle::searchNext() |
112 | { |
113 | } |
114 | |
115 | /** |
116 | * 搜索上一页 |
117 | */ |
118 | void CTvSubtitle::searchPrevious() |
119 | { |
120 | } |
121 | |
122 | int CTvSubtitle::sub_init(int bmp_width, int bmp_height) |
123 | { |
124 | pthread_mutex_init(&lock, NULL); |
125 | bmp_w = bmp_width; |
126 | bmp_h = bmp_height; |
127 | sub_w = 720; |
128 | sub_h = 576; |
129 | bmp_pitch = bmp_w * 4; |
130 | return 0; |
131 | } |
132 | |
133 | int CTvSubtitle::sub_destroy() |
134 | { |
135 | return 0; |
136 | } |
137 | |
138 | int CTvSubtitle::sub_lock() |
139 | { |
140 | pthread_mutex_lock(&lock); |
141 | return 0; |
142 | } |
143 | |
144 | int CTvSubtitle::sub_unlock() |
145 | { |
146 | pthread_mutex_unlock(&lock); |
147 | return 0; |
148 | } |
149 | |
150 | int CTvSubtitle::sub_clear() |
151 | { |
152 | return 0; |
153 | } |
154 | |
155 | static void clear_bitmap(CTvSubtitle *pSub) |
156 | { |
157 | unsigned char *ptr = pSub->buffer; |
158 | int y = pSub->bmp_h; |
159 | |
160 | while (y--) { |
161 | memset(ptr, 0, pSub->bmp_pitch); |
162 | ptr += pSub->bmp_pitch; |
163 | } |
164 | } |
165 | |
166 | static void show_sub_cb(AM_SUB2_Handle_t handle, AM_SUB2_Picture_t *pic) |
167 | { |
168 | LOGD("dvb callback-----------"); |
169 | |
170 | CTvSubtitle *pSub = ((CTvSubtitle *) AM_SUB2_GetUserData(handle)); |
171 | pthread_mutex_lock(&pSub->lock); |
172 | clear_bitmap(pSub); |
173 | |
174 | if (pic) { |
175 | AM_SUB2_Region_t *rgn = pic->p_region; |
176 | pSub->sub_w = pic->original_width; |
177 | pSub->sub_h = pic->original_height; |
178 | while (rgn) { |
179 | int sx, sy, dx, dy, rw, rh; |
180 | |
181 | // ensure we have a valid buffer |
182 | if (! rgn->p_buf) { |
183 | rgn = rgn->p_next; |
184 | continue; |
185 | } |
186 | |
187 | sx = 0; |
188 | sy = 0; |
189 | dx = pic->original_x + rgn->left; |
190 | dy = pic->original_y + rgn->top; |
191 | rw = rgn->width; |
192 | rh = rgn->height; |
193 | |
194 | if (dx < 0) { |
195 | sx = -dx; |
196 | dx = 0; |
197 | rw += dx; |
198 | } |
199 | |
200 | if (dx + rw > pSub->bmp_w) { |
201 | rw = pSub->bmp_w - dx; |
202 | } |
203 | |
204 | if (dy < 0) { |
205 | sy = -dy; |
206 | dy = 0; |
207 | rh += dy; |
208 | } |
209 | |
210 | if (dy + rh > pSub->bmp_h) { |
211 | rh = pSub->bmp_h - dy; |
212 | } |
213 | |
214 | if ((rw > 0) && (rh > 0)) { |
215 | unsigned char *sbegin = (unsigned char *)rgn->p_buf + sy * rgn->width + sx; |
216 | unsigned char *dbegin = pSub->buffer + dy * pSub->bmp_pitch + dx * 4; |
217 | unsigned char *src, *dst; |
218 | int size; |
219 | |
220 | while (rh) { |
221 | src = sbegin; |
222 | dst = dbegin; |
223 | size = rw; |
224 | while (size--) { |
225 | int c = src[0]; |
226 | |
227 | if (c < (int)rgn->entry) { |
228 | if (rgn->clut[c].a) { |
229 | *dst++ = rgn->clut[c].r; |
230 | *dst++ = rgn->clut[c].g; |
231 | *dst++ = rgn->clut[c].b; |
232 | } else { |
233 | dst += 3; |
234 | } |
235 | *dst++ = rgn->clut[c].a; |
236 | } else { |
237 | dst += 4; |
238 | } |
239 | src ++; |
240 | } |
241 | sbegin += rgn->width; |
242 | dbegin += pSub->bmp_pitch; |
243 | rh--; |
244 | } |
245 | } |
246 | |
247 | rgn = rgn->p_next; |
248 | } |
249 | pSub->mpObser->updateSubtitle(pic->original_width, pic->original_height); |
250 | } |
251 | pthread_mutex_unlock(&pSub->lock); |
252 | |
253 | } |
254 | |
255 | static uint64_t get_pts_cb(void *handle __unused, uint64_t pts __unused) |
256 | { |
257 | char buf[32]; |
258 | AM_ErrorCode_t ret; |
259 | uint32_t v; |
260 | uint64_t r; |
261 | |
262 | ret = AM_FileRead("/sys/class/tsync/pts_pcrscr", buf, sizeof(buf)); |
263 | if (!ret) { |
264 | v = strtoul(buf, 0, 16); |
265 | if (pts & (1LL << 32)) { |
266 | r = ((uint64_t)v) | (1LL << 32); |
267 | } else { |
268 | r = (uint64_t)v; |
269 | } |
270 | } else { |
271 | r = 0LL; |
272 | } |
273 | |
274 | return r; |
275 | } |
276 | |
277 | static void pes_data_cb(int dev_no __unused, int fhandle __unused, |
278 | const uint8_t *data, int len, void *user_data) |
279 | { |
280 | CTvSubtitle *pSub = ((CTvSubtitle *) user_data); |
281 | AM_PES_Decode(pSub->pes_handle, (uint8_t *)data, len); |
282 | } |
283 | |
284 | static int close_dmx(CTvSubtitle *pSub) |
285 | { |
286 | AM_DMX_FreeFilter(pSub->dmx_id, pSub->filter_handle); |
287 | AM_DMX_Close(pSub->dmx_id); |
288 | pSub->dmx_id = -1; |
289 | pSub->filter_handle = -1; |
290 | |
291 | return 0; |
292 | } |
293 | |
294 | static int open_dmx(CTvSubtitle *pSub, int dmx_id, int pid) |
295 | { |
296 | close_dmx(pSub); |
297 | AM_DMX_OpenPara_t op; |
298 | struct dmx_pes_filter_params pesp; |
299 | AM_ErrorCode_t ret; |
300 | |
301 | pSub->dmx_id = -1; |
302 | pSub->filter_handle = -1; |
303 | memset(&op, 0, sizeof(op)); |
304 | |
305 | ret = AM_DMX_Open(dmx_id, &op); |
306 | if (ret != AM_SUCCESS) { |
307 | LOGD("error AM_DMX_Open != AM_SUCCESS"); |
308 | goto error; |
309 | } |
310 | pSub->dmx_id = dmx_id; |
311 | |
312 | ret = AM_DMX_AllocateFilter(dmx_id, &pSub->filter_handle); |
313 | if (ret != AM_SUCCESS) { |
314 | LOGD("error AM_DMX_AllocateFilter != AM_SUCCESS"); |
315 | goto error; |
316 | } |
317 | |
318 | ret = AM_DMX_SetBufferSize(dmx_id, pSub->filter_handle, 0x80000); |
319 | if (ret != AM_SUCCESS) { |
320 | LOGD("error AM_DMX_SetBufferSize != AM_SUCCESS"); |
321 | goto error; |
322 | } |
323 | |
324 | memset(&pesp, 0, sizeof(pesp)); |
325 | pesp.pid = pid; |
326 | pesp.output = DMX_OUT_TAP; |
327 | pesp.pes_type = DMX_PES_TELETEXT0; |
328 | |
329 | ret = AM_DMX_SetPesFilter(dmx_id, pSub->filter_handle, &pesp); |
330 | if (ret != AM_SUCCESS) { |
331 | LOGD("error AM_DMX_SetPesFilter != AM_SUCCESS, err = %s", strerror(errno)); |
332 | goto error; |
333 | } |
334 | |
335 | ret = AM_DMX_SetCallback(dmx_id, pSub->filter_handle, pes_data_cb, pSub); |
336 | if (ret != AM_SUCCESS) { |
337 | LOGD("error AM_DMX_SetCallback != AM_SUCCESS"); |
338 | goto error; |
339 | } |
340 | |
341 | ret = AM_DMX_StartFilter(dmx_id, pSub->filter_handle); |
342 | if (ret != AM_SUCCESS) { |
343 | LOGD("error AM_DMX_StartFilter != AM_SUCCESS,dmx_id=%d,filter_handle=%d, ret = %d", dmx_id, pSub->filter_handle, ret); |
344 | goto error; |
345 | } |
346 | |
347 | return 0; |
348 | error: |
349 | if (pSub->filter_handle != -1) { |
350 | AM_DMX_FreeFilter(dmx_id, pSub->filter_handle); |
351 | } |
352 | if (pSub->dmx_id != -1) { |
353 | AM_DMX_Close(dmx_id); |
354 | } |
355 | |
356 | return -1; |
357 | } |
358 | |
359 | static void pes_sub_cb(AM_PES_Handle_t handle, uint8_t *buf, int size) |
360 | { |
361 | CTvSubtitle *pSub = ((CTvSubtitle *) AM_SUB2_GetUserData(handle)); |
362 | AM_SUB2_Decode(pSub->sub_handle, buf, size); |
363 | } |
364 | |
365 | int CTvSubtitle::sub_switch_status() |
366 | { |
367 | return isSubOpen ? 1 : 0; |
368 | } |
369 | int CTvSubtitle::sub_start_dvb_sub(int dmx_id, int pid, int page_id, int anc_page_id) |
370 | { |
371 | LOGD("start dvb subtitle=----------------"); |
372 | |
373 | AM_PES_Para_t pesp; |
374 | AM_SUB2_Para_t subp; |
375 | int ret; |
376 | |
377 | memset(&pesp, 0, sizeof(pesp)); |
378 | pesp.packet = pes_sub_cb; |
379 | pesp.user_data = this; |
380 | ret = AM_PES_Create(&pes_handle, &pesp); |
381 | if (ret != AM_SUCCESS) { |
382 | LOGD("error AM_PES_Create != AM_SUCCESS"); |
383 | goto error; |
384 | } |
385 | |
386 | memset(&subp, 0, sizeof(subp)); |
387 | subp.show = show_sub_cb; |
388 | subp.get_pts = get_pts_cb; |
389 | subp.composition_id = page_id; |
390 | subp.ancillary_id = anc_page_id; |
391 | subp.user_data = this; |
392 | ret = AM_SUB2_Create(&sub_handle, &subp); |
393 | if (ret != AM_SUCCESS) { |
394 | LOGD("error AM_SUB2_Create != AM_SUCCESS"); |
395 | goto error; |
396 | } |
397 | |
398 | ret = AM_SUB2_Start(sub_handle); |
399 | if (ret != AM_SUCCESS) { |
400 | LOGD("error AM_SUB2_Start != AM_SUCCESS"); |
401 | goto error; |
402 | } |
403 | |
404 | ret = open_dmx(this, dmx_id, pid); |
405 | if (ret < 0) { |
406 | LOGD("error open_dmx != AM_SUCCESS"); |
407 | goto error; |
408 | } |
409 | isSubOpen = true; |
410 | return 0; |
411 | error: |
412 | if (sub_handle) { |
413 | AM_SUB2_Destroy(sub_handle); |
414 | sub_handle = NULL; |
415 | } |
416 | if (pes_handle) { |
417 | AM_PES_Destroy(pes_handle); |
418 | pes_handle = NULL; |
419 | } |
420 | return -1; |
421 | } |
422 | |
423 | int CTvSubtitle::sub_start_dtv_tt(int dmx_id __unused, int region_id __unused, int pid __unused, |
424 | int page __unused, int sub_page __unused, bool is_sub __unused) |
425 | { |
426 | return 0; |
427 | } |
428 | |
429 | int CTvSubtitle::sub_stop_dvb_sub() |
430 | { |
431 | pthread_mutex_lock(&lock); |
432 | close_dmx(this); |
433 | AM_SUB2_Destroy(sub_handle); |
434 | AM_PES_Destroy(pes_handle); |
435 | |
436 | clear_bitmap(this); |
437 | mpObser->updateSubtitle(0, 0); |
438 | |
439 | sub_handle = NULL; |
440 | pes_handle = NULL; |
441 | isSubOpen = false; |
442 | pthread_mutex_unlock(&lock); |
443 | return 0; |
444 | } |
445 | |
446 | int CTvSubtitle::sub_stop_dtv_tt() |
447 | { |
448 | return 0; |
449 | } |
450 | |
451 | int CTvSubtitle::sub_tt_goto(int page __unused) |
452 | { |
453 | return 0; |
454 | } |
455 | |
456 | int CTvSubtitle::sub_tt_color_link(int color __unused) |
457 | { |
458 | return 0; |
459 | } |
460 | |
461 | int CTvSubtitle::sub_tt_home_link() |
462 | { |
463 | return 0; |
464 | } |
465 | |
466 | int CTvSubtitle::sub_tt_next(int dir __unused) |
467 | { |
468 | return 0; |
469 | } |
470 | |
471 | int CTvSubtitle::sub_tt_set_search_pattern(char *pattern __unused, bool casefold __unused) |
472 | { |
473 | return 0; |
474 | } |
475 | |
476 | int CTvSubtitle::sub_tt_search(int dir __unused) |
477 | { |
478 | return 0; |
479 | } |
480 | |
481 | /* |
482 | * 1, Set the country first and parameters should be either USA or KOREA |
483 | #define CMD_SET_COUNTRY_USA 0x5001 |
484 | #define CMD_SET_COUNTRY_KOREA 0x5002 |
485 | |
486 | 2, Set the source type which including |
487 | a)VBI data(for analog program only) |
488 | b)USER data(for AIR or Cable service) |
489 | CMD_CC_SET_VBIDATA = 0x7001, |
490 | CMD_CC_SET_USERDATA = 0x7002, |
491 | 2.1 If the frontend type is Analog we must set the channel Index |
492 | with command 'CMD_CC_SET_CHAN_NUM' and the parameter is like 57M |
493 | we set 0x20000, this should according to USA standard frequency |
494 | table. |
495 | |
496 | 3, Next is to set the CC service type |
497 | |
498 | #define CMD_CC_1 0x3001 |
499 | #define CMD_CC_2 0x3002 |
500 | #define CMD_CC_3 0x3003 |
501 | #define CMD_CC_4 0x3004 |
502 | |
503 | //this doesn't support currently |
504 | #define CMD_TT_1 0x3005 |
505 | #define CMD_TT_2 0x3006 |
506 | #define CMD_TT_3 0x3007 |
507 | #define CMD_TT_4 0x3008 |
508 | |
509 | #define CMD_SERVICE_1 0x4001 |
510 | #define CMD_SERVICE_2 0x4002 |
511 | #define CMD_SERVICE_3 0x4003 |
512 | #define CMD_SERVICE_4 0x4004 |
513 | #define CMD_SERVICE_5 0x4005 |
514 | #define CMD_SERVICE_6 0x4006 |
515 | |
516 | 4, Then set CMD_CC_START to start the CC service, and you needn't to stop |
517 | |
518 | CC service while switching services |
519 | |
520 | 5, CMD_CC_STOP should be called in some cases like switch source, change |
521 | |
522 | program, no signal, blocked...*/ |
523 | |
524 | //channel_num == 0 ,if frontend is dtv |
525 | //else != 0 |
526 | int CTvSubtitle::sub_start_atsc_cc(enum cc_param_country country, enum cc_param_source_type src_type, int channel_num, enum cc_param_caption_type caption_type) |
527 | { |
528 | LOGD("----sub_start_atsc_cc-1--- country=%d,src=%d,ctype=%d", country, src_type, caption_type); |
529 | switch (country) { |
530 | case CC_PARAM_COUNTRY_USA: |
531 | AM_CC_Cmd(CMD_SET_COUNTRY_USA); |
532 | break; |
533 | case CC_PARAM_COUNTRY_KOREA: |
534 | AM_CC_Cmd(CMD_SET_COUNTRY_KOREA); |
535 | break; |
536 | default: |
537 | AM_CC_Cmd(CMD_SET_COUNTRY_USA); |
538 | break; |
539 | } |
540 | |
541 | switch (src_type) { |
542 | case CC_PARAM_SOURCE_VBIDATA: |
543 | AM_CC_Cmd(CMD_CC_SET_VBIDATA); |
544 | break; |
545 | case CC_PARAM_SOURCE_USERDATA: |
546 | AM_CC_Cmd(CMD_CC_SET_USERDATA); |
547 | break; |
548 | default: |
549 | AM_CC_Cmd(CMD_CC_SET_USERDATA); |
550 | break; |
551 | } |
552 | |
553 | //just for test |
554 | if (channel_num == 0) { |
555 | } else { |
556 | //AM_CC_Cmd(CMD_CC_SET_CHAN_NUM); |
557 | } |
558 | |
559 | AM_CLOSECAPTION_cmd_t cc_t_cmd; |
560 | switch (caption_type) { |
561 | case CC_PARAM_ANALOG_CAPTION_TYPE_CC1: |
562 | cc_t_cmd = CMD_CC_1; |
563 | break; |
564 | case CC_PARAM_ANALOG_CAPTION_TYPE_CC2: |
565 | cc_t_cmd = CMD_CC_2; |
566 | break; |
567 | case CC_PARAM_ANALOG_CAPTION_TYPE_CC3: |
568 | cc_t_cmd = CMD_CC_3; |
569 | break; |
570 | case CC_PARAM_ANALOG_CAPTION_TYPE_CC4: |
571 | cc_t_cmd = CMD_CC_4; |
572 | break; |
573 | case CC_PARAM_DIGITAL_CAPTION_TYPE_SERVICE1: |
574 | cc_t_cmd = CMD_SERVICE_1; |
575 | break; |
576 | case CC_PARAM_DIGITAL_CAPTION_TYPE_SERVICE2: |
577 | cc_t_cmd = CMD_SERVICE_2; |
578 | break; |
579 | case CC_PARAM_DIGITAL_CAPTION_TYPE_SERVICE3: |
580 | cc_t_cmd = CMD_SERVICE_3; |
581 | break; |
582 | case CC_PARAM_DIGITAL_CAPTION_TYPE_SERVICE4: |
583 | cc_t_cmd = CMD_SERVICE_4; |
584 | break; |
585 | default: |
586 | cc_t_cmd = CMD_SERVICE_1; |
587 | break; |
588 | } |
589 | AM_CC_Cmd(cc_t_cmd); |
590 | |
591 | AM_CC_Set_CallBack(close_caption_callback, this); |
592 | AM_VCHIP_Set_CallBack(atv_vchip_callback, this); |
593 | //start |
594 | AM_CC_Cmd(CMD_CC_START); |
595 | LOGD("----sub_start_atsc_cc-2--- country=%d,src=%d,ctype=%d", country, src_type, caption_type); |
596 | return 0; |
597 | } |
598 | |
599 | int CTvSubtitle::sub_stop_atsc_cc() |
600 | { |
601 | LOGD("----sub_stop_atsc_cc----"); |
602 | AM_CC_Cmd(CMD_CC_STOP); |
603 | return 0; |
604 | } |
605 | |
606 | int CTvSubtitle::ResetVchipChgStat() |
607 | { |
608 | avchip_chg = 0; |
609 | AM_CC_Cmd(CMD_VCHIP_RST_CHGSTAT); |
610 | return 0; |
611 | } |
612 | |
613 | int CTvSubtitle::IsVchipChange() |
614 | { |
615 | return avchip_chg; |
616 | } |
617 | |
618 | //cnt :data buf len |
619 | //databuf len is max 512 |
620 | //cmdbuf len is max 128 |
621 | void CTvSubtitle::close_caption_callback(char *str, int cnt, int data_buf[], int cmd_buf[], void *user_data) |
622 | { |
623 | /* |
624 | CTvSubtitle *pSub = (CTvSubtitle *)user_data; |
625 | |
626 | if (pSub == NULL) |
627 | { |
628 | LOGD("sub cc callback is null user data for this"); |
629 | return; |
630 | } |
631 | |
632 | if (pSub->mpObser == NULL) return; |
633 | |
634 | pSub->mCurCCEv.mDataBufSize = cnt; |
635 | pSub->mCurCCEv.mpDataBuffer = data_buf; |
636 | pSub->mCurCCEv.mCmdBufSize = 128;//max |
637 | pSub->mCurCCEv.mpCmdBuffer = cmd_buf; |
638 | |
639 | pSub->mpObser->onEvent(pSub->mCurCCEv); |
640 | */ |
641 | |
642 | str = str; |
643 | cnt = cnt; |
644 | data_buf = data_buf; |
645 | cmd_buf = cmd_buf; |
646 | user_data = user_data; |
647 | } |
648 | |
649 | void CTvSubtitle::atv_vchip_callback(int Is_chg, void *user_data) |
650 | { |
651 | CTvSubtitle *pSub = (CTvSubtitle *)user_data; |
652 | pSub->avchip_chg = Is_chg; |
653 | } |
654 | |
655 |