void InbandTextTrackPrivateAVF::processCue(CFArrayRef attributedStrings, double time) { if (!m_player) return; if (m_havePartialCue) { // Cues do not have an explicit duration, they are displayed until the next "cue" (which might be empty) is emitted. m_currentCueEndTime = time; LOG(Media, "InbandTextTrackPrivateAVF::processCue flushing cue: start=%.2f, end=%.2f, settings=\"%s\", content=\"%s\" \n", m_currentCueStartTime, m_currentCueEndTime, m_currentCueSettings.toString().utf8().data(), m_currentCueContent.toString().utf8().data()); m_player->flushCurrentCue(this); resetCueValues(); } CFIndex count = CFArrayGetCount(attributedStrings); for (CFIndex i = 0; i < count; i++) { CFAttributedStringRef attributedString = static_cast<CFAttributedStringRef>(CFArrayGetValueAtIndex(attributedStrings, i)); if (!attributedString || !CFAttributedStringGetLength(attributedString)) continue; processCueAttributes(attributedString, m_currentCueContent, m_currentCueSettings); m_currentCueStartTime = time; m_havePartialCue = true; } }
void InbandTextTrackPrivateAVF::processCue(CFArrayRef attributedStrings, double time) { if (!client()) return; if (m_havePartialCue) { // Cues do not have an explicit duration, they are displayed until the next "cue" (which might be empty) is emitted. m_currentCueEndTime = time; if (m_currentCueEndTime >= m_currentCueStartTime) { for (size_t i = 0; i < m_cues.size(); i++) { GenericCueData* cueData = m_cues[i].get(); LOG(Media, "InbandTextTrackPrivateAVF::processCue flushing cue: start=%.2f, end=%.2f, content=\"%s\" \n", m_currentCueStartTime, m_currentCueEndTime, cueData->content().utf8().data()); if (!cueData->content().length()) continue; cueData->setStartTime(m_currentCueStartTime); cueData->setEndTime(m_currentCueEndTime); // AVFoundation cue "position" is to the center of the text so adjust relative to the edge because we will use it to // set CSS "left". if (cueData->position() >= 0 && cueData->size() > 0) cueData->setPosition(cueData->position() - cueData->size() / 2); LOG(Media, "InbandTextTrackPrivateAVF::processCue(%p) - adding cue for time = %.2f, position = %.2f, line = %.2f", this, cueData->startTime(), cueData->position(), cueData->line()); client()->addGenericCue(this, cueData); } } else LOG(Media, "InbandTextTrackPrivateAVF::processCue negative length cue(s) ignored: start=%.2f, end=%.2f\n", m_currentCueStartTime, m_currentCueEndTime); resetCueValues(); } if (!attributedStrings) return; CFIndex count = CFArrayGetCount(attributedStrings); if (!count) return; for (CFIndex i = 0; i < count; i++) { CFAttributedStringRef attributedString = static_cast<CFAttributedStringRef>(CFArrayGetValueAtIndex(attributedStrings, i)); if (!attributedString || !CFAttributedStringGetLength(attributedString)) continue; m_cues.append(adoptPtr(new GenericCueData)); processCueAttributes(attributedString, m_cues[i].get()); m_currentCueStartTime = time; m_havePartialCue = true; } }
void InbandTextTrackPrivateAVF::processCue(CFArrayRef attributedStrings, double time) { if (!client()) return; LOG(Media, "InbandTextTrackPrivateAVF::processCue - %li cues at time %.2f\n", attributedStrings ? CFArrayGetCount(attributedStrings) : 0, time); if (m_pendingCueStatus != None) { // Cues do not have an explicit duration, they are displayed until the next "cue" (which might be empty) is emitted. m_currentCueEndTime = time; if (m_currentCueEndTime >= m_currentCueStartTime) { for (size_t i = 0; i < m_cues.size(); i++) { GenericCueData* cueData = m_cues[i].get(); if (m_pendingCueStatus == Valid) { cueData->setEndTime(m_currentCueEndTime); cueData->setStatus(GenericCueData::Complete); LOG(Media, "InbandTextTrackPrivateAVF::processCue(%p) - updating cue: start=%.2f, end=%.2f, content=\"%s\"", this, cueData->startTime(), m_currentCueEndTime, cueData->content().utf8().data()); client()->updateGenericCue(this, cueData); } else { // We have to assume that the implicit duration is invalid for cues delivered during a seek because the AVF decode pipeline may not // see every cue, so DO NOT update cue duration while seeking. LOG(Media, "InbandTextTrackPrivateAVF::processCue(%p) - ignoring cue delivered during seek: start=%.2f, end=%.2f, content=\"%s\"", this, cueData->startTime(), m_currentCueEndTime, cueData->content().utf8().data()); } } } else LOG(Media, "InbandTextTrackPrivateAVF::processCue negative length cue(s) ignored: start=%.2f, end=%.2f\n", m_currentCueStartTime, m_currentCueEndTime); resetCueValues(); } if (!attributedStrings) return; CFIndex count = CFArrayGetCount(attributedStrings); if (!count) return; for (CFIndex i = 0; i < count; i++) { CFAttributedStringRef attributedString = static_cast<CFAttributedStringRef>(CFArrayGetValueAtIndex(attributedStrings, i)); if (!attributedString || !CFAttributedStringGetLength(attributedString)) continue; RefPtr<GenericCueData> cueData = GenericCueData::create(); processCueAttributes(attributedString, cueData.get()); if (!cueData->content().length()) continue; m_cues.append(cueData); m_currentCueStartTime = time; cueData->setStartTime(m_currentCueStartTime); cueData->setEndTime(numeric_limits<double>::infinity()); // AVFoundation cue "position" is to the center of the text so adjust relative to the edge because we will use it to // set CSS "left". if (cueData->position() >= 0 && cueData->size() > 0) cueData->setPosition(cueData->position() - cueData->size() / 2); LOG(Media, "InbandTextTrackPrivateAVF::processCue(%p) - adding cue for time = %.2f, position = %.2f, line = %.2f", this, cueData->startTime(), cueData->position(), cueData->line()); cueData->setStatus(GenericCueData::Partial); client()->addGenericCue(this, cueData.release()); m_pendingCueStatus = seeking() ? DeliveredDuringSeek : Valid; } }
OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize) { CGFloat minCartDrawSize = 64; CGFloat size = fmin(maxSize.width, maxSize.height); CGContextRef qlContext = NULL; if (size < minCartDrawSize) { size = 32; qlContext = QLThumbnailRequestCreateContext(thumbnail, CGSizeMake(size,size), true, NULL); } else { qlContext = QLThumbnailRequestCreateContext(thumbnail, CGSizeMake(size,size), false, NULL); } if (qlContext) { NDSHeader header; NDSIcon icon; int pathlen = 2047; UInt8 path[pathlen+1]; if (CFURLGetFileSystemRepresentation(url, true, path, pathlen)) { if (size < minCartDrawSize) { // at smaller sizes we only draw the game icon at native size and let QL do the scaling parseNDSInfo(path, &header, &icon); CGImageRef image = CGImageCreateWithNDSIcon(&icon); if (image) { CGContextDrawImage(qlContext, CGRectMake(0, 0, size, size), image); CGImageRelease(image); } } else { CGContextClearRect(qlContext, CGRectMake(0,0,size,size)); // draw cartridge background CFURLRef bgURL = CFBundleCopyResourceURL(CFBundleGetBundleWithIdentifier( CFSTR("net.mironer.nds.quicklookgenerator")), CFSTR("background"), CFSTR("png"), NULL); if (bgURL) { CGDataProviderRef bgPNG = CGDataProviderCreateWithURL(bgURL); if (bgPNG) { CGImageRef bg = CGImageCreateWithPNGDataProvider(bgPNG, NULL, true, kCGRenderingIntentDefault); if (bg) { CGContextDrawImage(qlContext, CGRectMake(0, 0, size, size), bg); CGImageRelease(bg); } CGDataProviderRelease(bgPNG); } CFRelease(bgURL); } // draw game icon parseNDSInfo(path, &header, &icon); CGImageRef image = CGImageCreateWithNDSIcon(&icon); if (image) { CGContextDrawImage(qlContext, CGRectMake(size / 5, size / 5, size * 3 / 5, size * 3 / 5), image); CGImageRelease(image); } // draw cartridge overlay CFURLRef ovrURL = CFBundleCopyResourceURL( CFBundleGetBundleWithIdentifier( CFSTR("net.mironer.nds.quicklookgenerator")), CFSTR("overlay"), CFSTR("png"), NULL); if (ovrURL) { CGDataProviderRef ovrPNG = CGDataProviderCreateWithURL(ovrURL); if (ovrPNG) { CGImageRef ovr = CGImageCreateWithPNGDataProvider(ovrPNG, NULL, true, kCGRenderingIntentDefault); if (ovr) { CGContextDrawImage(qlContext, CGRectMake(0, 0, size, size), ovr); CGImageRelease(ovr); } CGDataProviderRelease(ovrPNG); } CFRelease(ovrURL); } // draw serial number CFStringRef serial = CFStringCreateWithSerialNumber(&header); CFAttributedStringRef aString = CFAttributedStringCreate(kCFAllocatorDefault, serial, NULL); CFMutableAttributedStringRef attrStr = CFAttributedStringCreateMutableCopy(kCFAllocatorDefault, 128, aString); CFIndex len = CFAttributedStringGetLength(attrStr); CTFontRef font = CTFontCreateWithName(CFSTR("Lucida Sans"), size / 16, NULL); CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, len), kCTFontAttributeName, font); CTTextAlignment rightAlign = kCTRightTextAlignment; CTParagraphStyleSetting styleSettings[] = { {kCTParagraphStyleSpecifierAlignment, sizeof(rightAlign), &rightAlign} }; CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(styleSettings, 1); CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, len), kCTParagraphStyleAttributeName, paragraphStyle); CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrStr); CFRelease(attrStr); CFRelease(aString); CFRelease(serial); CFRelease(font); CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path, NULL, CGRectMake(-size * 1.55 / 8, -size * 6.1 / 8, size, size)); CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); CTFrameDraw(frame, qlContext); CFRelease(frame); CFRelease(framesetter); CFRelease(paragraphStyle); CFRelease(path); } } QLThumbnailRequestFlushContext(thumbnail, qlContext); CFRelease(qlContext); } return noErr; }