1/*
2 * Copyright (C) 2013 Cable Television Laboratories, Inc.
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 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK)
29
30#include "TrackPrivateBaseGStreamer.h"
31
32#include "GStreamerCommon.h"
33#include "Logging.h"
34#include "TrackPrivateBase.h"
35#include <glib-object.h>
36#include <gst/gst.h>
37#include <gst/tag/tag.h>
38#include <wtf/glib/GUniquePtr.h>
39#include <wtf/text/CString.h>
40
41GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug);
42#define GST_CAT_DEFAULT webkit_media_player_debug
43
44namespace WebCore {
45
46TrackPrivateBaseGStreamer::TrackPrivateBaseGStreamer(TrackPrivateBase* owner, gint index, GRefPtr<GstPad> pad)
47 : m_notifier(MainThreadNotifier<MainThreadNotification>::create())
48 , m_index(index)
49 , m_pad(pad)
50 , m_owner(owner)
51{
52 ASSERT(m_pad);
53
54 g_signal_connect_swapped(m_pad.get(), "notify::active", G_CALLBACK(activeChangedCallback), this);
55 g_signal_connect_swapped(m_pad.get(), "notify::tags", G_CALLBACK(tagsChangedCallback), this);
56
57 // We can't call notifyTrackOfTagsChanged() directly, because we need tagsChanged() to setup m_tags.
58 tagsChanged();
59}
60
61#if GST_CHECK_VERSION(1, 10, 0)
62TrackPrivateBaseGStreamer::TrackPrivateBaseGStreamer(TrackPrivateBase* owner, gint index, GRefPtr<GstStream> stream)
63 : m_notifier(MainThreadNotifier<MainThreadNotification>::create())
64 , m_index(index)
65 , m_stream(stream)
66 , m_owner(owner)
67{
68 ASSERT(m_stream);
69
70 // We can't call notifyTrackOfTagsChanged() directly, because we need tagsChanged() to setup m_tags.
71 tagsChanged();
72}
73#endif
74
75TrackPrivateBaseGStreamer::~TrackPrivateBaseGStreamer()
76{
77 disconnect();
78 m_notifier->invalidate();
79}
80
81void TrackPrivateBaseGStreamer::disconnect()
82{
83 m_tags.clear();
84
85#if GST_CHECK_VERSION(1, 10, 0)
86 if (m_stream)
87 m_stream.clear();
88#endif
89
90 m_notifier->cancelPendingNotifications();
91
92 if (!m_pad)
93 return;
94
95 g_signal_handlers_disconnect_matched(m_pad.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
96 m_pad.clear();
97}
98
99void TrackPrivateBaseGStreamer::activeChangedCallback(TrackPrivateBaseGStreamer* track)
100{
101 track->m_notifier->notify(MainThreadNotification::ActiveChanged, [track] { track->notifyTrackOfActiveChanged(); });
102}
103
104void TrackPrivateBaseGStreamer::tagsChangedCallback(TrackPrivateBaseGStreamer* track)
105{
106 track->tagsChanged();
107}
108
109void TrackPrivateBaseGStreamer::tagsChanged()
110{
111 GRefPtr<GstTagList> tags;
112 if (m_pad) {
113 if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_pad.get()), "tags"))
114 g_object_get(m_pad.get(), "tags", &tags.outPtr(), nullptr);
115 else
116 tags = adoptGRef(gst_tag_list_new_empty());
117 }
118#if GST_CHECK_VERSION(1, 10, 0)
119 else if (m_stream)
120 tags = adoptGRef(gst_stream_get_tags(m_stream.get()));
121#endif
122 else
123 tags = adoptGRef(gst_tag_list_new_empty());
124
125 GST_DEBUG("Inspecting track at index %d with tags: %" GST_PTR_FORMAT, m_index, tags.get());
126 {
127 LockHolder lock(m_tagMutex);
128 m_tags.swap(tags);
129 }
130
131 m_notifier->notify(MainThreadNotification::TagsChanged, [this] { notifyTrackOfTagsChanged(); });
132}
133
134void TrackPrivateBaseGStreamer::notifyTrackOfActiveChanged()
135{
136 if (!m_pad)
137 return;
138
139 gboolean active = false;
140 if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_pad.get()), "active"))
141 g_object_get(m_pad.get(), "active", &active, nullptr);
142
143 setActive(active);
144}
145
146bool TrackPrivateBaseGStreamer::getLanguageCode(GstTagList* tags, AtomicString& value)
147{
148 String language;
149 if (getTag(tags, GST_TAG_LANGUAGE_CODE, language)) {
150 language = gst_tag_get_language_code_iso_639_1(language.utf8().data());
151 GST_DEBUG("Converted track %d's language code to %s.", m_index, language.utf8().data());
152 if (language != value) {
153 value = language;
154 return true;
155 }
156 }
157 return false;
158}
159
160template<class StringType>
161bool TrackPrivateBaseGStreamer::getTag(GstTagList* tags, const gchar* tagName, StringType& value)
162{
163 GUniqueOutPtr<gchar> tagValue;
164 if (gst_tag_list_get_string(tags, tagName, &tagValue.outPtr())) {
165 GST_DEBUG("Track %d got %s %s.", m_index, tagName, tagValue.get());
166 value = tagValue.get();
167 return true;
168 }
169 return false;
170}
171
172void TrackPrivateBaseGStreamer::notifyTrackOfTagsChanged()
173{
174 TrackPrivateBaseClient* client = m_owner->client();
175
176 GRefPtr<GstTagList> tags;
177 {
178 LockHolder lock(m_tagMutex);
179 tags.swap(m_tags);
180 }
181
182 if (!tags)
183 return;
184
185 if (getTag(tags.get(), GST_TAG_TITLE, m_label) && client)
186 client->labelChanged(m_label);
187
188 AtomicString language;
189 if (!getLanguageCode(tags.get(), language))
190 return;
191
192 if (language == m_language)
193 return;
194
195 m_language = language;
196 if (client)
197 client->languageChanged(m_language);
198}
199
200} // namespace WebCore
201
202#endif // ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK)
203