/* * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. * All rights reserved. * * File : subtitleUtils.cpp * Description : subtitle util function, including charset convert * History : * Author : AL3 * Date : 2015/05/05 * Comment : create first version * */ #include "subtitleUtils.h" #include "media/mediaplayerinfo.h" #include "unicode/ucnv.h" #include "unicode/ustring.h" #include "cdx_log.h" namespace android { //* the CHARSET_XXX strings is defined in "av/include/media/mediaplayerinfo.h", //* and it should be the same as strings defined in "base/media/java/android/media/MediaPlayer.java" const char* strTextCodecFormats[] = { CHARSET_UNKNOWN , CHARSET_BIG5 , CHARSET_BIG5_HKSCS , CHARSET_BOCU_1 , CHARSET_CESU_8 , CHARSET_CP864 , CHARSET_EUC_JP , CHARSET_EUC_KR , CHARSET_GB18030 , CHARSET_GBK , CHARSET_HZ_GB_2312 , CHARSET_ISO_2022_CN , CHARSET_ISO_2022_CN_EXT , CHARSET_ISO_2022_JP , CHARSET_ISO_2022_KR , CHARSET_ISO_8859_1 , CHARSET_ISO_8859_10 , CHARSET_ISO_8859_13 , CHARSET_ISO_8859_14 , CHARSET_ISO_8859_15 , CHARSET_ISO_8859_16 , CHARSET_ISO_8859_2 , CHARSET_ISO_8859_3 , CHARSET_ISO_8859_4 , CHARSET_ISO_8859_5 , CHARSET_ISO_8859_6 , CHARSET_ISO_8859_7 , CHARSET_ISO_8859_8 , CHARSET_ISO_8859_9 , CHARSET_KOI8_R , CHARSET_KOI8_U , CHARSET_MACINTOSH , CHARSET_SCSU , CHARSET_SHIFT_JIS , CHARSET_TIS_620 , CHARSET_US_ASCII , CHARSET_UTF_16 , CHARSET_UTF_16BE , CHARSET_UTF_16LE , CHARSET_UTF_32 , CHARSET_UTF_32BE , CHARSET_UTF_32LE , CHARSET_UTF_7 , CHARSET_UTF_8 , CHARSET_WINDOWS_1250 , CHARSET_WINDOWS_1251 , CHARSET_WINDOWS_1252 , CHARSET_WINDOWS_1253 , CHARSET_WINDOWS_1254 , CHARSET_WINDOWS_1255 , CHARSET_WINDOWS_1256 , CHARSET_WINDOWS_1257 , CHARSET_WINDOWS_1258 , CHARSET_X_DOCOMO_SHIFT_JIS_2007 , CHARSET_X_GSM_03_38_2000 , CHARSET_X_IBM_1383_P110_1999 , CHARSET_X_IMAP_MAILBOX_NAME , CHARSET_X_ISCII_BE , CHARSET_X_ISCII_DE , CHARSET_X_ISCII_GU , CHARSET_X_ISCII_KA , CHARSET_X_ISCII_MA , CHARSET_X_ISCII_OR , CHARSET_X_ISCII_PA , CHARSET_X_ISCII_TA , CHARSET_X_ISCII_TE , CHARSET_X_ISO_8859_11_2001 , CHARSET_X_JAVAUNICODE , CHARSET_X_KDDI_SHIFT_JIS_2007 , CHARSET_X_MAC_CYRILLIC , CHARSET_X_SOFTBANK_SHIFT_JIS_2007 , CHARSET_X_UNICODEBIG , CHARSET_X_UTF_16LE_BOM , CHARSET_X_UTF16_OPPOSITEENDIAN , CHARSET_X_UTF16_PLATFORMENDIAN , CHARSET_X_UTF32_OPPOSITEENDIAN , CHARSET_X_UTF32_PLATFORMENDIAN , NULL }; static int SubtitleUtilsConvertUnicode(char* pOutBuf, int nOutBufSize, SubtitleItem* pSubItem, const char* pDefaultTextFormatName) { const char *pInBuf = pSubItem->pText; ESubtitleTextFormat eInputTextFormat = pSubItem->eTextFormat; int len = pSubItem->nTextLength; int charset; const char* enc;; unsigned int subTextLen; UErrorCode status; UConverter* conv; UConverter* utf8Conv; const char* src; int targetLength; char* target; //*if subtitle decoder know charset,use it ,or use app's setting. switch(eInputTextFormat) { case SUBTITLE_TEXT_FORMAT_UTF8: enc = CHARSET_UTF_8; break; case SUBTITLE_TEXT_FORMAT_GB2312: enc = CHARSET_HZ_GB_2312; break; case SUBTITLE_TEXT_FORMAT_UTF16LE: enc = CHARSET_UTF_16LE; break; case SUBTITLE_TEXT_FORMAT_UTF16BE: enc = CHARSET_UTF_16BE; break; case SUBTITLE_TEXT_FORMAT_UTF32LE: enc = CHARSET_UTF_32LE; break; case SUBTITLE_TEXT_FORMAT_UTF32BE: enc = CHARSET_UTF_32BE; break; case SUBTITLE_TEXT_FORMAT_BIG5: enc = CHARSET_BIG5; break; case SUBTITLE_TEXT_FORMAT_GBK: enc = CHARSET_GBK; break; case SUBTITLE_TEXT_FORMAT_ANSI: //can not match,set to "UTF-8" enc = CHARSET_UTF_8; break; default: enc = pDefaultTextFormatName; break; } status = U_ZERO_ERROR; conv = ucnv_open(enc, &status); if(U_FAILURE(status)) { logw("could not create UConverter for %s\n", enc); memset(pOutBuf, 0, nOutBufSize); return -1; } utf8Conv = ucnv_open("UTF-8", &status); if (U_FAILURE(status)) { logw("could not create UConverter for UTF-8\n"); memset(pOutBuf, 0, nOutBufSize); ucnv_close(conv); return -1; } //*first we need to untangle the utf8 and convert it back to the original bytes // since we are reducing the length of the string, we can do this in place src = pInBuf; targetLength = len * 3 + 1; //*now convert from native encoding to UTF-8 if(targetLength > nOutBufSize) targetLength = nOutBufSize; memset(pOutBuf, 0, nOutBufSize); target = &pOutBuf[0]; ucnv_convertEx(utf8Conv, conv, &target, (const char*)target + targetLength, &src, (const char*)src + len, NULL, NULL, NULL, NULL, true, true, &status); if (U_FAILURE(status)) { logw("ucnv_convertEx failed: %d\n", status); memset(pOutBuf, 0, nOutBufSize); } subTextLen = target - (char*)pOutBuf; logv("CedarXTimedText::convertUniCode src = %s, target = %s\n, subTextLen = %d", pInBuf, pOutBuf, subTextLen); ucnv_close(conv); ucnv_close(utf8Conv); return OK; } // Store the timing and text sample in a Parcel. // The Parcel will be sent to MediaPlayer.java through event, and will be // parsed in TimedText.java. int SubtitleUtilsFillTextSubtitleToParcel(Parcel* parcelDst, SubtitleItem* pSubItem, int nSubtitleID, const char* strDefaultTextFormatName, int* mIsFistItem) { int nFontStyleIdx; int nSubStyleFlag; unsigned int nTextLen; int nPts; #define MAX_OUTPUT_TEXT_SIZE (1024) char strOutText[MAX_OUTPUT_TEXT_SIZE]; SubtitleUtilsConvertUnicode(strOutText, MAX_OUTPUT_TEXT_SIZE, pSubItem, strDefaultTextFormatName); nFontStyleIdx = 0; nSubStyleFlag = 0; nTextLen = strlen((char*)strOutText); parcelDst->writeInt32(KEY_LOCAL_SETTING); parcelDst->writeInt32(KEY_START_TIME); //* pts in unit of us, write out pts in unit of ms. parcelDst->writeInt32((int)(pSubItem->nPts/1000)); parcelDst->writeInt32(KEY_STRUCT_TEXT); parcelDst->writeInt32(nTextLen); //* write the size of the text sample parcelDst->writeInt32(nTextLen); //* write the text sample as a byte array parcelDst->write((char*)strOutText, nTextLen); //*set subtitleID parcelDst->writeInt32(KEY_SUBTITLE_ID); parcelDst->writeInt32(nSubtitleID); if(pSubItem->eAlignment != 0) { parcelDst->writeInt32(KEY_STRUCT_AWEXTEND_SUBDISPPOS); //*write text subtitle's position area. parcelDst->writeInt32(pSubItem->eAlignment); parcelDst->writeInt32(KEY_STRUCT_AWEXTEND_SCREENRECT); //*write text subtitle's whole screen rect. parcelDst->writeInt32(0); parcelDst->writeInt32(0); parcelDst->writeInt32(pSubItem->nReferenceVideoHeight); parcelDst->writeInt32(pSubItem->nReferenceVideoWidth); if(*mIsFistItem == 1) { parcelDst->writeInt32(KEY_STRUCT_TEXT_POS); //*write text subtitle's position Rect. parcelDst->writeInt32(pSubItem->nStartY); parcelDst->writeInt32(pSubItem->nStartX); parcelDst->writeInt32(pSubItem->nEndY); parcelDst->writeInt32(pSubItem->nEndX); *mIsFistItem = 0; } } if(pSubItem->nFontSize!=0 && pSubItem->nPrimaryColor!=0) { //*convert strFontName,bBold,bItalic,bUnderlined if(!strcmp(pSubItem->strFontName, SUBTITLE_FONT_NAME_EPILOG)) nFontStyleIdx = 0; else if(!strcmp(pSubItem->strFontName, SUBTITLE_FONT_NAME_VERDANA)) nFontStyleIdx = 1; else if(!strcmp(pSubItem->strFontName, SUBTITLE_FONT_NAME_GEORGIA)) nFontStyleIdx = 2; else if(!strcmp(pSubItem->strFontName, SUBTITLE_FONT_NAME_ARIAL)) nFontStyleIdx = 3; else if(!strcmp(pSubItem->strFontName, SUBTITLE_FONT_NAME_TIMES_NEW_ROMAN)) nFontStyleIdx = 4; else nFontStyleIdx = -1; //* In the absence of any bits set in flags, the text //* is plain. Otherwise, 1: bold, 2: italic, 4: underline if(pSubItem->bBold) nSubStyleFlag |= 1<<0; if(pSubItem->bItalic) nSubStyleFlag |= 1<<1; if(pSubItem->bUnderlined) nSubStyleFlag |= 1<<2; parcelDst->writeInt32(KEY_STRUCT_STYLE_LIST); parcelDst->writeInt32(KEY_FONT_ID); parcelDst->writeInt32(nFontStyleIdx); parcelDst->writeInt32(KEY_FONT_SIZE); parcelDst->writeInt32((int)pSubItem->nFontSize); parcelDst->writeInt32(KEY_TEXT_COLOR_RGBA); parcelDst->writeInt32((int)pSubItem->nPrimaryColor); parcelDst->writeInt32(KEY_STYLE_FLAGS); parcelDst->writeInt32(nSubStyleFlag); } return 0; } // Store the timing and text sample in a Parcel. // The Parcel will be sent to MediaPlayer.java through event, and will be // parsed in TimedText.java. int SubtitleUtilsFillBitmapSubtitleToParcel(Parcel* parcelDst, SubtitleItem* pSubItem, int nSubtitleID) { int nBufSize; parcelDst->writeInt32(KEY_LOCAL_SETTING); parcelDst->writeInt32(KEY_START_TIME); //* pts in unit of us, write out pts in unit of ms. parcelDst->writeInt32((int)(pSubItem->nPts/1000)); parcelDst->writeInt32(KEY_STRUCT_AWEXTEND_BMP); //* write the pixel format of bmp subtitle parcelDst->writeInt32(KEY_STRUCT_AWEXTEND_PIXEL_FORMAT); #if 0 parcelDst->writeInt32(pSubItem->ePixelFormat); //* 0: ARGB, 1: YUV, defined in sdecoder.h //* but currently only support ARGB. #else //* application only support PIXEL_FORMAT_RGBA_8888 pixel format, this value will be ignored //* at frameworks/base/media/java/android/media/TimeText.java::parseParcel(). //* so we write zero here. if(pSubItem->ePixelFormat != SUBTITLE_PIXEL_FORMAT_ARGB) { loge("subtitle pixel format is not ARGB, can not handle."); abort(); } parcelDst->writeInt32(0); #endif //* write the width of bmp subtitle parcelDst->writeInt32(KEY_STRUCT_AWEXTEND_PICWIDTH); parcelDst->writeInt32(pSubItem->nBitmapWidth); //* write the height of bmp subtitle parcelDst->writeInt32(KEY_STRUCT_AWEXTEND_PICHEIGHT); parcelDst->writeInt32(pSubItem->nBitmapHeight); //* write the reference video width of bmp subtitle parcelDst->writeInt32(KEY_STRUCT_AWEXTEND_REFERENCE_VIDEO_WIDTH); parcelDst->writeInt32(pSubItem->nReferenceVideoWidth); //* write the reference video height of bmp subtitle parcelDst->writeInt32(KEY_STRUCT_AWEXTEND_REFERENCE_VIDEO_HEIGHT); parcelDst->writeInt32(pSubItem->nReferenceVideoHeight); //* write the size of the text sample, we only process PIXEL_FORMAT_RGBA_8888 format currently. nBufSize = pSubItem->nBitmapWidth*pSubItem->nBitmapHeight*4; parcelDst->writeInt32(nBufSize); //* write the argb buffer as an int32_t array parcelDst->writeInt32(nBufSize/4); parcelDst->write(pSubItem->pBitmapData, nBufSize); //* set subtitleID parcelDst->writeInt32(KEY_SUBTITLE_ID); parcelDst->writeInt32(nSubtitleID); return 0; } };