| 1037 | |
| 1038 | /* Mostly copied from avformatdecoder */ |
| 1039 | static void myth_libass_log(int level, const char *fmt, va_list vl, void *ctx) |
| 1040 | { |
| 1041 | static QString full_line("LibASS:"); |
| 1042 | static const int msg_len = 255; |
| 1043 | static QMutex string_lock; |
| 1044 | uint verbose_level = 0; |
| 1045 | |
| 1046 | switch (level) |
| 1047 | { |
| 1048 | case 0: //MSGL_FATAL |
| 1049 | verbose_level = VB_IMPORTANT; |
| 1050 | break; |
| 1051 | case 1: //MSGL_ERR |
| 1052 | case 2: //MSGL_WARN |
| 1053 | case 4: //MSGL_INFO |
| 1054 | verbose_level = VB_GENERAL; |
| 1055 | break; |
| 1056 | case 6: //MSGL_V |
| 1057 | case 7: //MSGL_DBG2 |
| 1058 | default: |
| 1059 | return; |
| 1060 | } |
| 1061 | |
| 1062 | if (!VERBOSE_LEVEL_CHECK(verbose_level)) |
| 1063 | return; |
| 1064 | |
| 1065 | string_lock.lock(); |
| 1066 | |
| 1067 | char str[msg_len+1]; |
| 1068 | int bytes = vsnprintf(str, msg_len+1, fmt, vl); |
| 1069 | // check for truncated messages and fix them |
| 1070 | if (bytes > msg_len) |
| 1071 | { |
| 1072 | VERBOSE(VB_IMPORTANT, QString("libASS log output truncated %1 of %2 bytes written") |
| 1073 | .arg(msg_len).arg(bytes)); |
| 1074 | str[msg_len-1] = '\n'; |
| 1075 | } |
| 1076 | |
| 1077 | full_line += QString(str); |
| 1078 | if (full_line.endsWith("\n")) |
| 1079 | { |
| 1080 | full_line.truncate(full_line.length() - 1); |
| 1081 | VERBOSE(verbose_level, full_line); |
| 1082 | full_line.truncate(0); |
| 1083 | } |
| 1084 | string_lock.unlock(); |
| 1085 | } |
| 1086 | |
| 1087 | void SubtitleScreen::InitialiseAssLibrary(void) |
| 1088 | { |
| 1089 | m_assLibrary = ass_library_init(); |
| 1090 | ass_set_message_cb(m_assLibrary, myth_libass_log, NULL); |
| 1091 | ass_set_extract_fonts(m_assLibrary, true); |
| 1092 | //add embedded fonts |
| 1093 | for ( uint i = 0; i < m_player->GetDecoder()->GetTrackCount(kTrackTypeAttachment); ++i) |
| 1094 | { |
| 1095 | QByteArray filename; |
| 1096 | QByteArray font; |
| 1097 | |
| 1098 | m_player->GetDecoder()->GetAttachmentData(i, filename, font); |
| 1099 | ass_add_font(m_assLibrary, filename.data(), font.data(), font.size()); |
| 1100 | } |
| 1101 | |
| 1102 | m_assRenderer = ass_renderer_init(m_assLibrary); |
| 1103 | if (!m_assRenderer) |
| 1104 | return; |
| 1105 | |
| 1106 | ass_set_fonts(m_assRenderer, NULL, "sans-serif", 1, NULL, 1); |
| 1107 | ass_set_hinting(m_assRenderer, ASS_HINTING_LIGHT); |
| 1108 | VERBOSE(VB_PLAYBACK | VB_EXTRA, LOC + QString("libASS library Init")); |
| 1109 | } |
| 1110 | |
| 1111 | void SubtitleScreen::CleanupAssLibrary(void) |
| 1112 | { |
| 1113 | CleanupAssTrack(); |
| 1114 | if (m_assRenderer) |
| 1115 | ass_renderer_done(m_assRenderer); |
| 1116 | m_assRenderer = NULL; |
| 1117 | if (m_assLibrary) |
| 1118 | { |
| 1119 | ass_clear_fonts(m_assLibrary); |
| 1120 | ass_library_done(m_assLibrary); |
| 1121 | } |
| 1122 | m_assLibrary = NULL; |
| 1123 | VERBOSE(VB_PLAYBACK | VB_EXTRA, LOC + QString("libASS library cleanup")); |
| 1124 | } |
| 1125 | |
| 1126 | void SubtitleScreen::InitialiseAssTrack(int tracknum) |
| 1127 | { |
| 1128 | if (tracknum == m_asstracknum && m_assTrack) |
| 1129 | return; |
| 1130 | |
| 1131 | CleanupAssTrack(); |
| 1132 | m_assTrack = ass_new_track(m_assLibrary); |
| 1133 | m_asstracknum = tracknum; |
| 1134 | |
| 1135 | QByteArray header = m_player->GetDecoder()->GetSubHeader(tracknum); |
| 1136 | if (!header.isNull()) |
| 1137 | { |
| 1138 | ass_process_codec_private(m_assTrack, header.data(), header.size()); |
| 1139 | } |
| 1140 | |
| 1141 | float tmp = 0.0; |
| 1142 | QRect dummy; |
| 1143 | m_player->getVideoOutput()->GetOSDBounds(dummy, m_assScreenRect, tmp, tmp, tmp); |
| 1144 | ResizeASSRenderer(m_assScreenRect); |
| 1145 | VERBOSE(VB_PLAYBACK | VB_EXTRA, LOC + QString("libASS Track Init(%1)").arg(m_asstracknum)); |
| 1146 | } |
| 1147 | |
| 1148 | void SubtitleScreen::CleanupAssTrack(void) |
| 1149 | { |
| 1150 | if (m_assTrack) |
| 1151 | ass_free_track(m_assTrack); |
| 1152 | m_assTrack = NULL; |
| 1153 | VERBOSE(VB_PLAYBACK | VB_EXTRA, LOC + QString("libASS Track cleanup")); |
| 1154 | } |
| 1155 | |
| 1156 | void SubtitleScreen::AddAssEvent(char *event) |
| 1157 | { |
| 1158 | if (m_assTrack) |
| 1159 | { |
| 1160 | ass_process_data(m_assTrack, event, strlen(event)); |
| 1161 | VERBOSE(VB_PLAYBACK | VB_EXTRA, LOC + QString("libASS event added to track(%i): %1").arg(m_asstracknum).arg(event)); |
| 1162 | } |
| 1163 | } |
| 1164 | |
| 1165 | void SubtitleScreen::ResizeASSRenderer(const QRect &Screen) |
| 1166 | { |
| 1167 | //int mt, mb, ml, mr; |
| 1168 | ass_set_frame_size(m_assRenderer, Screen.width(), Screen.height()); |
| 1169 | ass_set_margins(m_assRenderer, 0, 0, 0, 0); |
| 1170 | ass_set_use_margins(m_assRenderer, true); |
| 1171 | ass_set_font_scale(m_assRenderer, 1.0); |
| 1172 | } |
| 1173 | |
| 1174 | void SubtitleScreen::RenderASSTrack(uint64_t timecode) |
| 1175 | { |
| 1176 | VideoOutput *vo = m_player->getVideoOutput(); |
| 1177 | ASS_Image *images = NULL; |
| 1178 | int count = 0; |
| 1179 | int changed = 0; |
| 1180 | |
| 1181 | if (!vo || !m_assRenderer || !m_assTrack) |
| 1182 | return; |
| 1183 | |
| 1184 | QRect oldscreen = m_assScreenRect; |
| 1185 | QRect dummy; |
| 1186 | float tmp = 0.0; |
| 1187 | vo->GetOSDBounds(dummy, m_assScreenRect, tmp, tmp, tmp); |
| 1188 | if (oldscreen != m_assScreenRect) |
| 1189 | ResizeASSRenderer(m_assScreenRect); |
| 1190 | |
| 1191 | images = ass_render_frame(m_assRenderer, m_assTrack, timecode, &changed); |
| 1192 | if (changed) |
| 1193 | { |
| 1194 | MythPainter *osd_painter = vo->GetOSDPainter(); |
| 1195 | if (!osd_painter) |
| 1196 | return; |
| 1197 | DeleteAllChildren(); |
| 1198 | SetRedraw(); |
| 1199 | while (images) |
| 1200 | { |
| 1201 | //if width or height = 0 then dont display this image |
| 1202 | if (images->w == 0 || images->h == 0) |
| 1203 | { |
| 1204 | images = images->next; |
| 1205 | continue; |
| 1206 | } |
| 1207 | |
| 1208 | uint8_t alpha = images->color & 0xFF; |
| 1209 | uint8_t blue = images->color >> 8 & 0xFF; |
| 1210 | uint8_t green = images->color >> 16 & 0xFF; |
| 1211 | uint8_t red = images->color >> 24 & 0xFF; |
| 1212 | |
| 1213 | //image is fully transparent dont need to render |
| 1214 | if (alpha == 255) |
| 1215 | { |
| 1216 | images = images->next; |
| 1217 | continue; |
| 1218 | } |
| 1219 | |
| 1220 | QSize ImageSize(images->w, images->h); |
| 1221 | QRect DisplayRect(images->dst_x, images->dst_y, images->w, images->h); |
| 1222 | |
| 1223 | //create the image using Libass image data |
| 1224 | QImage qImage(ImageSize, QImage::Format_ARGB32); |
| 1225 | qImage.fill(0x00000000); |
| 1226 | |
| 1227 | unsigned char *src = images->bitmap; |
| 1228 | for (int y = 0; y < images->h; ++y) |
| 1229 | { |
| 1230 | for (int x = 0; x < images->w; ++x) |
| 1231 | { |
| 1232 | uint8_t pixelvalue = src[x]; |
| 1233 | if (pixelvalue) |
| 1234 | { |
| 1235 | uint8_t pixelalpha = pixelvalue * (255 - alpha) / 255; |
| 1236 | uint32_t pixel = (pixelalpha << 24) | (red << 16) | (green << 8) | (blue); |
| 1237 | qImage.setPixel(x, y, pixel); |
| 1238 | } |
| 1239 | } |
| 1240 | src += images->stride; |
| 1241 | } |
| 1242 | |
| 1243 | //add pictures to the OSD |
| 1244 | MythImage* image = NULL; |
| 1245 | MythUIImage *uiimage = NULL; |
| 1246 | |
| 1247 | if (osd_painter) |
| 1248 | image = osd_painter->GetFormatImage(); |
| 1249 | |
| 1250 | if (image) |
| 1251 | { |
| 1252 | image->Assign(qImage); |
| 1253 | QString name = QString("asssub%1").arg(count); |
| 1254 | uiimage = new MythUIImage(this, name); |
| 1255 | if (uiimage) |
| 1256 | { |
| 1257 | m_refreshArea = true; |
| 1258 | uiimage->SetImage(image); |
| 1259 | uiimage->SetArea(MythRect(DisplayRect)); |
| 1260 | } |
| 1261 | } |
| 1262 | images = images->next; |
| 1263 | count++; |
| 1264 | } |
| 1265 | VERBOSE(VB_PLAYBACK | VB_EXTRA, LOC + QString("libASS Rendered %1 Images").arg(count)); |
| 1266 | } |
| 1267 | } |