blob: bd84703d54efcefae54d5911732326114677a6ac
1 | // |
2 | // |
3 | // amlogic 2013 |
4 | // |
5 | // @ Project : tv |
6 | // @ Date : 2013-11 |
7 | // @ Author : |
8 | // |
9 | // |
10 | #include "CTvSubtitle.h" |
11 | #include "am_misc.h" |
12 | #include "am_dmx.h" |
13 | CTvSubtitle::CTvSubtitle() |
14 | { |
15 | mpObser = NULL; |
16 | } |
17 | CTvSubtitle::~CTvSubtitle() |
18 | { |
19 | } |
20 | void CTvSubtitle::setObserver(IObserver *pObser) |
21 | { |
22 | isSubOpen = false; |
23 | mpObser = pObser; |
24 | } |
25 | void CTvSubtitle::setBuffer(char *share_mem) |
26 | { |
27 | pthread_mutex_lock(&lock); |
28 | buffer = (unsigned char *)share_mem; |
29 | pthread_mutex_unlock(&lock); |
30 | } |
31 | void CTvSubtitle::stopDecoder() |
32 | { |
33 | } |
34 | /** |
35 | * 开始字幕信息解析showboz sync |
36 | */ |
37 | void CTvSubtitle::startSub() |
38 | { |
39 | } |
40 | |
41 | /** |
42 | * 停止图文/字幕信息解析 |
43 | */ |
44 | void CTvSubtitle::stop() |
45 | { |
46 | } |
47 | |
48 | /** |
49 | * 停止图文/字幕信息解析并清除缓存数据 |
50 | */ |
51 | void CTvSubtitle::clear() |
52 | { |
53 | |
54 | } |
55 | |
56 | /** |
57 | * 在图文模式下进入下一页 |
58 | */ |
59 | void CTvSubtitle::nextPage() |
60 | { |
61 | |
62 | } |
63 | |
64 | /** |
65 | * 在图文模式下进入上一页 |
66 | */ |
67 | void CTvSubtitle::previousPage() |
68 | { |
69 | |
70 | } |
71 | |
72 | /** |
73 | * 在图文模式下跳转到指定页 |
74 | * @param page 要跳转到的页号 |
75 | */ |
76 | void CTvSubtitle::gotoPage(int page) |
77 | { |
78 | |
79 | } |
80 | |
81 | /** |
82 | * 在图文模式下跳转到home页 |
83 | */ |
84 | void CTvSubtitle::goHome() |
85 | { |
86 | |
87 | } |
88 | |
89 | /** |
90 | * 在图文模式下根据颜色跳转到指定链接 |
91 | * @param color 颜色,COLOR_RED/COLOR_GREEN/COLOR_YELLOW/COLOR_BLUE |
92 | */ |
93 | void CTvSubtitle::colorLink(int color) |
94 | { |
95 | |
96 | } |
97 | |
98 | /** |
99 | * 在图文模式下设定搜索字符串 |
100 | * @param pattern 搜索匹配字符串 |
101 | * @param casefold 是否区分大小写 |
102 | */ |
103 | void CTvSubtitle::setSearchPattern(char *pattern, bool casefold) |
104 | { |
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 | |
167 | static void show_sub_cb(AM_SUB2_Handle_t handle, AM_SUB2_Picture_t *pic) |
168 | { |
169 | LOGD("dvb callback-----------"); |
170 | |
171 | CTvSubtitle *pSub = ((CTvSubtitle *) AM_SUB2_GetUserData(handle)); |
172 | pthread_mutex_lock(&pSub->lock); |
173 | clear_bitmap(pSub); |
174 | |
175 | if (pic) { |
176 | AM_SUB2_Region_t *rgn = pic->p_region; |
177 | pSub->sub_w = pic->original_width; |
178 | pSub->sub_h = pic->original_height; |
179 | while (rgn) { |
180 | int sx, sy, dx, dy, rw, rh; |
181 | |
182 | // ensure we have a valid buffer |
183 | if (! rgn->p_buf) { |
184 | rgn = rgn->p_next; |
185 | continue; |
186 | } |
187 | |
188 | sx = 0; |
189 | sy = 0; |
190 | dx = pic->original_x + rgn->left; |
191 | dy = pic->original_y + rgn->top; |
192 | rw = rgn->width; |
193 | rh = rgn->height; |
194 | |
195 | if (dx < 0) { |
196 | sx = -dx; |
197 | dx = 0; |
198 | rw += dx; |
199 | } |
200 | |
201 | if (dx + rw > pSub->bmp_w) { |
202 | rw = pSub->bmp_w - dx; |
203 | } |
204 | |
205 | if (dy < 0) { |
206 | sy = -dy; |
207 | dy = 0; |
208 | rh += dy; |
209 | } |
210 | |
211 | if (dy + rh > pSub->bmp_h) { |
212 | rh = pSub->bmp_h - dy; |
213 | } |
214 | |
215 | if ((rw > 0) && (rh > 0)) { |
216 | unsigned char *sbegin = (unsigned char *)rgn->p_buf + sy * rgn->width + sx; |
217 | unsigned char *dbegin = pSub->buffer + dy * pSub->bmp_pitch + dx * 4; |
218 | unsigned char *src, *dst; |
219 | int size; |
220 | |
221 | while (rh) { |
222 | src = sbegin; |
223 | dst = dbegin; |
224 | size = rw; |
225 | while (size--) { |
226 | int c = src[0]; |
227 | |
228 | if (c < (int)rgn->entry) { |
229 | if (rgn->clut[c].a) { |
230 | *dst++ = rgn->clut[c].r; |
231 | *dst++ = rgn->clut[c].g; |
232 | *dst++ = rgn->clut[c].b; |
233 | } else { |
234 | dst += 3; |
235 | } |
236 | *dst++ = rgn->clut[c].a; |
237 | } else { |
238 | dst += 4; |
239 | } |
240 | src ++; |
241 | } |
242 | sbegin += rgn->width; |
243 | dbegin += pSub->bmp_pitch; |
244 | rh--; |
245 | } |
246 | } |
247 | |
248 | rgn = rgn->p_next; |
249 | } |
250 | pSub->mpObser->updateSubtitle(pic->original_width, pic->original_height); |
251 | } |
252 | pthread_mutex_unlock(&pSub->lock); |
253 | |
254 | } |
255 | |
256 | static uint64_t get_pts_cb(void *handle, uint64_t pts) |
257 | { |
258 | char buf[32]; |
259 | AM_ErrorCode_t ret; |
260 | uint32_t v; |
261 | uint64_t r; |
262 | |
263 | ret = AM_FileRead("/sys/class/tsync/pts_pcrscr", buf, sizeof(buf)); |
264 | if (!ret) { |
265 | v = strtoul(buf, 0, 16); |
266 | if (pts & (1LL << 32)) { |
267 | r = ((uint64_t)v) | (1LL << 32); |
268 | } else { |
269 | r = (uint64_t)v; |
270 | } |
271 | } else { |
272 | r = 0LL; |
273 | } |
274 | |
275 | return r; |
276 | } |
277 | |
278 | static void pes_data_cb(int dev_no, int fhandle, 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 = %d", 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, int region_id, int pid, int page, int sub_page, bool is_sub) |
424 | { |
425 | return 0; |
426 | } |
427 | |
428 | int CTvSubtitle::sub_stop_dvb_sub() |
429 | { |
430 | pthread_mutex_lock(&lock); |
431 | close_dmx(this); |
432 | AM_SUB2_Destroy(sub_handle); |
433 | AM_PES_Destroy(pes_handle); |
434 | |
435 | clear_bitmap(this); |
436 | mpObser->updateSubtitle(0, 0); |
437 | |
438 | sub_handle = NULL; |
439 | pes_handle = NULL; |
440 | isSubOpen = false; |
441 | pthread_mutex_unlock(&lock); |
442 | return 0; |
443 | } |
444 | |
445 | int CTvSubtitle::sub_stop_dtv_tt() |
446 | { |
447 | return 0; |
448 | } |
449 | |
450 | int CTvSubtitle::sub_tt_goto(int page) |
451 | { |
452 | return 0; |
453 | } |
454 | |
455 | int CTvSubtitle::sub_tt_color_link(int color) |
456 | { |
457 | return 0; |
458 | } |
459 | |
460 | int CTvSubtitle::sub_tt_home_link() |
461 | { |
462 | return 0; |
463 | } |
464 | |
465 | int CTvSubtitle::sub_tt_next(int dir) |
466 | { |
467 | return 0; |
468 | } |
469 | |
470 | int CTvSubtitle::sub_tt_set_search_pattern(char *pattern, bool casefold) |
471 | { |
472 | return 0; |
473 | } |
474 | |
475 | int CTvSubtitle::sub_tt_search(int dir) |
476 | { |
477 | return 0; |
478 | } |
479 | |
480 | /* |
481 | * 1, Set the country first and parameters should be either USA or KOREA |
482 | #define CMD_SET_COUNTRY_USA 0x5001 |
483 | #define CMD_SET_COUNTRY_KOREA 0x5002 |
484 | |
485 | 2, Set the source type which including |
486 | a)VBI data(for analog program only) |
487 | b)USER data(for AIR or Cable service) |
488 | CMD_CC_SET_VBIDATA = 0x7001, |
489 | CMD_CC_SET_USERDATA = 0x7002, |
490 | 2.1 If the frontend type is Analog we must set the channel Index |
491 | with command 'CMD_CC_SET_CHAN_NUM' and the parameter is like 57M |
492 | we set 0x20000, this should according to USA standard frequency |
493 | table. |
494 | |
495 | 3, Next is to set the CC service type |
496 | |
497 | #define CMD_CC_1 0x3001 |
498 | #define CMD_CC_2 0x3002 |
499 | #define CMD_CC_3 0x3003 |
500 | #define CMD_CC_4 0x3004 |
501 | |
502 | //this doesn't support currently |
503 | #define CMD_TT_1 0x3005 |
504 | #define CMD_TT_2 0x3006 |
505 | #define CMD_TT_3 0x3007 |
506 | #define CMD_TT_4 0x3008 |
507 | |
508 | #define CMD_SERVICE_1 0x4001 |
509 | #define CMD_SERVICE_2 0x4002 |
510 | #define CMD_SERVICE_3 0x4003 |
511 | #define CMD_SERVICE_4 0x4004 |
512 | #define CMD_SERVICE_5 0x4005 |
513 | #define CMD_SERVICE_6 0x4006 |
514 | |
515 | 4, Then set CMD_CC_START to start the CC service, and you needn't to stop |
516 | |
517 | CC service while switching services |
518 | |
519 | 5, CMD_CC_STOP should be called in some cases like switch source, change |
520 | |
521 | program, no signal, blocked...*/ |
522 | |
523 | //channel_num == 0 ,if frontend is dtv |
524 | //else != 0 |
525 | 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) |
526 | { |
527 | LOGD("----sub_start_atsc_cc-1--- country=%d,src=%d,ctype=%d", country, src_type, caption_type); |
528 | switch (country) { |
529 | case CC_PARAM_COUNTRY_USA: |
530 | AM_CC_Cmd(CMD_SET_COUNTRY_USA); |
531 | break; |
532 | case CC_PARAM_COUNTRY_KOREA: |
533 | AM_CC_Cmd(CMD_SET_COUNTRY_KOREA); |
534 | break; |
535 | default: |
536 | AM_CC_Cmd(CMD_SET_COUNTRY_USA); |
537 | break; |
538 | } |
539 | |
540 | switch (src_type) { |
541 | case CC_PARAM_SOURCE_VBIDATA: |
542 | AM_CC_Cmd(CMD_CC_SET_VBIDATA); |
543 | break; |
544 | case CC_PARAM_SOURCE_USERDATA: |
545 | AM_CC_Cmd(CMD_CC_SET_USERDATA); |
546 | break; |
547 | default: |
548 | AM_CC_Cmd(CMD_CC_SET_USERDATA); |
549 | break; |
550 | } |
551 | |
552 | //just for test |
553 | if (channel_num == 0) { |
554 | } else { |
555 | //AM_CC_Cmd(CMD_CC_SET_CHAN_NUM); |
556 | } |
557 | |
558 | AM_CLOSECAPTION_cmd_t cc_t_cmd; |
559 | switch (caption_type) { |
560 | case CC_PARAM_ANALOG_CAPTION_TYPE_CC1: |
561 | cc_t_cmd = CMD_CC_1; |
562 | break; |
563 | case CC_PARAM_ANALOG_CAPTION_TYPE_CC2: |
564 | cc_t_cmd = CMD_CC_2; |
565 | break; |
566 | case CC_PARAM_ANALOG_CAPTION_TYPE_CC3: |
567 | cc_t_cmd = CMD_CC_3; |
568 | break; |
569 | case CC_PARAM_ANALOG_CAPTION_TYPE_CC4: |
570 | cc_t_cmd = CMD_CC_4; |
571 | break; |
572 | case CC_PARAM_DIGITAL_CAPTION_TYPE_SERVICE1: |
573 | cc_t_cmd = CMD_SERVICE_1; |
574 | break; |
575 | case CC_PARAM_DIGITAL_CAPTION_TYPE_SERVICE2: |
576 | cc_t_cmd = CMD_SERVICE_2; |
577 | break; |
578 | case CC_PARAM_DIGITAL_CAPTION_TYPE_SERVICE3: |
579 | cc_t_cmd = CMD_SERVICE_3; |
580 | break; |
581 | case CC_PARAM_DIGITAL_CAPTION_TYPE_SERVICE4: |
582 | cc_t_cmd = CMD_SERVICE_4; |
583 | break; |
584 | default: |
585 | cc_t_cmd = CMD_SERVICE_1; |
586 | break; |
587 | } |
588 | AM_CC_Cmd(cc_t_cmd); |
589 | |
590 | AM_CC_Set_CallBack(close_caption_callback, this); |
591 | AM_VCHIP_Set_CallBack(atv_vchip_callback, this); |
592 | //start |
593 | AM_CC_Cmd(CMD_CC_START); |
594 | LOGD("----sub_start_atsc_cc-2--- country=%d,src=%d,ctype=%d", country, src_type, caption_type); |
595 | return 0; |
596 | } |
597 | |
598 | int CTvSubtitle::sub_stop_atsc_cc() |
599 | { |
600 | LOGD("----sub_stop_atsc_cc----"); |
601 | AM_CC_Cmd(CMD_CC_STOP); |
602 | return 0; |
603 | } |
604 | int CTvSubtitle::ResetVchipChgStat() |
605 | { |
606 | avchip_chg = 0; |
607 | AM_CC_Cmd(CMD_VCHIP_RST_CHGSTAT); |
608 | return 0; |
609 | } |
610 | int CTvSubtitle::IsVchipChange() |
611 | { |
612 | return avchip_chg; |
613 | } |
614 | //cnt :data buf len |
615 | //databuf len is max 512 |
616 | //cmdbuf len is max 128 |
617 | void CTvSubtitle::close_caption_callback(char *str, int cnt, int data_buf[], int cmd_buf[], void *user_data) |
618 | { |
619 | |
620 | /* |
621 | CTvSubtitle *pSub = (CTvSubtitle *)user_data; |
622 | |
623 | if (pSub == NULL) |
624 | { |
625 | LOGD("sub cc callback is null user data for this"); |
626 | return; |
627 | } |
628 | |
629 | if (pSub->mpObser == NULL) return; |
630 | |
631 | pSub->mCurCCEv.mDataBufSize = cnt; |
632 | pSub->mCurCCEv.mpDataBuffer = data_buf; |
633 | pSub->mCurCCEv.mCmdBufSize = 128;//max |
634 | pSub->mCurCCEv.mpCmdBuffer = cmd_buf; |
635 | |
636 | pSub->mpObser->onEvent(pSub->mCurCCEv); |
637 | */ |
638 | } |
639 | void CTvSubtitle::atv_vchip_callback(int Is_chg, void *user_data) |
640 | { |
641 | CTvSubtitle *pSub = (CTvSubtitle *)user_data; |
642 | pSub->avchip_chg = Is_chg; |
643 | } |
644 |