Changeset 846 for trunk/src/3rdparty/phonon/gstreamer/mediaobject.cpp
- Timestamp:
- May 5, 2011, 5:36:53 AM (14 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk
- Property svn:mergeinfo changed
/branches/vendor/nokia/qt/4.7.2 (added) merged: 845 /branches/vendor/nokia/qt/current merged: 844 /branches/vendor/nokia/qt/4.6.3 removed
- Property svn:mergeinfo changed
-
trunk/src/3rdparty/phonon/gstreamer/mediaobject.cpp
r561 r846 17 17 #include <cmath> 18 18 #include <gst/interfaces/propertyprobe.h> 19 #include <gst/pbutils/install-plugins.h> 19 20 #include "common.h" 20 21 #include "mediaobject.h" … … 54 55 , m_prefinishMark(0) 55 56 , m_transitionTime(0) 57 , m_isStream(false) 56 58 , m_posAtSeek(-1) 57 59 , m_prefinishMarkReachedNotEmitted(true) … … 80 82 , m_availableTitles(0) 81 83 , m_currentTitle(1) 84 , m_pendingTitle(1) 82 85 { 83 86 qRegisterMetaType<GstCaps*>("GstCaps*"); … … 96 99 connect(m_tickTimer, SIGNAL(timeout()), SLOT(emitTick())); 97 100 } 98 connect(this, SIGNAL(stateChanged(Phonon::State, Phonon::State)),99 this, SLOT(notifyStateChange(Phonon::State, Phonon::State)));101 connect(this, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 102 this, SLOT(notifyStateChange(Phonon::State, Phonon::State))); 100 103 101 104 } … … 137 140 } 138 141 142 void 143 pluginInstallationDone( GstInstallPluginsReturn res, gpointer userData ) 144 { 145 // Nothing inside yet 146 Q_UNUSED(res); 147 Q_UNUSED(userData); 148 } 149 139 150 void MediaObject::saveState() 140 151 { … … 196 207 bool canPlay = (m_hasAudio || m_videoStreamFound); 197 208 Phonon::ErrorType error = canPlay ? Phonon::NormalError : Phonon::FatalError; 209 #ifdef PLUGIN_INSTALL_API 210 GstInstallPluginsContext *ctx = gst_install_plugins_context_new (); 211 gchar *details[2]; 212 details[0] = m_missingCodecs[0].toLocal8Bit().data(); 213 details[1] = NULL; 214 GstInstallPluginsReturn status; 215 216 status = gst_install_plugins_async( details, ctx, pluginInstallationDone, NULL ); 217 gst_install_plugins_context_free ( ctx ); 218 219 if ( status != GST_INSTALL_PLUGINS_STARTED_OK ) 220 { 221 if( status == GST_INSTALL_PLUGINS_HELPER_MISSING ) 222 setError(tr("Missing codec helper script assistant."), Phonon::FatalError ); 223 else 224 setError(tr("Plugin codec installation failed for codec: %0") 225 .arg(m_missingCodecs[0].split("|")[3]), error); 226 } 227 m_missingCodecs.clear(); 228 #else 229 QString codecs = m_missingCodecs.join(", "); 230 198 231 if (error == Phonon::NormalError && m_hasVideo && !m_videoStreamFound) { 199 232 m_hasVideo = false; 200 233 emit hasVideoChanged(false); 201 234 } 202 QString codecs = m_missingCodecs.join(", "); 203 setError(QString(tr("A required codec is missing. You need to install the following codec(s) to play this content: %0")).arg(codecs), error); 235 setError(tr("A required codec is missing. You need to install the following codec(s) to play this content: %0").arg(codecs), error); 204 236 m_missingCodecs.clear(); 237 #endif 205 238 } 206 239 } … … 249 282 250 283 } 251 media->addMissingCodecName(value); 284 285 #ifdef PLUGIN_INSTALL_API 286 QString plugins = QString("gstreamer|0.10|%0|%1|decoder-%2") 287 .arg( qApp->applicationName() ) 288 .arg( value ) 289 .arg( QString::fromUtf8(gst_caps_to_string (caps) ) ); 290 media->addMissingCodecName( plugins ); 291 #else 292 media->addMissingCodecName( value ); 293 #endif 252 294 } 253 295 … … 310 352 // Note that the notify::caps _must_ be installed after linking to work with Dapper 311 353 m_capsHandler = g_signal_connect(pad, "notify::caps", G_CALLBACK(notifyVideoCaps), this); 312 354 313 355 if (!m_loading && !m_hasVideo) { 314 356 m_hasVideo = m_videoStreamFound; … … 369 411 370 412 // Create a new datasource based on the input URL 371 QByteArray encoded_cstr_url = url.toEncoded(); 413 // add the 'file' scheme if it's missing; the double '/' is needed! 414 QByteArray encoded_cstr_url = (url.scheme() == QLatin1String("") ? 415 "file://" + url.toEncoded() : 416 url.toEncoded()); 372 417 m_datasource = gst_element_make_from_uri(GST_URI_SRC, encoded_cstr_url.constData(), (const char*)NULL); 373 418 if (!m_datasource) … … 389 434 m_backend->logMessage(QString("new device speed : 2X"), Backend::Info, this); 390 435 } 436 } 437 438 /* make HTTP sources send extra headers so we get icecast 439 * metadata in case the stream is an icecast stream */ 440 if (encoded_cstr_url.startsWith("http://") 441 && g_object_class_find_property (G_OBJECT_GET_CLASS (m_datasource), "iradio-mode")) { 442 g_object_set (m_datasource, "iradio-mode", TRUE, NULL); 443 m_isStream = true; 391 444 } 392 445 … … 443 496 gst_object_sink (GST_OBJECT (m_pipeline)); 444 497 445 m_decodebin = gst_element_factory_make ("decodebin ", NULL);498 m_decodebin = gst_element_factory_make ("decodebin2", NULL); 446 499 g_signal_connect (m_decodebin, "new-decoded-pad", G_CALLBACK (&cb_newpad), this); 447 500 g_signal_connect (m_decodebin, "unknown-type", G_CALLBACK (&cb_unknown_type), this); … … 647 700 } else if (currentState == GST_STATE_PLAYING) { 648 701 changeState(Phonon::PlayingState); 649 } else if ( !m_atEndOfStream &&gst_element_set_state(m_pipeline, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE) {702 } else if (gst_element_set_state(m_pipeline, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE) { 650 703 m_pendingState = Phonon::PlayingState; 651 704 } else { … … 677 730 678 731 Phonon::State oldState = m_state; 679 m_state = newstate; // m_state must be set before emitting, since 732 m_state = newstate; // m_state must be set before emitting, since 680 733 // Error state requires that state() will return the new value 681 734 m_pendingState = newstate; … … 697 750 case Phonon::StoppedState: 698 751 m_backend->logMessage("phonon state changed: Stopped", Backend::Info, this); 752 // We must reset the pipeline when playing again 753 m_resetNeeded = true; 699 754 m_tickTimer->stop(); 700 755 break; … … 862 917 GstState state; 863 918 gst_element_set_state(m_pipeline, GST_STATE_NULL); 864 gst_element_get_state 919 gst_element_get_state(m_pipeline, &state, NULL, 2000); 865 920 866 921 m_source = source; … … 872 927 changeState(Phonon::LoadingState); 873 928 m_loading = true; 874 m_resetNeeded = false; 929 // IMPORTANT: Honor the m_resetNeeded flag as it currently stands. 930 // See https://qa.mandriva.com/show_bug.cgi?id=56807 931 //m_resetNeeded = false; 875 932 m_resumeState = false; 876 933 m_pendingState = Phonon::StoppedState; … … 885 942 m_aboutToFinishEmitted = false; 886 943 m_error = NoError; 887 m_errorString = QString();888 944 m_errorString.clear(); 945 889 946 m_bufferPercent = 0; 890 947 m_prefinishMarkReachedNotEmitted = true; … … 895 952 m_atEndOfStream = false; 896 953 897 // Clear exising meta tags 954 m_availableTitles = 0; 955 m_pendingTitle = 1; 956 m_currentTitle = 1; 957 958 // Clear existing meta tags 898 959 m_metaData.clear(); 960 m_isStream = false; 899 961 900 962 switch (source.type()) { 901 case MediaSource::Url: { 902 if (createPipefromURL(source.url())) 903 m_loading = true; 904 else 963 case MediaSource::Url: { 964 if (!createPipefromURL(source.url())) 905 965 setError(tr("Could not open media source.")); 906 966 } … … 908 968 909 969 case MediaSource::LocalFile: { 910 if (createPipefromURL(QUrl::fromLocalFile(source.fileName()))) 911 m_loading = true; 912 else 970 if (!createPipefromURL(QUrl::fromLocalFile(source.fileName()))) 913 971 setError(tr("Could not open media source.")); 914 972 } … … 923 981 924 982 case MediaSource::Stream: 925 if (createPipefromStream(source)) 926 m_loading = true; 927 else 983 if (!createPipefromStream(source)) 928 984 setError(tr("Could not open media source.")); 929 985 break; … … 931 987 case MediaSource::Disc: 932 988 { 933 934 935 989 QString mediaUrl; 990 switch (source.discType()) { 991 case Phonon::NoDisc: 936 992 qWarning() << "I should never get to see a MediaSource that is a disc but doesn't specify which one"; 937 993 return; … … 949 1005 return; 950 1006 } 951 if (!mediaUrl.isEmpty() && createPipefromURL(QUrl(mediaUrl))) 952 m_loading = true; 953 else 1007 if (mediaUrl.isEmpty() || !createPipefromURL(QUrl(mediaUrl))) 954 1008 setError(tr("Could not open media source.")); 955 1009 } … … 967 1021 // We need to link this node to ensure that fake sinks are connected 968 1022 // before loading, otherwise the stream will be blocked 969 if (m_loading) 970 link(); 1023 link(); 971 1024 beginLoad(); 972 1025 } … … 1005 1058 } 1006 1059 1007 m_availableTitles = 1;1008 gint64 titleCount;1009 GstFormat format = gst_format_get_by_nick("track");1010 if (gst_element_query_duration (m_pipeline, &format, &titleCount)) {1060 if (m_source.discType() == Phonon::Cd) { 1061 gint64 titleCount; 1062 GstFormat format = gst_format_get_by_nick("track"); 1063 if (gst_element_query_duration (m_pipeline, &format, &titleCount)) { 1011 1064 //check if returned format is still "track", 1012 1065 //gstreamer sometimes returns the total time, if tracks information is not available. 1013 if (qstrcmp(gst_format_get_name(format), "track") == 0) { 1014 int oldAvailableTitles = m_availableTitles; 1015 m_availableTitles = (int)titleCount; 1016 if (m_availableTitles != oldAvailableTitles) { 1017 emit availableTitlesChanged(m_availableTitles); 1018 m_backend->logMessage(QString("Available titles changed: %0").arg(m_availableTitles), Backend::Info, this); 1066 if (qstrcmp(gst_format_get_name(format), "track") == 0) { 1067 int oldAvailableTitles = m_availableTitles; 1068 m_availableTitles = (int)titleCount; 1069 if (m_availableTitles != oldAvailableTitles) { 1070 emit availableTitlesChanged(m_availableTitles); 1071 m_backend->logMessage(QString("Available titles changed: %0").arg(m_availableTitles), Backend::Info, this); 1072 } 1019 1073 } 1020 1074 } 1021 1075 } 1022 1023 1076 } 1024 1077 … … 1078 1131 1079 1132 quint64 current = currentTime(); 1080 quint64 total = totalTime(); 1133 quint64 total = totalTime(); 1081 1134 1082 1135 if (current < total - m_prefinishMark) … … 1099 1152 if (m_tickInterval > 0 && currentTime != m_previousTickTime) { 1100 1153 emit tick(currentTime); 1101 m_previousTickTime = currentTime; 1154 m_previousTickTime = currentTime; 1102 1155 } 1103 1156 if (m_state == Phonon::PlayingState) { … … 1110 1163 // Prepare load of next source 1111 1164 if (currentTime >= totalTime - ABOUT_TO_FINNISH_TIME) { 1112 if (!m_aboutToFinishEmitted) { 1165 if (m_source.type() == MediaSource::Disc && 1166 m_autoplayTitles && 1167 m_availableTitles > 1 && 1168 m_currentTitle < m_availableTitles) { 1169 m_aboutToFinishEmitted = false; 1170 } else if (!m_aboutToFinishEmitted) { 1113 1171 m_aboutToFinishEmitted = true; // track is about to finish 1114 1172 emit aboutToFinish(); … … 1214 1272 switch (GST_MESSAGE_TYPE (gstMessage)) { 1215 1273 1216 case GST_MESSAGE_EOS: 1217 m_backend->logMessage("EOS rec ieved", Backend::Info, this);1274 case GST_MESSAGE_EOS: 1275 m_backend->logMessage("EOS received", Backend::Info, this); 1218 1276 handleEndOfStream(); 1219 1277 break; … … 1223 1281 gst_message_parse_tag(gstMessage, &tag_list); 1224 1282 if (tag_list) { 1283 TagMap newTags; 1284 gst_tag_list_foreach (tag_list, &foreach_tag_function, &newTags); 1285 gst_tag_list_free(tag_list); 1286 1287 // Determine if we should no fake the album/artist tags. 1288 // This is a little confusing as we want to fake it on initial 1289 // connection where title, album and artist are all missing. 1290 // There are however times when we get just other information, 1291 // e.g. codec, and so we want to only do clever stuff if we 1292 // have a commonly available tag (ORGANIZATION) or we have a 1293 // change in title 1294 bool fake_it = 1295 (m_isStream 1296 && ((!newTags.contains("TITLE") 1297 && newTags.contains("ORGANIZATION")) 1298 || (newTags.contains("TITLE") 1299 && m_metaData.value("TITLE") != newTags.value("TITLE"))) 1300 && !newTags.contains("ALBUM") 1301 && !newTags.contains("ARTIST")); 1302 1225 1303 TagMap oldMap = m_metaData; // Keep a copy of the old one for reference 1226 // Append any new meta tags to the existing tag list 1227 gst_tag_list_foreach (tag_list, &foreach_tag_function, &m_metaData); 1304 1305 // Now we've checked the new data, append any new meta tags to the existing tag list 1306 // We cannot use TagMap::iterator as this is a multimap and when streaming data 1307 // could in theory be lost. 1308 QList<QString> keys = newTags.keys(); 1309 for (QList<QString>::iterator i = keys.begin(); i != keys.end(); ++i) { 1310 QString key = *i; 1311 if (m_isStream) { 1312 // If we're streaming, we need to remove data in m_metaData 1313 // in order to stop it filling up indefinitely (as it's a multimap) 1314 m_metaData.remove(key); 1315 } 1316 QList<QString> values = newTags.values(key); 1317 for (QList<QString>::iterator j = values.begin(); j != values.end(); ++j) { 1318 QString value = *j; 1319 QString currVal = m_metaData.value(key); 1320 if (!m_metaData.contains(key) || currVal != value) { 1321 m_metaData.insert(key, value); 1322 } 1323 } 1324 } 1325 1228 1326 m_backend->logMessage("Meta tags found", Backend::Info, this); 1229 if (oldMap != m_metaData && !m_loading) 1230 emit metaDataChanged(m_metaData); 1231 gst_tag_list_free(tag_list); 1232 } 1327 if (oldMap != m_metaData) { 1328 // This is a bit of a hack to ensure that stream metadata is 1329 // returned. We get as much as we can from the Shoutcast server's 1330 // StreamTitle= header. If further info is decoded from the stream 1331 // itself later, then it will overwrite this info. 1332 if (m_isStream && fake_it) { 1333 m_metaData.remove("ALBUM"); 1334 m_metaData.remove("ARTIST"); 1335 1336 // Detect whether we want to "fill in the blanks" 1337 QString str; 1338 if (m_metaData.contains("TITLE")) 1339 { 1340 str = m_metaData.value("TITLE"); 1341 int splitpoint; 1342 // Check to see if our title matches "%s - %s" 1343 // Where neither %s are empty... 1344 if ((splitpoint = str.indexOf(" - ")) > 0 1345 && str.size() > (splitpoint+3)) { 1346 m_metaData.insert("ARTIST", str.left(splitpoint)); 1347 m_metaData.replace("TITLE", str.mid(splitpoint+3)); 1348 } 1349 } else { 1350 str = m_metaData.value("GENRE"); 1351 if (!str.isEmpty()) 1352 m_metaData.insert("TITLE", str); 1353 else 1354 m_metaData.insert("TITLE", "Streaming Data"); 1355 } 1356 if (!m_metaData.contains("ARTIST")) { 1357 str = m_metaData.value("LOCATION"); 1358 if (!str.isEmpty()) 1359 m_metaData.insert("ARTIST", str); 1360 else 1361 m_metaData.insert("ARTIST", "Streaming Data"); 1362 } 1363 str = m_metaData.value("ORGANIZATION"); 1364 if (!str.isEmpty()) 1365 m_metaData.insert("ALBUM", str); 1366 else 1367 m_metaData.insert("ALBUM", "Streaming Data"); 1368 } 1369 // As we manipulate the title, we need to recompare 1370 // oldMap and m_metaData here... 1371 if (oldMap != m_metaData && !m_loading) 1372 emit metaDataChanged(m_metaData); 1373 } 1374 } 1233 1375 } 1234 1376 break; … … 1256 1398 m_tickTimer->start(); 1257 1399 changeState(Phonon::PlayingState); 1400 if ((m_source.type() == MediaSource::Disc) && (m_currentTitle != m_pendingTitle)) { 1401 setTrack(m_pendingTitle); 1402 } 1258 1403 if (m_resumeState && m_oldState == Phonon::PlayingState) { 1259 1404 seek(m_oldPos); … … 1291 1436 m_backend->logMessage("gstreamer: pipeline state set to ready", Backend::Debug, this); 1292 1437 m_tickTimer->stop(); 1438 if ((m_source.type() == MediaSource::Disc) && (m_currentTitle != m_pendingTitle)) { 1439 setTrack(m_pendingTitle); 1440 } 1293 1441 break; 1294 1442 … … 1329 1477 gst_caps_unref (caps); 1330 1478 gst_object_unref (sinkPad); 1331 } 1479 } 1332 1480 } else { 1333 1481 setError(QString(err->message), Phonon::FatalError); … … 1401 1549 //case GST_MESSAGE_LATENCY: only from 0.10.12 1402 1550 //case GST_MESSAGE_ASYNC_DONE: only from 0.10.13 1403 default: 1404 break; 1551 default: 1552 break; 1405 1553 } 1406 1554 } … … 1418 1566 m_atEndOfStream = true; 1419 1567 1420 if (m_autoplayTitles && 1568 if (m_source.type() == MediaSource::Disc && 1569 m_autoplayTitles && 1421 1570 m_availableTitles > 1 && 1422 1571 m_currentTitle < m_availableTitles) { … … 1442 1591 setState(m_pendingState); 1443 1592 } 1593 } 1594 } 1595 1596 void MediaObject::invalidateGraph() 1597 { 1598 m_resetNeeded = true; 1599 if (m_state == Phonon::PlayingState || m_state == Phonon::PausedState) { 1600 changeState(Phonon::StoppedState); 1444 1601 } 1445 1602 } … … 1503 1660 void MediaObject::_iface_setCurrentTitle(int title) 1504 1661 { 1662 m_backend->logMessage(QString("setCurrentTitle %0").arg(title), Backend::Info, this); 1663 if ((title == m_currentTitle) || (title == m_pendingTitle)) 1664 return; 1665 1666 m_pendingTitle = title; 1667 1668 if (m_state == Phonon::PlayingState || m_state == Phonon::StoppedState) { 1669 setTrack(m_pendingTitle); 1670 } else { 1671 setState(Phonon::StoppedState); 1672 } 1673 } 1674 1675 void MediaObject::setTrack(int title) 1676 { 1677 if (((m_state != Phonon::PlayingState) && (m_state != Phonon::StoppedState)) || (title < 1) || (title > m_availableTitles)) 1678 return; 1679 1680 1681 //let's seek to the beginning of the song 1505 1682 GstFormat trackFormat = gst_format_get_by_nick("track"); 1506 m_backend->logMessage(QString("setCurrentTitle %0").arg(title), Backend::Info, this); 1507 if ((title == m_currentTitle) || (title < 1) || (title > m_availableTitles)) 1508 return; 1509 1510 m_currentTitle = title; 1511 1512 //let's seek to the beginning of the song 1513 if (gst_element_seek_simple(m_pipeline, trackFormat, GST_SEEK_FLAG_FLUSH, m_currentTitle - 1)) { 1683 m_backend->logMessage(QString("setTrack %0").arg(title), Backend::Info, this); 1684 if (gst_element_seek_simple(m_pipeline, trackFormat, GST_SEEK_FLAG_FLUSH, title - 1)) { 1685 m_currentTitle = title; 1514 1686 updateTotalTime(); 1515 1687 m_atEndOfStream = false;
Note:
See TracChangeset
for help on using the changeset viewer.