[Mac] process raw VTT in-band captions
[WebKit-https.git] / Source / WebCore / html / track / InbandGenericTextTrack.cpp
1 /*
2  * Copyright (C) 2012-2014 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if ENABLE(VIDEO_TRACK)
29
30 #include "InbandGenericTextTrack.h"
31
32 #include "ExceptionCodePlaceholder.h"
33 #include "HTMLMediaElement.h"
34 #include "InbandTextTrackPrivate.h"
35 #include "Logging.h"
36 #include <math.h>
37 #include <wtf/text/CString.h>
38
39 #if ENABLE(WEBVTT_REGIONS)
40 #include "VTTRegionList.h"
41 #endif
42
43 namespace WebCore {
44
45 GenericTextTrackCueMap::GenericTextTrackCueMap()
46 {
47 }
48
49 GenericTextTrackCueMap::~GenericTextTrackCueMap()
50 {
51 }
52
53 void GenericTextTrackCueMap::add(GenericCueData* cueData, TextTrackCueGeneric* cue)
54 {
55     m_dataToCueMap.add(cueData, cue);
56     m_cueToDataMap.add(cue, cueData);
57 }
58
59 PassRefPtr<TextTrackCueGeneric> GenericTextTrackCueMap::find(GenericCueData* cueData)
60 {
61     CueDataToCueMap::iterator iter = m_dataToCueMap.find(cueData);
62     if (iter == m_dataToCueMap.end())
63         return 0;
64
65     return iter->value;
66 }
67
68 PassRefPtr<GenericCueData> GenericTextTrackCueMap::find(TextTrackCue* cue)
69 {
70     CueToDataMap::iterator iter = m_cueToDataMap.find(cue);
71     if (iter == m_cueToDataMap.end())
72         return 0;
73
74     return iter->value;
75 }
76
77 void GenericTextTrackCueMap::remove(GenericCueData* cueData)
78 {
79     RefPtr<TextTrackCueGeneric> cue = find(cueData);
80
81     if (cue)
82         m_cueToDataMap.remove(cue);
83     m_dataToCueMap.remove(cueData);
84 }
85
86 void GenericTextTrackCueMap::remove(TextTrackCue* cue)
87 {
88     RefPtr<GenericCueData> genericData = find(cue);
89     if (genericData) {
90         m_dataToCueMap.remove(genericData);
91         m_cueToDataMap.remove(cue);
92     }
93 }
94
95 PassRefPtr<InbandGenericTextTrack> InbandGenericTextTrack::create(ScriptExecutionContext* context, TextTrackClient* client, PassRefPtr<InbandTextTrackPrivate> playerPrivate)
96 {
97     return adoptRef(new InbandGenericTextTrack(context, client, playerPrivate));
98 }
99
100 InbandGenericTextTrack::InbandGenericTextTrack(ScriptExecutionContext* context, TextTrackClient* client, PassRefPtr<InbandTextTrackPrivate> trackPrivate)
101     : InbandTextTrack(context, client, trackPrivate)
102 {
103 }
104
105 InbandGenericTextTrack::~InbandGenericTextTrack()
106 {
107 }
108
109 void InbandGenericTextTrack::updateCueFromCueData(TextTrackCueGeneric* cue, GenericCueData* cueData)
110 {
111     cue->willChange();
112
113     cue->setStartTime(cueData->startTime(), IGNORE_EXCEPTION);
114     double endTime = cueData->endTime();
115     if (std::isinf(endTime) && mediaElement())
116         endTime = mediaElement()->duration();
117     cue->setEndTime(endTime, IGNORE_EXCEPTION);
118     cue->setText(cueData->content());
119     cue->setId(cueData->id());
120     cue->setBaseFontSizeRelativeToVideoHeight(cueData->baseFontSize());
121     cue->setFontSizeMultiplier(cueData->relativeFontSize());
122     cue->setFontName(cueData->fontName());
123
124     if (cueData->position() > 0)
125         cue->setPosition(lround(cueData->position()), IGNORE_EXCEPTION);
126     if (cueData->line() > 0)
127         cue->setLine(lround(cueData->line()), IGNORE_EXCEPTION);
128     if (cueData->size() > 0)
129         cue->setSize(lround(cueData->size()), IGNORE_EXCEPTION);
130     if (cueData->backgroundColor().isValid())
131         cue->setBackgroundColor(cueData->backgroundColor().rgb());
132     if (cueData->foregroundColor().isValid())
133         cue->setForegroundColor(cueData->foregroundColor().rgb());
134     if (cueData->highlightColor().isValid())
135         cue->setHighlightColor(cueData->highlightColor().rgb());
136
137     if (cueData->align() == GenericCueData::Start)
138         cue->setAlign(ASCIILiteral("start"), IGNORE_EXCEPTION);
139     else if (cueData->align() == GenericCueData::Middle)
140         cue->setAlign(ASCIILiteral("middle"), IGNORE_EXCEPTION);
141     else if (cueData->align() == GenericCueData::End)
142         cue->setAlign(ASCIILiteral("end"), IGNORE_EXCEPTION);
143     cue->setSnapToLines(false);
144
145     cue->didChange();
146 }
147
148 void InbandGenericTextTrack::addGenericCue(InbandTextTrackPrivate* trackPrivate, PassRefPtr<GenericCueData> prpCueData)
149 {
150     ASSERT_UNUSED(trackPrivate, trackPrivate == m_private);
151
152     RefPtr<GenericCueData> cueData = prpCueData;
153     if (m_cueMap.find(cueData.get()))
154         return;
155
156     RefPtr<TextTrackCueGeneric> cue = TextTrackCueGeneric::create(*scriptExecutionContext(), cueData->startTime(), cueData->endTime(), cueData->content());
157     updateCueFromCueData(cue.get(), cueData.get());
158     if (hasCue(cue.get(), TextTrackCue::IgnoreDuration)) {
159         LOG(Media, "InbandGenericTextTrack::addGenericCue ignoring already added cue: start=%.2f, end=%.2f, content=\"%s\"\n", cueData->startTime(), cueData->endTime(), cueData->content().utf8().data());
160         return;
161     }
162
163     if (cueData->status() != GenericCueData::Complete)
164         m_cueMap.add(cueData.get(), cue.get());
165
166     addCue(cue, ASSERT_NO_EXCEPTION);
167 }
168
169 void InbandGenericTextTrack::updateGenericCue(InbandTextTrackPrivate*, GenericCueData* cueData)
170 {
171     RefPtr<TextTrackCueGeneric> cue = m_cueMap.find(cueData);
172     if (!cue)
173         return;
174
175     updateCueFromCueData(cue.get(), cueData);
176
177     if (cueData->status() == GenericCueData::Complete)
178         m_cueMap.remove(cueData);
179 }
180
181 void InbandGenericTextTrack::removeGenericCue(InbandTextTrackPrivate*, GenericCueData* cueData)
182 {
183     RefPtr<TextTrackCueGeneric> cue = m_cueMap.find(cueData);
184     if (cue) {
185         LOG(Media, "InbandGenericTextTrack::removeGenericCue removing cue: start=%.2f, end=%.2f, content=\"%s\"\n", cueData->startTime(), cueData->endTime(), cueData->content().utf8().data());
186         removeCue(cue.get(), IGNORE_EXCEPTION);
187     } else
188         m_cueMap.remove(cueData);
189 }
190
191 void InbandGenericTextTrack::removeCue(TextTrackCue* cue, ExceptionCode& ec)
192 {
193     m_cueMap.remove(cue);
194     TextTrack::removeCue(cue, ec);
195 }
196
197 WebVTTParser& InbandGenericTextTrack::parser()
198 {
199     if (!m_webVTTParser)
200         m_webVTTParser = std::make_unique<WebVTTParser>(static_cast<WebVTTParserClient*>(this), scriptExecutionContext());
201     return *m_webVTTParser;
202 }
203
204 void InbandGenericTextTrack::parseWebVTTCueData(InbandTextTrackPrivate* trackPrivate, const ISOWebVTTCue& cueData)
205 {
206     ASSERT_UNUSED(trackPrivate, trackPrivate == m_private);
207     parser().parseCueData(cueData);
208 }
209
210 void InbandGenericTextTrack::parseWebVTTFileHeader(InbandTextTrackPrivate* trackPrivate, String header)
211 {
212     ASSERT_UNUSED(trackPrivate, trackPrivate == m_private);
213     parser().parseFileHeader(header);
214 }
215
216 void InbandGenericTextTrack::newCuesParsed()
217 {
218     Vector<RefPtr<WebVTTCueData>> cues;
219     parser().getNewCues(cues);
220
221     for (auto& cueData : cues) {
222         RefPtr<VTTCue> vttCue = VTTCue::create(*scriptExecutionContext(), *cueData);
223
224         if (hasCue(vttCue.get(), TextTrackCue::IgnoreDuration)) {
225             LOG(Media, "InbandGenericTextTrack::newCuesParsed ignoring already added cue: start=%.2f, end=%.2f, content=\"%s\"\n", vttCue->startTime(), vttCue->endTime(), vttCue->text().utf8().data());
226             return;
227         }
228         addCue(vttCue.release(), ASSERT_NO_EXCEPTION);
229     }
230 }
231
232 #if ENABLE(WEBVTT_REGIONS)
233 void InbandGenericTextTrack::newRegionsParsed()
234 {
235     Vector<RefPtr<VTTRegion>> newRegions;
236     parser().getNewRegions(newRegions);
237
238     for (auto& region : newRegions) {
239         region->setTrack(this);
240         regions()->add(region);
241     }
242 }
243 #endif
244
245 void InbandGenericTextTrack::fileFailedToParse()
246 {
247     LOG(Media, "Error parsing WebVTT stream.");
248 }
249
250 } // namespace WebCore
251
252 #endif