Ticket #3580: mythtv-commskip-0.20-fixes.patch
File mythtv-commskip-0.20-fixes.patch, 44.4 KB (added by , 17 years ago) |
---|
-
libs/libmythtv/programinfo.h
12 12 using namespace std; 13 13 typedef QMap<long long, long long> frm_pos_map_t; 14 14 typedef QMap<long long, int> frm_dir_map_t; 15 typedef QMap<long long, time_t> pos_time_map_t; 15 16 16 17 #define NUMPROGRAMLINES 42 17 18 … … 223 224 void SetPositionMap(frm_pos_map_t &, int type, 224 225 long long min_frm = -1, long long max_frm = -1) const; 225 226 void SetPositionMapDelta(frm_pos_map_t &, int type) const; 227 int GetTrackMap( const QString &sChanId, const QString &sStartTime, pos_time_map_t &TrackMap); 226 228 227 229 228 230 // GUI stuff … … 375 377 #endif 376 378 377 379 /* vim: set expandtab tabstop=4 shiftwidth=4: */ 380 -
libs/libmythtv/programinfo.cpp
2544 2544 } 2545 2545 } 2546 2546 2547 ///////////////////////////////////////////////////////////////////////////// 2548 // 2549 // GetTrackMap - creates a position map <position offset, time> indicating the 2550 // ending position of each commercial break in a recording 2551 // returns the number of tracks 2552 // 2553 ///////////////////////////////////////////////////////////////////////////// 2554 2555 int ProgramInfo::GetTrackMap ( const QString &sChanId, const QString &sStartTime, pos_time_map_t &trackMap ) 2556 { 2557 2558 frm_dir_map_t markmap; 2559 frm_pos_map_t posMap; 2560 int keyframe_dist = 15; 2561 2562 trackMap.clear(); 2563 ProgramInfo *pInfo = ProgramInfo::GetProgramFromRecorded( sChanId, sStartTime ); 2564 2565 if (pInfo == NULL) 2566 { 2567 VERBOSE( VB_UPNP, QString( "ProgramInfo::GetTrackMap - GetProgramFromRecorded( %1, %2 ) returned NULL" ) 2568 .arg( sChanId ) 2569 .arg( sStartTime )); 2570 return 0; 2571 } 2572 2573 pInfo->GetMarkupMap(markmap, MARK_COMM_END); 2574 2575 pInfo->GetPositionMap(posMap, MARK_GOP_START); 2576 2577 if ( posMap.isEmpty() ) { 2578 pInfo->GetPositionMap(posMap, MARK_GOP_BYFRAME); 2579 keyframe_dist = 1; 2580 } 2581 2582 delete pInfo; 2583 2584 if ( markmap.isEmpty() || posMap.isEmpty() ) 2585 { 2586 VERBOSE( VB_UPNP, "ProgramInfo::GetTrackMap - No Commercial End Marks."); 2587 return 0; 2588 } 2589 2590 QMap<long long, int>::Iterator i; 2591 long long pos, j=0; time_t time; 2592 2593 for (i = markmap.begin(); i != markmap.end(); ++i) { 2594 pos = i.key()/keyframe_dist; 2595 time = (double) i.key() / 29.97; /* #frames/NTSC framerate = #seconds */ 2596 trackMap[time] = posMap[pos]; 2597 j++; 2598 } 2599 return j+1; 2600 } 2601 2547 2602 /** \fn ProgramInfo::ReactivateRecording(void) 2548 2603 * \brief Asks the scheduler to restart this recording if possible. 2549 2604 */ … … 4436 4491 } 4437 4492 4438 4493 /* vim: set expandtab tabstop=4 shiftwidth=4: */ 4494 -
libs/libmythupnp/httprequest.cpp
4 4 // Purpose - Http Request/Response 5 5 // 6 6 // Created By : David Blain Created On : Oct. 21, 2005 7 // Modified By : Modified On:7 // Modified By : Rob Gingher Modified On: Jun. 13, 2007 8 8 // 9 9 ////////////////////////////////////////////////////////////////////////////// 10 10 … … 41 41 42 42 static MIMETypes g_MIMETypes[] = 43 43 { 44 { "swf" , "application/futuresplash" }, 45 { "pdf" , "application/pdf" }, 46 { "xls" , "application/vnd.ms-excel" }, 47 { "doc" , "application/vnd.ms-word" }, 48 { "rm" , "application/vnd.rn-realmedia" }, 49 { "zip" , "application/x-tar" }, 50 { "gz" , "application/x-tar" }, 51 { "mid" , "audio/midi" }, 52 { "mp3" , "audio/mpeg" }, 53 { "m3u" , "audio/m3u" }, 54 { "wav" , "audio/wav" }, 44 55 { "gif" , "image/gif" }, 45 56 { "jpg" , "image/jpeg" }, 46 57 { "png" , "image/png" }, 58 { "css" , "text/css" }, 47 59 { "htm" , "text/html" }, 48 60 { "html", "text/html" }, 49 61 { "js" , "text/html" }, 50 62 { "txt" , "text/plain" }, 51 63 { "xml" , "text/xml" }, 52 { "pdf" , "application/pdf" },53 64 { "avi" , "video/avi" }, 54 { "css" , "text/css" },55 { "swf" , "application/futuresplash" },56 { "xls" , "application/vnd.ms-excel" },57 { "doc" , "application/vnd.ms-word" },58 { "mid" , "audio/midi" },59 { "mp3" , "audio/mpeg" },60 { "rm" , "application/vnd.rn-realmedia" },61 { "wav" , "audio/wav" },62 { "zip" , "application/x-tar" },63 { "gz" , "application/x-tar" },64 65 { "mpg" , "video/mpeg" }, 65 66 { "mpeg", "video/mpeg" }, 66 67 { "vob", "video/mpeg" }, … … 178 179 .arg( nSize ); 179 180 sHeader += "\r\n"; 180 181 182 // VERBOSE(VB_UPNP, QString("HTTPRequest::BuildHeader : sHeader : %1 : ").arg(sHeader)); 183 181 184 return sHeader; 182 185 } 183 186 … … 213 216 break; 214 217 } 215 218 216 //VERBOSE(VB_UPNP,QString("HTTPRequest::SendResponse(xml/html) :%1 -> %2:")217 //.arg(GetResponseStatus())218 //.arg(GetPeerAddress()));219 // VERBOSE(VB_UPNP,QString("HTTPRequest::SendResponse(xml/html) :%1 -> %2:") 220 // .arg(GetResponseStatus()) 221 // .arg(GetPeerAddress())); 219 222 220 223 // ---------------------------------------------------------------------- 221 224 // Make it so the header is sent with the data … … 276 279 m_eResponseType = ResponseTypeOther; 277 280 m_sResponseTypeText = "text/plain"; 278 281 279 /*280 Dump request header281 for ( QStringMap::iterator it = m_mapHeaders.begin();282 it != m_mapHeaders.end();283 ++it )284 {285 cout << it.key() << ": " << it.data() << endl;286 }287 */288 289 282 // ---------------------------------------------------------------------- 290 283 // Make it so the header is sent with the data 291 284 // ---------------------------------------------------------------------- … … 302 295 m_sResponseTypeText = GetMimeType( info.extension( FALSE ).lower() ); 303 296 304 297 // ------------------------------------------------------------------ 305 // Get File size 298 // Get File size - set llEnd to whole file 306 299 // ------------------------------------------------------------------ 307 300 308 301 struct stat st; … … 312 305 313 306 m_nResponseStatus = 200; 314 307 315 // ------------------------------------------------------------------316 // Process any Range Header 317 // ------------------------------------------------------------------308 // -------------------------------------------------------------- 309 // Process bytes parameter for start and end offsets of track 310 // -------------------------------------------------------------- 318 311 312 QString sBytes = m_mapParams[ "bytes"]; 313 if(ParseRange( sBytes, &llStart, &llEnd )) llSize = llEnd - llStart + 1; 314 315 // -------------------------------------------------------------- 316 // Process Range header for ff/rew -- Offset from start of track 317 // -------------------------------------------------------------- 318 319 long long llOffset = 0, llDummy = 0; 320 319 321 bool bRange = false; 320 QString sRange = GetHeaderValue( "range", "" );321 322 322 if (sRange.length() > 0) 323 { 324 if ( bRange = ParseRange( sRange, llSize, &llStart, &llEnd ) ) 325 { 326 m_nResponseStatus = 206; 327 m_mapRespHeaders[ "Content-Range" ] = QString("bytes %1-%2/%3") 328 .arg( llStart ) 329 .arg( llEnd ) 330 .arg( llSize ); 331 llSize = (llEnd - llStart) + 1; 332 } 333 } 323 QString sRange = GetHeaderValue( "range", "" ); 324 bRange = ParseRange ( sRange, &llOffset, &llDummy ); 325 326 // VERBOSE(VB_UPNP,QString("HTTPRequest::SendResponseFile : Start/End %1-%2 Offset/Dummy %3-%4 bRange: %5") 327 // .arg(llStart) 328 // .arg(llEnd) 329 // .arg(llOffset) 330 // .arg(llDummy) 331 // .arg(bRange)); 332 333 // 334 // Did user hit ff or rewind? Is there a different start pos.? 335 // 336 337 if ( bRange ) { /* process for partial content */ 338 m_nResponseStatus = 206; 339 llDummy += llStart; 340 llStart += llOffset; 341 if( llDummy > llStart && llDummy < llEnd) llEnd = llDummy; 342 llSize = llEnd - llStart + 1; 343 } 344 345 m_mapRespHeaders[ "Content-Range" ] = QString("bytes %1-%2/%3") 346 .arg( llStart ) 347 .arg( llEnd ) 348 .arg( llSize ); 334 349 335 350 // DSM-?20 specific response headers 336 337 351 if (bRange == false) 338 352 m_mapRespHeaders[ "User-Agent" ] = "redsonic"; 339 353 … … 342 356 // ------------------------------------------------------------------ 343 357 344 358 } 345 else 359 else /* file does not exist */ 346 360 m_nResponseStatus = 404; 347 361 348 362 // -=>TODO: Should set "Content-Length: *" if file is still recording … … 404 418 } 405 419 406 420 ///////////////////////////////////////////////////////////////////////////// 421 // Parse Range 422 // returns: true if a non-zero start or end range is provided 423 ///////////////////////////////////////////////////////////////////////////// 424 425 bool HTTPRequest::ParseRange( QString sRange, 426 long long *pllStart, 427 long long *pllEnd ) 428 { 429 // ---------------------------------------------------------------------- 430 // -=>TODO: Only handle 1 range at this time... should make work with full spec. 431 // ---------------------------------------------------------------------- 432 433 if (sRange.length() == 0) 434 return false; 435 436 // ---------------------------------------------------------------------- 437 // remove any "bytes=" 438 // ---------------------------------------------------------------------- 439 440 int nIdx = sRange.find( QRegExp( "(\\d|\\-)") ); 441 442 if (nIdx < 0) 443 return false; 444 445 if (nIdx > 0) 446 sRange.remove( 0, nIdx ); 447 448 // ---------------------------------------------------------------------- 449 // Split multiple ranges 450 // ---------------------------------------------------------------------- 451 452 QStringList ranges = QStringList::split( ",", sRange ); 453 454 if (ranges.count() == 0) 455 return false; 456 457 // ---------------------------------------------------------------------- 458 // Split first range into its components 459 // ---------------------------------------------------------------------- 460 461 QStringList parts = QStringList::split( "-", ranges[0], true ); 462 463 if (parts.count() != 2) 464 return false; 465 466 if (parts[0].isNull() && parts[1].isNull()) 467 return false; 468 469 // ---------------------------------------------------------------------- 470 // 471 // ---------------------------------------------------------------------- 472 473 if (parts[0].isNull()) 474 { 475 // ------------------------------------------------------------------ 476 // Does it match "-####" ? return true 477 // ------------------------------------------------------------------ 478 479 *pllEnd = strtoll( parts[1], NULL, 10 ); 480 } 481 else if (parts[1].isNull()) 482 { 483 // ------------------------------------------------------------------ 484 // Does it match "####-"? if 0-, return false, else return true 485 // ------------------------------------------------------------------ 486 487 *pllStart = strtoll( parts[0], NULL, 10 ); 488 489 if (*pllStart == 0) return false; 490 } 491 else 492 { 493 // ------------------------------------------------------------------ 494 // Must be "####-####" return false if invalid 495 // ------------------------------------------------------------------ 496 497 *pllStart = strtoll( parts[0], NULL, 10 ); 498 *pllEnd = strtoll( parts[1], NULL, 10 ); 499 500 if (*pllStart > *pllEnd) 501 return false; 502 } 503 504 //cout << getSocketHandle() << "Range Requested " << *pllStart << " - " << *pllEnd << endl; 505 506 return true; 507 } 508 509 ///////////////////////////////////////////////////////////////////////////// 407 510 // 408 511 ///////////////////////////////////////////////////////////////////////////// 409 512 … … 490 593 } 491 594 else 492 595 m_response << "</" << m_sMethod << "Response>\r\n"; 596 597 // VERBOSE(VB_UPNP, QString("HTTPRequest::FormatActionResponse : m_aBuffer: %1" ).arg(m_aBuffer)); 598 493 599 } 494 600 495 601 ///////////////////////////////////////////////////////////////////////////// … … 714 820 715 821 try 716 822 { 717 // Read first line to determin requestType823 // Read first line to determine requestType 718 824 719 825 QString sRequestLine = ReadLine( 2000 ); 720 826 … … 895 1001 // 896 1002 ///////////////////////////////////////////////////////////////////////////// 897 1003 898 bool HTTPRequest::ParseRange( QString sRange,899 long long llSize,900 long long *pllStart,901 long long *pllEnd )902 {903 // ----------------------------------------------------------------------904 // -=>TODO: Only handle 1 range at this time... should make work with full spec.905 // ----------------------------------------------------------------------906 907 if (sRange.length() == 0)908 return false;909 910 // ----------------------------------------------------------------------911 // remove any "bytes="912 // ----------------------------------------------------------------------913 914 int nIdx = sRange.find( QRegExp( "(\\d|\\-)") );915 916 if (nIdx < 0)917 return false;918 919 if (nIdx > 0)920 sRange.remove( 0, nIdx );921 922 // ----------------------------------------------------------------------923 // Split multiple ranges924 // ----------------------------------------------------------------------925 926 QStringList ranges = QStringList::split( ",", sRange );927 928 if (ranges.count() == 0)929 return false;930 931 // ----------------------------------------------------------------------932 // Split first range into its components933 // ----------------------------------------------------------------------934 935 QStringList parts = QStringList::split( "-", ranges[0], true );936 937 if (parts.count() != 2)938 return false;939 940 if (parts[0].isNull() && parts[1].isNull())941 return false;942 943 // ----------------------------------------------------------------------944 //945 // ----------------------------------------------------------------------946 947 if (parts[0].isNull())948 {949 // ------------------------------------------------------------------950 // Does it match "-####"951 // ------------------------------------------------------------------952 953 long long llValue = strtoll( parts[1], NULL, 10 );954 955 *pllStart = llSize - llValue;956 *pllEnd = llSize - 1;957 }958 else if (parts[1].isNull())959 {960 // ------------------------------------------------------------------961 // Does it match "####-"962 // ------------------------------------------------------------------963 964 *pllStart = strtoll( parts[0], NULL, 10 );965 966 if (*pllStart == 0)967 return false;968 969 *pllEnd = llSize - 1;970 }971 else972 {973 // ------------------------------------------------------------------974 // Must be "####-####"975 // ------------------------------------------------------------------976 977 *pllStart = strtoll( parts[0], NULL, 10 );978 *pllEnd = strtoll( parts[1], NULL, 10 );979 980 if (*pllStart > *pllEnd)981 return false;982 }983 984 //cout << getSocketHandle() << "Range Requested " << *pllStart << " - " << *pllEnd << endl;985 986 return true;987 }988 989 /////////////////////////////////////////////////////////////////////////////990 //991 /////////////////////////////////////////////////////////////////////////////992 993 1004 void HTTPRequest::ExtractMethodFromURL() 994 1005 { 995 1006 QStringList sList = QStringList::split( "/", m_sBaseUrl, false ); -
libs/libmythupnp/httprequest.h
148 148 QString GetAdditionalHeaders( void ); 149 149 150 150 bool ParseRange ( QString sRange, 151 long long llSize,152 151 long long *pllStart, 153 152 long long *pllEnd ); 154 153 -
programs/mythbackend/mythxml.cpp
4 4 // Purpose - Html & XML status HttpServerExtension 5 5 // 6 6 // Created By : David Blain Created On : Oct. 24, 2005 7 // Modified By : Modified On: 8 // 7 // 8 // Modified By : Robert Gingher Modified On: Jun. 5, 2007 9 // * added method genm3u for commercial break tracks 10 // 9 11 ////////////////////////////////////////////////////////////////////////////// 10 12 11 13 #include "mythxml.h" … … 84 86 if (sURI == "GetExpiring" ) return MXML_GetExpiring; 85 87 if (sURI == "GetPreviewImage" ) return MXML_GetPreviewImage; 86 88 if (sURI == "GetRecording" ) return MXML_GetRecording; 89 if (sURI == "GenM3u" ) return MXML_GenM3u; 87 90 if (sURI == "GetVideo" ) return MXML_GetVideo; 88 91 if (sURI == "GetMusic" ) return MXML_GetMusic; 89 92 if (sURI == "GetConnectionInfo" ) return MXML_GetConnectionInfo; … … 122 125 case MXML_GetPreviewImage : GetPreviewImage( pRequest ); return true; 123 126 124 127 case MXML_GetRecording : GetRecording ( pThread, pRequest ); return true; 128 case MXML_GenM3u : GenM3u ( pRequest ); return true; 125 129 case MXML_GetMusic : GetMusic ( pThread, pRequest ); return true; 126 130 case MXML_GetVideo : GetVideo ( pThread, pRequest ); return true; 127 131 … … 968 972 // We only handle requests for local resources 969 973 970 974 delete pInfo; 971 972 975 return; 973 976 } 974 977 … … 1134 1137 } 1135 1138 1136 1139 ///////////////////////////////////////////////////////////////////////////// 1140 // 1141 // GetRecording 1137 1142 // 1138 1143 ///////////////////////////////////////////////////////////////////////////// 1139 1144 … … 1147 1152 pRequest->m_nResponseStatus = 404; 1148 1153 1149 1154 QString sChanId = pRequest->m_mapParams[ "ChanId" ]; 1150 QString sStartTime = pRequest->m_mapParams[ "StartTime" ];1155 QString sStartTime = pRequest->m_mapParams[ "StartTime" ]; 1151 1156 1152 1157 if (sStartTime.length() == 0) 1153 1158 { … … 1155 1160 return; 1156 1161 } 1157 1162 1163 // -------------------------------------------------------------- 1164 // Process track parameter for start and end offsets of track 1165 // and set bytes parameter 1166 // -------------------------------------------------------------- 1167 1168 int numtracks, trackno = pRequest->m_mapParams[ "track" ].toInt(); 1169 pos_time_map_t trackMap; 1170 QString sStart, sRange; 1171 QMap<long long, time_t>::Iterator i; 1172 int j=0; 1173 1174 ProgramInfo *pInfo = ProgramInfo::GetProgramFromRecorded( sChanId, sStartTime ); 1175 1176 numtracks=pInfo->GetTrackMap( sChanId, sStartTime, trackMap ); 1177 if (numtracks > 0 && trackno > 0 && trackno <= numtracks ) { /* is there a valid track parameter? */ 1178 sStart = "0-"; 1179 i = trackMap.begin(); 1180 if (trackno > 1) { 1181 for(j=1 ; j<=trackno-2; j++) ++i; 1182 sStart = QString("%1-") 1183 .arg(i.data()); 1184 i++; 1185 } 1186 sRange = sStart; 1187 if (trackno < numtracks ) sRange = QString("%1%2") 1188 .arg(sStart) 1189 .arg(i.data()); 1190 pRequest->m_mapParams[ "bytes" ] = sRange; 1191 } 1192 1193 delete pInfo; 1194 1195 /* VERBOSE( VB_UPNP, QString( "MythXML::GetRecording - trackno = %1; range = %2; bytes = %3" ) 1196 .arg( trackno ) 1197 .arg( sRange) 1198 .arg( pRequest->m_mapParams["bytes"])); 1199 */ 1200 1158 1201 // ---------------------------------------------------------------------- 1159 1202 // DSM-320 & DSM-520 Special File Request for Index file of MPEG 1160 1203 // … … 1228 1271 .arg( pInfo->hostname )); 1229 1272 1230 1273 delete pInfo; 1231 1232 1274 return; 1233 1275 } 1234 1276 … … 1265 1307 1266 1308 ///////////////////////////////////////////////////////////////////////////// 1267 1309 // 1310 // GenM3u 1311 // 1268 1312 ///////////////////////////////////////////////////////////////////////////// 1269 1313 1314 void MythXML::GenM3u ( HTTPRequest *pRequest ) 1315 { 1316 1317 pRequest->m_eResponseType = ResponseTypeHTML; 1318 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; 1319 pRequest->m_nResponseStatus = 404; 1320 1321 QString sStartTime = pRequest->m_mapParams[ "StartTime" ]; 1322 QString sChanId = pRequest->m_mapParams[ "ChanId" ]; 1323 1324 QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate ); 1325 1326 if (!dtStart.isValid()) 1327 { 1328 VERBOSE( VB_UPNP, "MythXML::GenM3u - StartTime missing."); 1329 return; 1330 } 1331 1332 QString sHostIP = gContext->GetSetting( "BackendServerIP", "localhost" ); 1333 QString sHostName = gContext->GetHostName(); 1334 QString sPort = gContext->GetSettingOnHost( "BackendStatusPort", sHostName); 1335 QString sRecordingUrl = QString( "http://%1:%2/Myth/GetRecording?ChanId=%3&StartTime=%4" ) 1336 .arg( sHostIP ) 1337 .arg( sPort ) 1338 .arg( sChanId ) 1339 .arg( sStartTime ); 1340 1341 pos_time_map_t trackMap; 1342 ProgramInfo *pInfo = ProgramInfo::GetProgramFromRecorded( sChanId, dtStart ); 1343 int num=pInfo->GetTrackMap( sChanId, sStartTime, trackMap); 1344 delete pInfo; 1345 1346 if( num == 0) return; 1347 1348 for (int i = 1; i <= num; ++i) 1349 pRequest->m_response << QString("%1&track=%2\r\n" ) 1350 .arg( sRecordingUrl ) 1351 .arg( i ); 1352 1353 pRequest->m_eResponseType = ResponseTypeOther; 1354 pRequest->m_sFileName = "GenM3u.m3u"; 1355 pRequest->m_sResponseTypeText = pRequest->GetMimeType( "m3u" ); 1356 pRequest->m_nResponseStatus = 200; 1357 1358 /* If we need to create a file, modify this code 1359 1360 sFileName = QString( "%1.%2x%3.png" ) 1361 .arg( pRequest->m_sFileName ) 1362 .arg( nWidth ) 1363 .arg( nHeight ); 1364 1365 // ---------------------------------------------------------------------- 1366 // check to see if image is already created. 1367 // ---------------------------------------------------------------------- 1368 1369 if (QFile::exists( sFileName )) 1370 { 1371 pRequest->m_eResponseType = ResponseTypeFile; 1372 pRequest->m_nResponseStatus = 200; 1373 pRequest->m_sFileName = sFileName; 1374 return; 1375 } 1376 1377 float fAspect = 0.0; 1378 1379 QImage *pImage = new QImage(pRequest->m_sFileName); 1380 1381 if (!pImage) 1382 return; 1383 1384 if (fAspect <= 0) 1385 fAspect = (float)(pImage->width()) / pImage->height(); 1386 1387 if ( nWidth == 0 ) 1388 nWidth = (int)rint(nHeight * fAspect); 1389 1390 if ( nHeight == 0 ) 1391 nHeight = (int)rint(nWidth / fAspect); 1392 1393 QImage img = pImage->smoothScale( nWidth, nHeight); 1394 1395 img.save( sFileName.ascii(), "PNG" ); 1396 1397 delete pImage; 1398 1399 pRequest->m_sFileName = sFileName; 1400 */ 1401 } 1402 1403 1404 ///////////////////////////////////////////////////////////////////////////// 1405 // 1406 ///////////////////////////////////////////////////////////////////////////// 1407 1270 1408 void MythXML::GetMusic( HttpWorkerThread *pThread, 1271 1409 HTTPRequest *pRequest ) 1272 1410 { … … 1613 1751 } 1614 1752 1615 1753 // vim:set shiftwidth=4 tabstop=4 expandtab: 1754 -
programs/mythbackend/mediaserver.cpp
125 125 "http-get:*:image/png:*," 126 126 "http-get:*:video/avi:*," 127 127 "http-get:*:audio/mpeg:*," 128 "http-get:*:audio/m3u:*," 128 129 "http-get:*:audio/wav:*," 129 130 "http-get:*:video/mpeg:*," 130 131 "http-get:*:video/nupplevideo:*," -
programs/mythbackend/upnpcdstv.cpp
4 4 // Purpose - uPnp Content Directory Extention for Recorded TV 5 5 // 6 6 // Created By : David Blain Created On : Jan. 24, 2005 7 // Modified By : Modified On: 7 // Modified By : Robert Gingher Modified On: Jun. 11, 2007 8 // *** added commercial free playlist *** 8 9 // 9 10 ////////////////////////////////////////////////////////////////////////////// 10 11 … … 19 20 20 21 /* 21 22 Recordings RecTv 22 - All Programs RecTv/ All23 + <recording 1> RecTv/ All/item?ChanId=1004&StartTime=2006-04-06T20:00:0023 - All Programs RecTv/0 24 + <recording 1> RecTv/0/item?ChanId=1004&StartTime=2006-04-06T20:00:00 24 25 + <recording 2> 25 26 + <recording 3> 26 - By Title RecTv/title 27 - <title 1> RecTv/title/key=Stargate SG-1 28 + <recording 1> RecTv/title/key=Stargate SG-1/item?ChanId=1004&StartTime=2006-04-06T20:00:00 27 28 - Commercial Free RecTv/1 29 - <recording 1> RecTv/1/key=1004@2006-04-06T20:00:00 30 + <track 1> RecTv/1/key=1004@2006-04-06T20:00:00/item?ChanId=1004&StartTime=2006-04-06T20:00:00&track=1 31 + <track 2> RecTv/1/key=1004@2006-04-06T20:00:00/item?ChanId=1004&StartTime=2006-04-06T20:00:00&track=2 32 33 - By Title RecTv/2 34 - <title 1> RecTv/2/key=Stargate SG-1 35 + <recording 1> RecTv/2/key=Stargate SG-1/item?ChanId=1004&StartTime=2006-04-06T20:00:00 29 36 + <recording 2> 37 30 38 - By Genre 31 39 - By Date 32 40 - By Channel 33 - By Group41 - By Recording Group 34 42 */ 35 43 36 44 … … 39 47 { "All Recordings", 40 48 "*", 41 49 "SELECT 0 as key, " 42 "CONCAT( title,': ', subtitle) as name, "50 "CONCAT( DATE_FORMAT(starttime, '%Y/%m/%d'),' ',title,': ', subtitle) as name, " 43 51 "1 as children " 44 52 "FROM recorded " 45 53 "%1 " 46 "ORDER BY starttime DESC",54 "ORDER BY name DESC", 47 55 "" }, 48 56 57 { "Commercial Free", 58 "CONCAT(chanid, '@', starttime)", 59 "SELECT CONCAT(chanid, '@', starttime) as id, " 60 "CONCAT( DATE_FORMAT(starttime, '%Y/%m/%d'),' ',title,': ', subtitle) as name, " 61 "count( commflagged ) as children " 62 "FROM recorded " 63 "WHERE commflagged = 1 " 64 "%1 " 65 "GROUP BY name " 66 "ORDER BY name DESC", 67 "AND id = :KEY" }, 68 49 69 { "By Title", 50 70 "title", 51 71 "SELECT title as id, " … … 53 73 "count( title ) as children " 54 74 "FROM recorded " 55 75 "%1 " 56 "GROUP BY title "57 "ORDER BY title",58 "WHERE title=:KEY" },76 "GROUP BY name " 77 "ORDER BY name", 78 "WHERE id = :KEY" }, 59 79 60 80 { "By Genre", 61 81 "category", … … 64 84 "count( category ) as children " 65 85 "FROM recorded " 66 86 "%1 " 67 "GROUP BY category"68 "ORDER BY category",69 "WHERE category=:KEY" },87 "GROUP BY name " 88 "ORDER BY name", 89 "WHERE id= :KEY" }, 70 90 71 91 { "By Date", 72 92 "DATE_FORMAT(starttime, '%Y-%m-%d')", 73 93 "SELECT DATE_FORMAT(starttime, '%Y-%m-%d') as id, " 74 " DATE_FORMAT(starttime, '%Y-%m-%d %W') as name, "94 "CONCAT('Recorded on ',DATE_FORMAT(starttime, '%m/%d/%Y')) as name, " 75 95 "count( DATE_FORMAT(starttime, '%Y-%m-%d %W') ) as children " 76 96 "FROM recorded " 77 97 "%1 " 78 98 "GROUP BY name " 79 99 "ORDER BY starttime DESC", 80 "WHERE DATE_FORMAT(starttime, '%Y-%m-%d') =:KEY" },100 "WHERE id = :KEY" }, 81 101 82 102 { "By Channel", 83 103 "chanid", 84 104 "SELECT channel.chanid as id, " 85 "CONCAT( channel.channum, ' ', channel.callsign) as name, "105 "CONCAT('Channel ',channel.channum, ' ', channel.callsign) as name, " 86 106 "count( channum ) as children " 87 107 "FROM channel " 88 108 "INNER JOIN recorded ON channel.chanid = recorded.chanid " 89 109 "%1 " 90 110 "GROUP BY name " 91 "ORDER BY channel.chanid",92 "WHERE channel.chanid=:KEY" },111 "ORDER BY id", 112 "WHERE id = :KEY" }, 93 113 94 114 95 115 { "By Group", … … 98 118 "recgroup as name, count( recgroup ) as children " 99 119 "FROM recorded " 100 120 "%1 " 101 "GROUP BY recgroup"102 "ORDER BY recgroup",103 "WHERE recgroup=:KEY" }121 "GROUP BY name " 122 "ORDER BY name", 123 "WHERE id = :KEY" } 104 124 }; 105 125 106 126 int UPnpCDSTv::g_nRootCount = sizeof( g_RootNodes ) / sizeof( UPnpCDSRootInfo ); … … 168 188 169 189 ///////////////////////////////////////////////////////////////////////////// 170 190 // 191 // AddItem() 192 // 171 193 ///////////////////////////////////////////////////////////////////////////// 172 194 173 195 void UPnpCDSTv::AddItem( const QString &sObjectId, … … 175 197 bool bAddRef, 176 198 MSqlQuery &query ) 177 199 { 178 int nChanid = query.value( 0).toInt();200 int nChanId = query.value( 0).toInt(); 179 201 QDateTime dtStartTime = query.value( 1).toDateTime(); 180 202 QDateTime dtEndTime = query.value( 2).toDateTime(); 181 203 QString sTitle = query.value( 3).toString(); … … 186 208 QString sRecGroup = query.value( 8).toString(); 187 209 long long nFileSize = stringToLongLong( query.value( 9).toString() ); 188 210 QString sBaseName = query.value(10).toString(); 189 190 211 QDateTime dtProgStart = query.value(11).toDateTime(); 191 212 QDateTime dtProgEnd = query.value(12).toDateTime(); 192 213 … … 204 225 // Build Support Strings 205 226 // ---------------------------------------------------------------------- 206 227 207 QString sName = sTitle + ": " + sSubtitle; 228 QStringList idPath = QStringList::split( "/", sObjectId.section('=',0,0) ); 229 bool commflag = (idPath[1] == "1"); 208 230 209 231 QString sURIBase = QString( "http://%1:%2/Myth/" ) 210 232 .arg( m_mapBackendIp [ sHostName ] ) 211 233 .arg( m_mapBackendPort[ sHostName ] ); 212 234 213 QString sURIParams = QString( "?ChanId=%1&StartTime=%2" ) 214 .arg( nChanid ) 235 QString sName = sTitle + ": " + sSubtitle; 236 237 QString sURIParams = QString( "?ChanId=%1&StartTime=%2" ) 238 .arg( nChanId ) 215 239 .arg( dtStartTime.toString(Qt::ISODate)); 216 240 217 QString sId = QString( "%1/item%2") 218 .arg( sObjectId ) 219 .arg( sURIParams ); 241 QString sStartTime = query.value(1).toString(); 242 pos_time_map_t trackMap; 243 ProgramInfo *pInfo = ProgramInfo::GetProgramFromRecorded( QString("%1").arg(nChanId), sStartTime ); 244 int num=0; 245 time_t uiStart, uiEnd=0; 246 QMap<long long, time_t>::Iterator i; 247 long long llSize, llStartPos, llEndPos=0; 220 248 221 CDSObject *pItem = CDSObject::CreateVideoItem( sId,222 sName,223 sObjectId);224 pItem->m_bRestricted = false;225 pItem->m_bSearchable = true;226 pItem->m_sWriteStatus = "WRITABLE";249 if (pInfo != NULL && commflag) { 250 num=pInfo->GetTrackMap( QString("%1").arg(nChanId), sStartTime, trackMap); 251 i = trackMap.begin(); 252 delete pInfo; 253 pResults->m_nTotalMatches = num; 254 } 227 255 228 if ( bAddRef ) 256 if (!commflag) num = 1; 257 258 if( num == 0) return; 259 260 for (int trackno = 1; trackno <= num; ++trackno ) 229 261 { 230 QString sRefId = QString( "%1/0/item%2") 231 .arg( m_sExtensionId ) 232 .arg( sURIParams ); 262 QString sTrackParam = QString("&track=%1").arg(trackno); 263 QString sTrackName = QString(" track %1").arg(trackno); 233 264 234 pItem->SetPropValue( "refID", sRefId ); 235 } 265 if ( !commflag ) { 266 sTrackParam = ""; 267 sTrackName = ""; 268 } 236 269 237 pItem->SetPropValue( "genre" , sCategory ); 238 pItem->SetPropValue( "longDescription", sDescription ); 239 pItem->SetPropValue( "description" , sSubtitle ); 270 QString sId = QString( "%1/item%2%3") 271 .arg( sObjectId ) 272 .arg( sURIParams ) 273 .arg( sTrackParam ); 274 CDSObject *pItem = CDSObject::CreateVideoItem( sId, QString("%1%2").arg(sName).arg(sTrackName), sObjectId); 240 275 241 //pItem->SetPropValue( "producer" , ); 242 //pItem->SetPropValue( "rating" , ); 243 //pItem->SetPropValue( "actor" , ); 244 //pItem->SetPropValue( "director" , ); 245 //pItem->SetPropValue( "publisher" , ); 246 //pItem->SetPropValue( "language" , ); 247 //pItem->SetPropValue( "relation" , ); 248 //pItem->SetPropValue( "region" , ); 276 pItem->m_bRestricted = false; 277 pItem->m_bSearchable = true; 278 pItem->m_sWriteStatus = "WRITABLE"; 249 279 250 // ---------------------------------------------------------------------- 251 // Needed for Microsoft Media Player Compatibility 252 // (Won't display correct Title without them) 253 // ---------------------------------------------------------------------- 280 if ( bAddRef ) 281 { 282 QString sRefId = QString( "%1/0/item%2%3") 283 .arg( m_sExtensionId ) 284 .arg( sURIParams ) 285 .arg(sTrackParam); 254 286 255 pItem->SetPropValue( "creator" , "[Unknown Author]" ); 256 pItem->SetPropValue( "artist" , "[Unknown Author]" ); 257 pItem->SetPropValue( "album" , "[Unknown Series]" ); 258 pItem->SetPropValue( "actor" , "[Unknown Author]" ); 287 pItem->SetPropValue( "refID", sRefId ); 288 } 259 289 260 pResults->Add( pItem ); 290 pItem->SetPropValue( "genre" , sCategory ); 291 pItem->SetPropValue( "longDescription", sDescription ); 292 pItem->SetPropValue( "description" , sSubtitle ); 261 293 262 // ---------------------------------------------------------------------- 263 // Add Video Resource Element based on File extension (HTTP) 264 // ---------------------------------------------------------------------- 265 266 QFileInfo fInfo( sBaseName ); 294 // ---------------------------------------------------------------------- 295 // Needed for Microsoft Media Player Compatibility 296 // (Won't display correct Title without them) 297 // ---------------------------------------------------------------------- 298 pItem->SetPropValue( "creator" , "[Unknown Author]" ); 299 pItem->SetPropValue( "artist" , "[Unknown Author]" ); 300 pItem->SetPropValue( "album" , "[Unknown Series]" ); 301 pItem->SetPropValue( "actor" , "[Unknown Author]" ); 267 302 268 QString sMimeType = HTTPRequest::GetMimeType( fInfo.extension( FALSE )); 269 // DLNA string below is temp fix for ps3 seeking. 270 QString sProtocol = QString( "http-get:*:%1:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.OR G_FLAGS=01500000000000000000000000000000" ).arg( sMimeType ); 271 QString sURI = QString( "%1GetRecording%2").arg( sURIBase ) 272 .arg( sURIParams ); 303 pResults->Add( pItem ); 273 304 274 Resource *pRes = pItem->AddResource( sProtocol, sURI ); 305 // ---------------------------------------------------------------------- 306 // Add Video Resource Element/Playlist based on File extension (HTTP) 307 // ---------------------------------------------------------------------- 275 308 276 uint uiStart = dtProgStart.toTime_t(); 277 uint uiEnd = dtProgEnd.toTime_t(); 278 uint uiDur = uiEnd - uiStart; 309 QFileInfo fInfo( sBaseName ); 310 QString sMimeType = HTTPRequest::GetMimeType( fInfo.extension( FALSE )); 311 // DLNA string below is temp fix for ps3 seeking. 312 QString sProtocol = QString( "http-get:*:%1:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.OR G_FLAGS=01500000000000000000000000000000" ).arg( sMimeType ); 313 QString sURI = QString( "%1GetRecording%2%3").arg( sURIBase ) 314 .arg( sURIParams ) 315 .arg(sTrackParam); 279 316 317 Resource *pRes = pItem->AddResource( sProtocol, sURI ); 280 318 281 QString sDur = QString( "%1:%2:%3" ) 282 .arg( (uiDur / 3600) % 24, 2 ) 283 .arg( (uiDur / 60) % 60 , 2 ) 284 .arg( uiDur % 60 , 2 ); 319 if ( !commflag ) { 320 uiStart = dtProgStart.toTime_t(); 321 uiEnd = dtProgEnd.toTime_t(); 322 llSize = nFileSize; 323 } else { 324 uiStart = uiEnd; 325 llStartPos = llEndPos; 326 llEndPos = i.data(); 327 uiEnd = i.key(); 328 if (trackno == num) { 329 uiEnd = dtProgEnd.toTime_t() - dtProgStart.toTime_t(); 330 llEndPos = nFileSize; 331 } 332 llSize = llEndPos - llStartPos; 333 } 285 334 286 pRes->AddAttribute( "duration" , sDur ); 287 pRes->AddAttribute( "size" , longLongToString( nFileSize) ); 335 /* VERBOSE( VB_UPNP, QString( "UpnpCDSTv::AddItem() - Id %1 start = %2 end = %3 size = %4" ) 336 .arg( sId ) 337 .arg( uiStart ) 338 .arg( uiEnd ) 339 .arg( llSize )); 340 */ 341 time_t uiDur = uiEnd - uiStart; 288 342 289 /* 290 // ----------------------------------------------------------------------291 // Add Video Resource Element based on File extension (mythtv)292 // ----------------------------------------------------------------------343 QString sDur = QString( "%1:%2:%3" ) 344 .arg( (uiDur / 3600) % 24, 2 ) 345 .arg( (uiDur / 60) % 60 , 2 ) 346 .arg( uiDur % 60 , 2 ); 293 347 294 sProtocol = QString( "myth:*:%1:*" ).arg( sMimeType ); 295 sURI = QString( "myth://%1/%2" ) 296 .arg( m_mapBackendIp [ sHostName ] ) 297 .arg( sBaseName ); 348 pRes->AddAttribute( "duration" , sDur ); 349 pRes->AddAttribute( "size" , longLongToString( llSize ) ); 298 350 299 pRes = pItem->AddResource( sProtocol, sURI ); 351 // ---------------------------------------------------------------------- 352 // Add Thumbnail Resource 353 // ---------------------------------------------------------------------- 300 354 301 pRes->AddAttribute( "duration" , sDur ); 302 pRes->AddAttribute( "size" , longLongToString( nFileSize) ); 303 */ 304 // ---------------------------------------------------------------------- 305 // Add Thumbnail Resource 306 // ---------------------------------------------------------------------- 355 //sURI = QString( "%1GetPreviewImage%2").arg( sURIBase ) 356 // .arg( sURIParams ); 307 357 308 //sURI = QString( "%1GetPreviewImage%2").arg( sURIBase ) 309 // .arg( sURIParams ); 358 //pItem->AddResource( "http-get:*:image/png:*" , sURI ); 310 359 311 //pItem->AddResource( "http-get:*:image/png:*" , sURI ); 360 if(commflag) ++i; 361 } /* for */ 312 362 313 363 } 314 364 -
programs/mythbackend/mythxml.h
4 4 // Purpose - Myth XML protocol HttpServerExtension 5 5 // 6 6 // Created By : David Blain Created On : Oct. 24, 2005 7 // Modified By : Modified On:7 // Modified By : R. Gingher Modified On: Jun. 5, 2007 8 8 // 9 9 ////////////////////////////////////////////////////////////////////////////// 10 10 … … 44 44 MXML_GetPreviewImage = 9, 45 45 46 46 MXML_GetRecording = 10, 47 MXML_GetMusic = 11, 47 MXML_GenM3u = 11, 48 MXML_GetMusic = 12, 48 49 49 MXML_GetExpiring = 1 2,50 MXML_GetProgramDetails = 1 3,51 MXML_GetVideo = 1 4,50 MXML_GetExpiring = 13, 51 MXML_GetProgramDetails = 14, 52 MXML_GetVideo = 15, 52 53 53 MXML_GetConnectionInfo = 1 5,54 MXML_GetAlbumArt = 1 654 MXML_GetConnectionInfo = 16, 55 MXML_GetAlbumArt = 17 55 56 56 57 } MythXMLMethod; 57 58 … … 114 115 void GetRecording ( HttpWorkerThread *pThread, 115 116 HTTPRequest *pRequest ); 116 117 118 void GenM3u ( HTTPRequest *pRequest ); 119 120 117 121 void GetMusic ( HttpWorkerThread *pThread, 118 122 HTTPRequest *pRequest ); 119 123 120 124 void GetVideo ( HttpWorkerThread *pThread, 121 125 HTTPRequest *pRequest ); 122 126 127 void GetDeviceDesc ( HTTPRequest *pRequest ); 123 128 124 void GetDeviceDesc ( HTTPRequest *pRequest );125 129 void GetFile ( HTTPRequest *pRequest, QString sFileName ); 126 130 127 131 public: … … 130 134 131 135 bool ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ); 132 136 137 int TrackEndMap ( QString sChanId, QDateTime dtStart, frm_pos_map_t &trackMap ); 138 133 139 // Static methods shared with HttpStatus 134 140 135 141 static void FillProgramInfo ( QDomDocument *pDoc, -
programs/mythbackend/upnpcdsmusic.cpp
130 130 // 131 131 ///////////////////////////////////////////////////////////////////////////// 132 132 133 QString UPnpCDSMusic::GetTableName( QString sColumn)133 QString UPnpCDSMusic::GetTableName( QString /* sColumn */) 134 134 { 135 135 return "music_songs song"; 136 136 }