Ticket #10020: allchanges.diff
File allchanges.diff, 46.3 KB (added by , 13 years ago) |
---|
-
new file mythtv/libs/libmythupnp/audioConverterL16.cpp
diff --git a/mythtv/libs/libmythupnp/audioConverterL16.cpp b/mythtv/libs/libmythupnp/audioConverterL16.cpp new file mode 100644 index 0000000..0fa046a
- + 1 #include "audioConverterL16.h" 2 3 extern "C" { 4 5 #include "../../external/FFmpeg/libavdevice/avdevice.h" 6 #include "../../external/FFmpeg/libavutil/samplefmt.h" 7 8 // MemoryUrlProtocol is necessary to make FFMPEG libraries output 9 // to a memory buffer rather than to a file 10 11 #define MEMORY_URL_PROTOCOL_NAME "memoryUrl" 12 #define MEMORY_URL_PROTOCOL_PREFIX "memoryUrl:" 13 14 typedef struct { 15 char* buffer; 16 int used; 17 } MEMORY_URL_BUF; 18 19 static int memoryUrl_open(URLContext *urlContext, const char *dummy, int flags) 20 { 21 MEMORY_URL_BUF* urlContextData = 22 (MEMORY_URL_BUF*)av_malloc(sizeof(MEMORY_URL_BUF)); 23 urlContextData->buffer = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); 24 urlContextData->used = 0; 25 urlContext->priv_data = urlContextData; 26 urlContext->is_streamed = 1; 27 return 0; 28 } 29 30 static int memoryUrl_write(URLContext *h, const unsigned char *buffer, int size) 31 { 32 MEMORY_URL_BUF *audioData = (MEMORY_URL_BUF*)h->priv_data; 33 int bytesToWrite = AVCODEC_MAX_AUDIO_FRAME_SIZE - audioData->used; 34 if( size < bytesToWrite ) 35 { 36 bytesToWrite = size; 37 } 38 if( bytesToWrite < 0 ) 39 { 40 return -1; 41 } 42 memcpy( audioData->buffer + audioData->used, buffer, bytesToWrite ); 43 audioData->used += size; 44 return bytesToWrite; 45 } 46 47 static int memoryUrl_read(URLContext *h, unsigned char *buffer, int size) 48 { 49 MEMORY_URL_BUF* audioData = (MEMORY_URL_BUF*) h->priv_data; 50 int bytesToReturn; 51 52 if (size < audioData->used) 53 { 54 bytesToReturn = size; 55 } 56 else 57 { 58 bytesToReturn = audioData->used; 59 } 60 61 // Return the bytes 62 memcpy(buffer,audioData->buffer,bytesToReturn); 63 64 // Move remaining data to the front of the buffer 65 if(bytesToReturn < audioData->used) { 66 memmove( 67 audioData->buffer, 68 audioData->buffer + bytesToReturn, 69 audioData->used - bytesToReturn); 70 } 71 72 audioData->used -= bytesToReturn; 73 return bytesToReturn; 74 } 75 76 static int memoryUrl_close(URLContext* h) 77 { 78 MEMORY_URL_BUF* buffer = (MEMORY_URL_BUF*)h->priv_data; 79 av_free(buffer->buffer); 80 av_free(buffer); 81 return 0; 82 } 83 84 static URLProtocol memoryUrl_protocol = { 85 MEMORY_URL_PROTOCOL_NAME, 86 memoryUrl_open, 87 memoryUrl_read, 88 memoryUrl_write, 89 0, 90 memoryUrl_close 91 }; 92 93 } 94 95 bool AudioConverterL16::initialised = false; 96 const AVSampleFormat AudioConverterL16::kTargetSampleFormat = AV_SAMPLE_FMT_S16; 97 const int AudioConverterL16::kTargetSampleRate = 44100; 98 const int AudioConverterL16::kTargetChannels = 2; 99 const char *AudioConverterL16::kTargetFormatName = "s16be"; 100 101 AudioConverterL16::AudioConverterL16() 102 { 103 if(!initialised) 104 { 105 /* must be called before using avcodec lib */ 106 avcodec_init(); 107 av_register_protocol2(&memoryUrl_protocol,sizeof(memoryUrl_protocol)); 108 avcodec_register_all(); 109 avdevice_register_all() ; 110 av_register_all(); 111 initialised = true; 112 } 113 114 m_audioBuffer = NULL; 115 m_writeBuffer = NULL; 116 m_inputFormatContext = NULL; 117 m_resampleBuffer = NULL; 118 m_resampleContext = NULL; 119 m_outputFormatContext = NULL; 120 m_openedFile = false; 121 122 } 123 124 AudioConverterL16::~AudioConverterL16() 125 { 126 127 av_freep( &m_writeBuffer ); 128 av_freep( &m_audioBuffer ); 129 av_freep( &m_resampleBuffer ); 130 131 if( m_inputFormatContext != NULL ) 132 { 133 avformat_free_context( m_inputFormatContext ); 134 } 135 136 if ( m_outputFormatContext != NULL ) 137 { 138 if( m_outputFormatContext->pb != NULL ) 139 { 140 url_fclose( m_outputFormatContext->pb ); 141 } 142 avformat_free_context( m_outputFormatContext ); 143 } 144 145 if( m_resampleContext != NULL ) 146 { 147 audio_resample_close( m_resampleContext ); 148 } 149 150 } 151 152 int AudioConverterL16::Open(QString inputfile) 153 { 154 155 if ( m_openedFile ) 156 { 157 // Can only open the file once - to open a new file, 158 // create a new AudioConverterL16 object 159 return AVERROR_NOTSUPP; 160 } 161 162 AVCodec *inputCodec; 163 m_writeBuffer = (uint8_t*) av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); 164 if( m_writeBuffer == NULL ) 165 { 166 return AVERROR_NOMEM; 167 } 168 169 const char *pseudoFilename = MEMORY_URL_PROTOCOL_PREFIX; 170 171 // Open input file 172 int error = av_open_input_file( 173 &m_inputFormatContext, 174 inputfile.toLocal8Bit().data(), 175 NULL, 176 0, 177 NULL); 178 179 if( error != 0 ) 180 { 181 return error; // Couldn't open file 182 } 183 184 // Retrieve stream information 185 error = av_find_stream_info(m_inputFormatContext); 186 if( error < 0 ) 187 { 188 return error; // Couldn't find stream information 189 } 190 191 // Find the first audio stream 192 bool foundStream = false; 193 for( 194 m_inputStreamIndex = 0; 195 m_inputStreamIndex < m_inputFormatContext->nb_streams; 196 m_inputStreamIndex++ ) 197 { 198 if( m_inputFormatContext->streams[m_inputStreamIndex]->codec->codec_type 199 == CODEC_TYPE_AUDIO ) 200 { 201 foundStream = true; 202 break; 203 } 204 } 205 if (!foundStream) 206 { 207 return AVERROR_NOFMT; 208 } 209 210 // Get a pointer to the codec context for the audio stream 211 m_inputCodecContext=m_inputFormatContext->streams[m_inputStreamIndex]->codec; 212 213 // Find the decoder for the audio stream 214 inputCodec = avcodec_find_decoder(m_inputCodecContext->codec_id); 215 if( inputCodec == NULL ) 216 { 217 return AVERROR_NOFMT; 218 } 219 220 // Inform the codec that we can handle truncated bitstreams -- i.e., 221 // bitstreams where frame boundaries can fall in the middle of packets 222 if( inputCodec->capabilities & CODEC_CAP_TRUNCATED ) 223 { 224 m_inputCodecContext->flags |= CODEC_FLAG_TRUNCATED; 225 } 226 227 // Open codec 228 if( avcodec_open(m_inputCodecContext, inputCodec) < 0 ) 229 { 230 return AVERROR_NOFMT; 231 } 232 233 m_audioBuffer = (int16_t*) av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); 234 if( m_audioBuffer == NULL ) 235 { 236 return AVERROR_NOMEM; 237 } 238 239 // Resample if input rate not allowed or wrong number of channels 240 if( 241 m_inputCodecContext->sample_fmt != kTargetSampleFormat 242 || m_inputCodecContext->channels != kTargetChannels 243 || m_inputCodecContext->sample_rate != kTargetSampleRate 244 ) 245 { 246 m_resampling = true; 247 m_resampleContext = av_audio_resample_init( 248 kTargetChannels, 249 m_inputCodecContext->channels, 250 kTargetSampleRate, 251 m_inputCodecContext->sample_rate, 252 kTargetSampleFormat, 253 m_inputCodecContext->sample_fmt, 254 16, 10, 0, 0.8); // These values cribbed from libavcodec/resample.c 255 256 if ( m_resampleContext == NULL ) 257 { 258 return AVERROR_UNKNOWN; 259 } 260 261 m_resampleBuffer = (short*) av_malloc( 262 (AVCODEC_MAX_AUDIO_FRAME_SIZE / GetBytesPerSampleOut()) * 4 + 16); 263 264 if ( m_resampleBuffer == NULL ) 265 { 266 return AVERROR_NOMEM; 267 } 268 } 269 else 270 { 271 m_resampling = false; 272 } 273 274 AVOutputFormat* outputFormat = av_guess_format( 275 kTargetFormatName, 276 NULL, 277 NULL ); 278 279 if ( outputFormat == NULL ) 280 { 281 return AVERROR_NOFMT; 282 } 283 284 m_outputFormatContext = avformat_alloc_context(); 285 m_outputFormatContext->oformat = outputFormat; 286 287 m_outputAudioStream = av_new_stream( m_outputFormatContext, 1 ); 288 if ( !m_outputAudioStream ) { 289 return AVERROR_UNKNOWN; 290 } 291 292 m_outputAudioStream->codec->codec_id = outputFormat->audio_codec; 293 m_outputAudioStream->codec->codec_type = AVMEDIA_TYPE_AUDIO; 294 m_outputAudioStream->codec->sample_fmt = kTargetSampleFormat; 295 m_outputAudioStream->codec->sample_rate = kTargetSampleRate; 296 m_outputAudioStream->codec->channels = kTargetChannels; 297 298 // some formats want stream headers to be separate 299 if( m_outputFormatContext->oformat->flags & AVFMT_GLOBALHEADER ) 300 m_outputAudioStream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; 301 302 if ( av_set_parameters(m_outputFormatContext, NULL) < 0 ) 303 { 304 return AVERROR_UNKNOWN; 305 } 306 307 AVCodec *codec = avcodec_find_encoder( 308 m_outputAudioStream->codec->codec_id ); 309 310 if ( codec == NULL ) 311 { 312 return AVERROR_NOFMT; 313 } 314 315 /* open it */ 316 if ( avcodec_open( m_outputAudioStream->codec, codec ) < 0 ) 317 { 318 return AVERROR_NOFMT; 319 } 320 321 error = url_fopen( &m_outputFormatContext->pb, pseudoFilename, URL_WRONLY ); 322 if ( error < 0 ) 323 { 324 return error; 325 } 326 327 m_urlContext = url_fileno( m_outputFormatContext->pb ); 328 if( m_urlContext == 0 ) 329 { 330 return AVERROR_UNKNOWN; 331 } 332 333 m_doneHeader = false; 334 m_doneTrailer = false; 335 m_openedFile = true; 336 337 m_bytesPerSampleIn = av_get_bits_per_sample_fmt( 338 m_inputCodecContext->sample_fmt ) / 8; 339 340 if ( m_bytesPerSampleIn == 0 ) 341 { 342 return AVERROR_NOFMT; 343 } 344 345 if ( GetBytesPerSampleOut() == 0 ) 346 { 347 return AVERROR_NOFMT; 348 } 349 350 m_outputLength = 351 (m_inputFormatContext->duration * 352 kTargetSampleRate * 353 kTargetChannels * 354 GetBytesPerSampleOut( ) ) / 355 AV_TIME_BASE; 356 357 return 0; 358 359 } 360 361 const int64_t AudioConverterL16::GetOutputLength( ) 362 { 363 return m_outputLength; 364 } 365 366 const int AudioConverterL16::GetOutputRate( ) 367 { 368 return kTargetSampleRate; 369 } 370 371 int AudioConverterL16::GetChunk( unsigned char* buffer ) 372 { 373 int buffer_size; 374 int decompressedBytes; 375 int error; 376 AVPacket packet; 377 378 if( !m_openedFile ) 379 { 380 return AVERROR_NOTSUPP; 381 } 382 383 if( m_doneTrailer ) 384 { 385 return 0; 386 } 387 388 if( !m_doneHeader ) 389 { 390 error = av_write_header( m_outputFormatContext ); 391 if ( error != 0 ) 392 { 393 return error; 394 } 395 m_doneHeader = true; 396 } 397 398 bool wroteAnything = false; 399 400 while ( av_read_frame( m_inputFormatContext, &packet ) >=0 ) 401 { 402 // Is this a packet from the audio stream? 403 if( (unsigned int) packet.stream_index==m_inputStreamIndex ) 404 { 405 buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; 406 decompressedBytes = avcodec_decode_audio3( 407 m_inputCodecContext, 408 m_audioBuffer, 409 &buffer_size, 410 &packet ); 411 412 if( decompressedBytes < 0 ) 413 { 414 av_free_packet( &packet ); 415 return AVERROR_INVALIDDATA; 416 } 417 else if ( decompressedBytes < packet.size ) 418 { 419 av_free_packet( &packet ); 420 return AVERROR_INVALIDDATA; 421 } 422 else if ( decompressedBytes > 0 && buffer_size > 0 ) 423 { 424 if( m_resampling ) 425 { 426 int outputSamples = audio_resample( 427 m_resampleContext, 428 m_resampleBuffer, 429 m_audioBuffer, 430 buffer_size / 431 ( m_bytesPerSampleIn * m_inputCodecContext->channels ) 432 ); 433 434 if (outputSamples == 0) 435 { 436 av_free_packet( &packet ); 437 return AVERROR_UNKNOWN; 438 } 439 error = WriteAudioFrame( 440 m_resampleBuffer, 441 outputSamples * GetBytesPerSampleOut() * kTargetChannels 442 ); 443 } 444 else 445 { 446 error = WriteAudioFrame( m_audioBuffer, buffer_size ); 447 } 448 if ( error != 0 ) 449 { 450 av_free_packet( &packet ); 451 return error; 452 } 453 wroteAnything = true; 454 break; 455 } 456 } 457 av_free_packet( &packet ); 458 } 459 460 // If nothing was written, then we've reached the end 461 if( !wroteAnything ) 462 { 463 error = av_write_trailer( m_outputFormatContext ); 464 if ( error != 0 ) 465 { 466 return error; 467 } 468 m_doneTrailer = true; 469 } 470 471 put_flush_packet( m_outputFormatContext->pb ); 472 473 return memoryUrl_read( m_urlContext, buffer, AVCODEC_MAX_AUDIO_FRAME_SIZE ); 474 475 } 476 477 int AudioConverterL16::WriteAudioFrame( int16_t* samples, int bufferSize ) 478 { 479 AVCodecContext *codecContext; 480 AVPacket packet; 481 av_init_packet(&packet); 482 codecContext = m_outputAudioStream->codec; 483 484 packet.size = avcodec_encode_audio( codecContext, m_writeBuffer, bufferSize, samples ); 485 if ( packet.size < 0 ) 486 { 487 return AVERROR_UNKNOWN; 488 } 489 490 if ( codecContext->coded_frame && codecContext->coded_frame->pts != (int64_t) AV_NOPTS_VALUE ) 491 { 492 packet.pts= av_rescale_q( 493 codecContext->coded_frame->pts, 494 codecContext->time_base, 495 m_outputAudioStream->time_base ); 496 } 497 498 packet.flags |= AV_PKT_FLAG_KEY; 499 packet.stream_index= m_outputAudioStream->index; 500 packet.data= m_writeBuffer; 501 502 /* write the compressed frame in the media file */ 503 if ( av_write_frame (m_outputFormatContext, &packet ) < 0 ) 504 { 505 return AVERROR_UNKNOWN; 506 } 507 508 return 0; 509 } 510 511 const int AudioConverterL16::GetOutputChannels( ) 512 { 513 return kTargetChannels; 514 } 515 516 const int AudioConverterL16::GetBitsPerSample( ) 517 { 518 return 8 * GetBytesPerSampleOut(); 519 } 520 521 const int AudioConverterL16::GetBytesPerSampleOut( ) 522 { 523 static int bytesPerSampleOut = 0; 524 if (bytesPerSampleOut == 0) 525 { 526 bytesPerSampleOut = av_get_bits_per_sample_fmt(kTargetSampleFormat) / 8; 527 } 528 return bytesPerSampleOut; 529 } 530 -
new file mythtv/libs/libmythupnp/audioConverterL16.h
diff --git a/mythtv/libs/libmythupnp/audioConverterL16.h b/mythtv/libs/libmythupnp/audioConverterL16.h new file mode 100644 index 0000000..2f6ea6e
- + 1 #ifndef AUDIOCONVERTERL16_H 2 #define AUDIOCONVERTERL16_H 3 4 #include <QString> 5 6 extern "C" { 7 8 #include "../../external/FFmpeg/libavformat/avformat.h" 9 #include "../../external/FFmpeg/libavutil/samplefmt.h" 10 11 } 12 13 class AudioConverterL16 14 { 15 16 public: 17 18 AudioConverterL16(); 19 virtual ~AudioConverterL16(); 20 21 // Opens the indicated file for conversion 22 // Returns 0 on success, else negative value 23 int Open ( QString inputfile ); 24 25 // Outputs a chunk of converted bytes. 26 // Buffer size must be at least AVCODEC_MAX_AUDIO_FRAME_SIZE bytes 27 // Returns number of bytes output into buffer. 28 // If no more bytes to return, returns 0. 29 // If error, returns negative number. 30 int GetChunk ( unsigned char *buffer ); 31 32 // Length in bytes of expected output 33 const int64_t GetOutputLength ( ); 34 35 // The sample rate that will be output 36 static const int GetOutputRate ( ); 37 38 // Output channels 39 static const int GetOutputChannels ( ); 40 41 // Output bits per sample 42 static const int GetBitsPerSample ( ); 43 44 private: 45 int WriteAudioFrame ( int16_t *samples, int bufferSize ); 46 static const int GetBytesPerSampleOut ( ); 47 48 bool m_resampling; 49 bool m_openedFile; 50 bool m_doneHeader; 51 bool m_doneTrailer; 52 URLContext *m_urlContext; 53 AVFormatContext *m_outputFormatContext; 54 AVFormatContext *m_inputFormatContext; 55 unsigned int m_inputStreamIndex; 56 AVCodecContext *m_inputCodecContext; 57 int16_t *m_audioBuffer; 58 ReSampleContext *m_resampleContext; 59 short *m_resampleBuffer; 60 AVStream *m_outputAudioStream; 61 uint8_t *m_writeBuffer; 62 int64_t m_outputLength; 63 int m_bytesPerSampleIn; 64 65 static bool initialised; 66 static const AVSampleFormat kTargetSampleFormat; 67 static const int kTargetSampleRate; 68 static const int kTargetChannels; 69 static const char *kTargetFormatName; 70 71 }; 72 73 #endif // AUDIOCONVERTERL16_H -
mythtv/libs/libmythupnp/httprequest.cpp
diff --git a/mythtv/libs/libmythupnp/httprequest.cpp b/mythtv/libs/libmythupnp/httprequest.cpp index 75d6366..dcfb320 100644
a b 54 54 #include "serializers/xmlSerializer.h" 55 55 #include "serializers/soapSerializer.h" 56 56 #include "serializers/jsonSerializer.h" 57 #include "audioConverterL16.h" 57 58 58 59 #ifndef O_LARGEFILE 59 60 #define O_LARGEFILE 0 … … long HTTPRequest::SendResponse( void ) 282 283 return( nBytes ); 283 284 } 284 285 286 long HTTPRequest::SendL16ResponseFile( QString sFileName ) 287 { 288 long nBytes = 0; 289 290 LOG(VB_UPNP, LOG_INFO, QString("SendL16ResponseFile ( %1 )").arg(sFileName)); 291 292 m_eResponseType = ResponseTypeOther; 293 m_sResponseTypeText = "text/plain"; 294 295 // ---------------------------------------------------------------------- 296 // Make it so the header is sent with the data 297 // ---------------------------------------------------------------------- 298 299 #ifdef USE_SETSOCKOPT 300 // Never send out partially complete segments 301 setsockopt( getSocketHandle(), SOL_TCP, TCP_CORK, &g_on, sizeof( g_on )); 302 #endif 303 304 QFile tmpFile( sFileName ); 305 if (tmpFile.exists( ) && tmpFile.open( QIODevice::ReadOnly )) 306 { 307 m_sResponseTypeText = QString("audio/L16;rate=%1;channels=%2").arg(AudioConverterL16::GetOutputRate()).arg(AudioConverterL16::GetOutputChannels()); 308 m_nResponseStatus = 200; 309 m_mapRespHeaders[ "User-Agent" ] = "redsonic"; 310 } 311 else 312 { 313 LOG(VB_UPNP, LOG_INFO, 314 QString("HTTPRequest::SendL16ResponseFile(%1) - cannot find file!") 315 .arg(sFileName)); 316 m_nResponseStatus = 404; 317 } 318 319 QString sRange = GetHeaderValue( "range", "" ); 320 321 // ---------------------------------------------------------------------- 322 // Write out Header. 323 // ---------------------------------------------------------------------- 324 AudioConverterL16* converter = new AudioConverterL16(); 325 int error = converter->Open(tmpFile.fileName()); 326 if (error < 0) 327 { 328 LOG(VB_UPNP, LOG_INFO, 329 QString("HTTPRequest::SendL16ResponseFile(%1) - cannot convert file! AVError %2") 330 .arg(sFileName) 331 .arg(error)); 332 m_nResponseStatus = 404; 333 } 334 335 QString rHeader = BuildHeader( converter->GetOutputLength() ); 336 QByteArray sHeader = rHeader.toUtf8(); 337 nBytes = WriteBlockDirect( sHeader.constData(), sHeader.length() ); 338 339 // ---------------------------------------------------------------------- 340 // Write out File. 341 // ---------------------------------------------------------------------- 342 343 unsigned char *buffer = new unsigned char[AVCODEC_MAX_AUDIO_FRAME_SIZE]; 344 int bytesConverted = 0; 345 qlonglong bytesWritten = 0; 346 bool converterError = false; 347 do 348 { 349 bytesConverted = converter->GetChunk(buffer); 350 if( bytesConverted < 0 ) 351 { 352 converterError = true; 353 break; 354 } 355 bytesWritten = 0; 356 while(bytesWritten < bytesConverted) 357 { 358 qlonglong bytesWrittenThisTime = WriteBlockDirect((const char*)buffer + bytesWritten, bytesConverted - bytesWritten); 359 if (bytesWrittenThisTime > 0) 360 { 361 bytesWritten += bytesWrittenThisTime; 362 } 363 else 364 { 365 break; 366 } 367 } 368 } while (bytesWritten == bytesConverted && bytesConverted > 0); 369 370 if(converterError) 371 { 372 LOG(VB_UPNP, LOG_INFO, 373 QString("SendL16ResponseFile( %1 ) - failed during file conversion! AVError %2" ) 374 .arg(sFileName) 375 .arg(error)); 376 nBytes = -1; 377 } 378 379 if( bytesWritten != bytesConverted) 380 { 381 LOG(VB_UPNP, LOG_INFO, 382 QString("SendL16ResponseFile( %1 ) aborted by client" ).arg(sFileName)); 383 } 384 delete(buffer); 385 delete(converter); 386 387 // ---------------------------------------------------------------------- 388 // Turn off the option so any small remaining packets will be sent 389 // ---------------------------------------------------------------------- 390 391 #ifdef USE_SETSOCKOPT 392 setsockopt( getSocketHandle(), SOL_TCP, TCP_CORK, &g_off, sizeof( g_off )); 393 #endif 394 395 // -=>TODO: Only returns header length... 396 // should we change to return total bytes? 397 398 return nBytes; 399 400 } 401 285 402 ///////////////////////////////////////////////////////////////////////////// 286 403 // 287 404 ///////////////////////////////////////////////////////////////////////////// … … long HTTPRequest::SendResponseFile( QString sFileName ) 292 409 long long llSize = 0; 293 410 long long llStart = 0; 294 411 long long llEnd = 0; 412 413 if( m_sMethod == "GetMusic" && m_mapParams["format"] == "L16") 414 { 415 return SendL16ResponseFile(sFileName); 416 } 295 417 296 418 LOG(VB_UPNP, LOG_INFO, QString("SendResponseFile ( %1 )").arg(sFileName)); 297 419 -
mythtv/libs/libmythupnp/httprequest.h
diff --git a/mythtv/libs/libmythupnp/httprequest.h b/mythtv/libs/libmythupnp/httprequest.h index 06bd2b3..df3421f 100644
a b class UPNP_PUBLIC HTTPRequest 193 193 void FormatFileResponse ( const QString &sFileName ); 194 194 void FormatRawResponse ( const QString &sXML ); 195 195 196 long SendResponse ( void ); 197 long SendResponseFile( QString sFileName ); 196 long SendResponse ( void ); 197 long SendResponseFile ( QString sFileName ); 198 long SendL16ResponseFile ( QString sFileName ); 198 199 199 200 QString GetHeaderValue ( const QString &sKey, QString sDefault ); 200 201 -
mythtv/libs/libmythupnp/libmythupnp.pro
diff --git a/mythtv/libs/libmythupnp/libmythupnp.pro b/mythtv/libs/libmythupnp/libmythupnp.pro index ec48bfb..09b14be 100644
a b HEADERS += configuration.h 26 26 HEADERS += soapclient.h mythxmlclient.h mmembuf.h upnpexp.h 27 27 HEADERS += upnpserviceimpl.h 28 28 HEADERS += servicehost.h wsdl.h htmlserver.h serverSideScripting.h 29 HEADERS += audioConverterL16.h 29 30 30 31 HEADERS += serializers/serializer.h serializers/xmlSerializer.h 31 32 HEADERS += serializers/jsonSerializer.h serializers/soapSerializer.h … … SOURCES += configuration.cpp soapclient.cpp mythxmlclient.cpp mmembuf.cpp 39 40 SOURCES += upnpserviceimpl.cpp 40 41 SOURCES += htmlserver.cpp serverSideScripting.cpp 41 42 SOURCES += servicehost.cpp wsdl.cpp upnpsubscription.cpp 43 SOURCES += audioConverterL16.cpp 42 44 43 45 SOURCES += serializers/serializer.cpp serializers/xmlSerializer.cpp 44 46 SOURCES += serializers/jsonSerializer.cpp 45 47 46 48 INCLUDEPATH += ../libmythbase ../libmythservicecontracts .. 47 49 INCLUDEPATH += ./serializers 50 INCLUDEPATH += ../../external/FFmpeg 48 51 49 52 DEPENDPATH += ../libmythbase .. 50 53 LIBS += -L../libmythbase -lmythbase-$$LIBVERSION -
mythtv/libs/libmythupnp/upnpcds.cpp
diff --git a/mythtv/libs/libmythupnp/upnpcds.cpp b/mythtv/libs/libmythupnp/upnpcds.cpp index 791d8a0..1222e9b 100644
a b void UPnpCDS::HandleSearch( HTTPRequest *pRequest ) 533 533 // ---------------------------------------------------------------------- 534 534 // -=>TODO: Need to process all expressions in searchCriteria... for now, 535 535 // Just focus on the "upnp:class derivedfrom" expression 536 // and the "upnp:class =" expression 536 537 // ---------------------------------------------------------------------- 537 538 538 539 for ( QStringList::Iterator it = request.m_sSearchList.begin(); 539 540 it != request.m_sSearchList.end(); 540 541 ++it ) 541 542 { 542 if ((*it).contains("upnp:class derivedfrom", Qt::CaseInsensitive)) 543 if ((*it).contains("upnp:class derivedfrom", Qt::CaseInsensitive) || 544 (*it).contains("upnp:class =", Qt::CaseInsensitive)) 543 545 { 544 546 QStringList sParts = (*it).split(' ', QString::SkipEmptyParts); 545 547 … … void UPnpCDS::HandleSearch( HTTPRequest *pRequest ) 547 549 { 548 550 request.m_sSearchClass = sParts[2].trimmed(); 549 551 request.m_sSearchClass.remove( '"' ); 552 request.m_sSearchClass.remove( ')' ); 550 553 551 554 break; 552 555 } … … UPnpCDSExtensionResults *UPnpCDSExtension::Search( UPnpCDSRequest *pRequest ) 824 827 825 828 UPnpCDSExtensionResults *pResults = new UPnpCDSExtensionResults(); 826 829 827 CreateItems( pRequest, pResults, 0, "", false ); 830 QString sKey = ""; 831 if (IsOurContainerPrefix(pRequest->m_sContainerID)) 832 { 833 sKey = RemoveContainerPrefix(pRequest->m_sContainerID); 834 } 835 CreateItems( pRequest, pResults, 0, sKey, false ); 828 836 829 837 return pResults; 830 838 } … … void UPnpCDSExtension::CreateItems( UPnpCDSRequest *pRequest, 1326 1334 pResults->m_nTotalMatches = 0; 1327 1335 pResults->m_nUpdateID = 1; 1328 1336 1329 UPnpCDSRootInfo *pInfo = GetRootInfo( nNodeIdx );1337 UPnpCDSRootInfo *pInfo = GetRootInfo( nNodeIdx, pRequest->m_sContainerID ); 1330 1338 1331 1339 if (pInfo == NULL) 1332 1340 return; -
mythtv/libs/libmythupnp/upnpcds.h
diff --git a/mythtv/libs/libmythupnp/upnpcds.h b/mythtv/libs/libmythupnp/upnpcds.h index ed3b094..9e79655 100644
a b class UPNP_PUBLIC UPnpCDSExtension 167 167 168 168 protected: 169 169 170 virtual bool IsOurContainerPrefix(QString sContainerID) { return false; }; 171 virtual QString RemoveContainerPrefix(QString sContainerID) { return sContainerID; }; 170 172 QString RemoveToken ( const QString &sToken, const QString &sStr, int num ); 171 173 172 174 virtual UPnpCDSExtensionResults *ProcessRoot ( UPnpCDSRequest *pRequest, … … class UPNP_PUBLIC UPnpCDSExtension 202 204 203 205 // ------------------------------------------------------------------ 204 206 205 virtual UPnpCDSRootInfo *GetRootInfo ( int nIdx ) = 0;207 virtual UPnpCDSRootInfo *GetRootInfo ( int nIdx, QString sContainerId = "" ) = 0; 206 208 virtual int GetRootCount ( ) = 0; 207 209 virtual QString GetTableName ( QString sColumn ) = 0; 208 210 virtual QString GetItemListSQL( QString sColumn = "" ) = 0; -
mythtv/programs/mythbackend/mediaserver.cpp
diff --git a/mythtv/programs/mythbackend/mediaserver.cpp b/mythtv/programs/mythbackend/mediaserver.cpp index 14891ca..9e53476 100644
a b 16 16 #include "upnpcdstv.h" 17 17 #include "upnpcdsmusic.h" 18 18 #include "upnpcdsvideo.h" 19 #include "upnpcdsmusicalbum.h" 19 20 20 21 #include <QScriptEngine> 21 22 #include <QNetworkProxy> … … void MediaServer::Init(bool bIsMaster, bool bDisableUPnp /* = false */) 230 231 "MediaServer::Registering UPnpCDSVideo Extension"); 231 232 232 233 RegisterExtension(new UPnpCDSVideo()); 234 235 LOG(VB_UPNP, LOG_INFO, 236 "MediaServer::Registering UPnpCDSMusicAlbum Extension"); 237 238 RegisterExtension(new UPnpCDSMusicAlbum()); 233 239 } 234 240 235 241 #if 0 -
mythtv/programs/mythbackend/mythbackend.pro
diff --git a/mythtv/programs/mythbackend/mythbackend.pro b/mythtv/programs/mythbackend/mythbackend.pro index ac8742c..bcc168c 100644
a b HEADERS += playbacksock.h scheduler.h server.h housekeeper.h backendutil.h 23 23 HEADERS += upnpcdstv.h upnpcdsmusic.h upnpcdsvideo.h mediaserver.h 24 24 HEADERS += internetContent.h main_helpers.h backendcontext.h 25 25 HEADERS += httpconfig.h mythsettings.h commandlineparser.h 26 HEADERS += upnpcdsmusicalbum.h audioConverterL16.h 26 27 27 28 HEADERS += serviceHosts/mythServiceHost.h serviceHosts/guideServiceHost.h 28 29 HEADERS += serviceHosts/contentServiceHost.h serviceHosts/dvrServiceHost.h … … SOURCES += housekeeper.cpp backendutil.cpp 37 38 SOURCES += upnpcdstv.cpp upnpcdsmusic.cpp upnpcdsvideo.cpp mediaserver.cpp 38 39 SOURCES += internetContent.cpp main_helpers.cpp backendcontext.cpp 39 40 SOURCES += httpconfig.cpp mythsettings.cpp commandlineparser.cpp 41 SOURCES += upnpcdsmusicalbum.cpp audioConverterL16.cpp 40 42 41 43 SOURCES += services/myth.cpp services/guide.cpp services/content.cpp 42 44 SOURCES += services/dvr.cpp services/channel.cpp services/video.cpp -
mythtv/programs/mythbackend/upnpcdsmusic.cpp
diff --git a/mythtv/programs/mythbackend/upnpcdsmusic.cpp b/mythtv/programs/mythbackend/upnpcdsmusic.cpp index 57cc570..55d8ef9 100644
a b 15 15 #include "upnpcdsmusic.h" 16 16 #include "httprequest.h" 17 17 #include "mythcorecontext.h" 18 #include "../../mythtv/libs/libmythupnp/audioConverterL16.h" 18 19 19 20 /* 20 21 Music Music … … UPnpCDSRootInfo UPnpCDSMusic::g_RootNodes[] = 109 110 110 111 int UPnpCDSMusic::g_nRootCount = sizeof( g_RootNodes ) / sizeof( UPnpCDSRootInfo ); 111 112 113 UPnpCDSRootInfo UPnpCDSMusic::g_Containers[] = 114 { 115 { 116 "musicalbum", 117 "song.album_id", 118 "SELECT song_id as id, " 119 "name, " 120 "1 as children " 121 "FROM music_songs song " 122 "%1 " 123 "ORDER BY name", 124 "WHERE album_id = :KEY" 125 } 126 }; 127 128 int UPnpCDSMusic::g_nContainersCount = sizeof( g_Containers ) / sizeof( UPnpCDSRootInfo ); 129 112 130 ///////////////////////////////////////////////////////////////////////////// 113 131 // 114 132 ///////////////////////////////////////////////////////////////////////////// 115 133 116 UPnpCDSRootInfo *UPnpCDSMusic::GetRootInfo( int nIdx )134 UPnpCDSRootInfo *UPnpCDSMusic::GetRootInfo( int nIdx, QString sContainerId ) 117 135 { 136 for(int i=0; i<g_nContainersCount; i++) 137 { 138 if (sContainerId.startsWith(g_Containers[ i ].title)) 139 { 140 return &(g_Containers[ i ]); 141 } 142 } 143 118 144 if ((nIdx >=0 ) && ( nIdx < g_nRootCount )) 119 145 return &(g_RootNodes[ nIdx ]); 120 146 … … void UPnpCDSMusic::BuildItemQuery( MSqlQuery &query, const QStringMap &mapParams 175 201 // 176 202 ///////////////////////////////////////////////////////////////////////////// 177 203 204 bool UPnpCDSMusic::IsOurContainerPrefix(QString sContainerId) 205 { 206 for(int i=0; i<g_nContainersCount; i++) 207 { 208 if (sContainerId.startsWith(g_Containers[ i ].title)) 209 { 210 return true; 211 } 212 } 213 return false; 214 } 215 216 ///////////////////////////////////////////////////////////////////////////// 217 // 218 ///////////////////////////////////////////////////////////////////////////// 219 220 QString UPnpCDSMusic::RemoveContainerPrefix(QString sContainerId) 221 { 222 for(int i=0; i<g_nContainersCount; i++) 223 { 224 if (sContainerId.startsWith(g_Containers[ i ].title)) 225 { 226 return sContainerId.right(sContainerId.length() - strlen(g_Containers[ i ].title)); 227 } 228 } 229 return sContainerId; 230 } 231 232 ///////////////////////////////////////////////////////////////////////////// 233 // 234 ///////////////////////////////////////////////////////////////////////////// 235 178 236 bool UPnpCDSMusic::IsBrowseRequestForUs( UPnpCDSRequest *pRequest ) 179 237 { 180 238 // ---------------------------------------------------------------------- … … bool UPnpCDSMusic::IsBrowseRequestForUs( UPnpCDSRequest *pRequest ) 182 240 // ---------------------------------------------------------------------- 183 241 184 242 // Xbox360 compatibility code. 185 243 /* 186 244 if (pRequest->m_eClient == CDS_ClientXBox && 187 245 pRequest->m_sContainerID == "7") 188 246 { … … bool UPnpCDSMusic::IsBrowseRequestForUs( UPnpCDSRequest *pRequest ) 193 251 194 252 return true; 195 253 } 196 254 */ 197 255 if ((pRequest->m_sObjectId.isEmpty()) && 198 256 (!pRequest->m_sContainerID.isEmpty())) 199 257 pRequest->m_sObjectId = pRequest->m_sContainerID; … … bool UPnpCDSMusic::IsSearchRequestForUs( UPnpCDSRequest *pRequest ) 215 273 // ---------------------------------------------------------------------- 216 274 217 275 // XBox 360 compatibility code 218 276 /* 219 277 if (pRequest->m_eClient == CDS_ClientXBox && 220 278 pRequest->m_sContainerID == "7") 221 279 { … … bool UPnpCDSMusic::IsSearchRequestForUs( UPnpCDSRequest *pRequest ) 227 285 228 286 return true; 229 287 } 230 288 */ 231 289 if (pRequest->m_sContainerID == "4") 232 290 { 233 291 pRequest->m_sObjectId = "Music"; … … void UPnpCDSMusic::AddItem( const UPnpCDSRequest *pRequest, 308 366 .arg( sServerIp ) 309 367 .arg( sPort ); 310 368 311 QString sURIParams = QString( " ?Id=%1" )369 QString sURIParams = QString( "\%3FId\%3D%1" ) 312 370 .arg( nId ); 313 371 314 372 … … void UPnpCDSMusic::AddItem( const UPnpCDSRequest *pRequest, 365 423 QString sURI = QString( "%1GetMusic%2").arg( sURIBase ) 366 424 .arg( sURIParams ); 367 425 368 Resource *pRes = pItem->AddResource( sProtocol, sURI ); 426 // DLNA.ORG_OP=00 indicates ranges/seeking not supported 427 // DLNA.ORG_CI=1 indicates transcoded 428 QString sTranscodedProtocol = QString( "http-get:*:audio/L16:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=00;DLNA.ORG_CI=1" ); 429 QString sTranscodedURI = sURI + "%26format=L16"; 369 430 370 nLength /= 1000; 431 Resource *pRes = pItem->AddResource( sProtocol, sURI ); 432 Resource *pTranscodedRes = pItem->AddResource( sTranscodedProtocol, sTranscodedURI ); 371 433 372 434 QString sDur; 373 435 374 sDur.sprintf("%02d:%02d:%02d", 375 (nLength / 3600) % 24, 376 (nLength / 60) % 60, 377 nLength % 60); 378 379 pRes->AddAttribute( "duration" , sDur ); 436 sDur.sprintf("%02d:%02d:%02d.%03d", 437 (nLength / 3600000) % 24, 438 (nLength / 60000) % 60, 439 (nLength / 1000) % 60, 440 nLength % 1000 441 ); 442 443 pRes->AddAttribute ( "duration", sDur ); 444 pTranscodedRes->AddAttribute ( "duration", sDur ); 445 446 pTranscodedRes->AddAttribute ( "bitrate", QString("%1").arg( 447 AudioConverterL16::GetBitsPerSample() * 448 AudioConverterL16::GetOutputRate() * 449 AudioConverterL16::GetOutputChannels() 450 )); 451 pTranscodedRes->AddAttribute ( "bitsPerSample", QString("%1").arg( AudioConverterL16::GetBitsPerSample() ) ); 452 pTranscodedRes->AddAttribute ( "sampleFrequency", QString("%1").arg( AudioConverterL16::GetOutputRate() ) ); 453 pTranscodedRes->AddAttribute ( "nrAudioChannels", QString("%1").arg( AudioConverterL16::GetOutputChannels() ) ); 454 380 455 } 381 456 382 457 // vim:ts=4:sw=4:ai:et:si:sts=4 -
mythtv/programs/mythbackend/upnpcdsmusic.h
diff --git a/mythtv/programs/mythbackend/upnpcdsmusic.h b/mythtv/programs/mythbackend/upnpcdsmusic.h index 5cc28a6..815d38e 100644
a b class UPnpCDSMusic : public UPnpCDSExtension 24 24 private: 25 25 26 26 static UPnpCDSRootInfo g_RootNodes[]; 27 static UPnpCDSRootInfo g_Containers[]; 27 28 static int g_nRootCount; 29 static int g_nContainersCount; 28 30 29 31 protected: 30 32 33 virtual bool IsOurContainerPrefix ( QString sContainerId ); 34 virtual QString RemoveContainerPrefix ( QString sContainerId ); 31 35 virtual bool IsBrowseRequestForUs( UPnpCDSRequest *pRequest ); 32 36 virtual bool IsSearchRequestForUs( UPnpCDSRequest *pRequest ); 33 37 34 virtual UPnpCDSRootInfo *GetRootInfo ( int nIdx);38 virtual UPnpCDSRootInfo *GetRootInfo ( int nIdx, QString sContainerId = "" ); 35 39 virtual int GetRootCount ( ); 36 40 virtual QString GetTableName ( QString sColumn ); 37 41 virtual QString GetItemListSQL( QString sColumn = "" ); -
new file mythtv/programs/mythbackend/upnpcdsmusicalbum.cpp
diff --git a/mythtv/programs/mythbackend/upnpcdsmusicalbum.cpp b/mythtv/programs/mythbackend/upnpcdsmusicalbum.cpp new file mode 100644 index 0000000..256ea14
- + 1 ////////////////////////////////////////////////////////////////////////////// 2 // Program Name: upnpcdsmusicalbum.cpp 3 // 4 // Purpose - uPnp Content Directory Extension for Music Albums 5 // 6 // Created By : Joe Bryant Created On : Jul. 29, 2011 7 // Modified By : Modified On: 8 // 9 ////////////////////////////////////////////////////////////////////////////// 10 11 #include <climits> 12 13 #include <QFileInfo> 14 15 #include "upnpcdsmusicalbum.h" 16 #include "httprequest.h" 17 #include "mythcorecontext.h" 18 19 20 UPnpCDSRootInfo UPnpCDSMusicAlbum::g_RootNodes[] = 21 { 22 23 { "By Album", 24 "album.album_id", 25 "SELECT a.album_id as id, " 26 "a.album_name as name, " 27 "count( song.album_id ) as children " 28 "FROM music_songs song join music_albums a on a.album_id = song.album_id " 29 "%1 " 30 "GROUP BY a.album_id " 31 "ORDER BY a.album_name", 32 "WHERE album.album_id=:KEY" }, 33 34 }; 35 36 int UPnpCDSMusicAlbum::g_nRootCount = sizeof( g_RootNodes ) / sizeof( UPnpCDSRootInfo ); 37 38 ///////////////////////////////////////////////////////////////////////////// 39 // 40 ///////////////////////////////////////////////////////////////////////////// 41 42 UPnpCDSRootInfo *UPnpCDSMusicAlbum::GetRootInfo( int nIdx, QString sContainerId ) 43 { 44 if ((nIdx >=0 ) && ( nIdx < g_nRootCount )) 45 return &(g_RootNodes[ nIdx ]); 46 47 return NULL; 48 } 49 50 ///////////////////////////////////////////////////////////////////////////// 51 // 52 ///////////////////////////////////////////////////////////////////////////// 53 54 int UPnpCDSMusicAlbum::GetRootCount() 55 { 56 return g_nRootCount; 57 } 58 59 ///////////////////////////////////////////////////////////////////////////// 60 // 61 ///////////////////////////////////////////////////////////////////////////// 62 63 QString UPnpCDSMusicAlbum::GetTableName( QString sColumn ) 64 { 65 return "music_albums album"; 66 } 67 68 ///////////////////////////////////////////////////////////////////////////// 69 // 70 ///////////////////////////////////////////////////////////////////////////// 71 72 QString UPnpCDSMusicAlbum::GetItemListSQL( QString /* sColumn */ ) 73 { 74 return "select album_id as intid, artist_name as artist, album_name as album " \ 75 "from music_albums " \ 76 "join music_artists " \ 77 "on music_albums.artist_id = music_artists.artist_id"; 78 } 79 80 ///////////////////////////////////////////////////////////////////////////// 81 // 82 ///////////////////////////////////////////////////////////////////////////// 83 84 void UPnpCDSMusicAlbum::BuildItemQuery( MSqlQuery &query, const QStringMap &mapParams ) 85 { 86 int nId = mapParams[ "Id" ].toInt(); 87 88 QString sSQL = QString( "%1 WHERE music_albums.album_id=:ID " ) 89 .arg( GetItemListSQL() ); 90 91 query.prepare( sSQL ); 92 93 query.bindValue( ":ID", (int)nId ); 94 } 95 96 97 ///////////////////////////////////////////////////////////////////////////// 98 // 99 ///////////////////////////////////////////////////////////////////////////// 100 101 void UPnpCDSMusicAlbum::AddItem( const UPnpCDSRequest *pRequest, 102 const QString &sObjectId, 103 UPnpCDSExtensionResults *pResults, 104 bool bAddRef, 105 MSqlQuery &query ) 106 { 107 QString sName; 108 109 QString sId = QString( "musicalbum" ) + query.value( 0 ).toString(); 110 QString sArtist = query.value( 1 ).toString(); 111 QString sAlbum = query.value( 2 ).toString(); 112 113 CDSObject *pItem = CDSObject::CreateMusicAlbum( QString( sId ), sAlbum, QString( "1" ), NULL); 114 115 pItem->SetPropValue( "artist", sArtist ); 116 117 pResults->Add( pItem ); 118 119 } 120 121 // vim:ts=4:sw=4:ai:et:si:sts=4 -
new file mythtv/programs/mythbackend/upnpcdsmusicalbum.h
diff --git a/mythtv/programs/mythbackend/upnpcdsmusicalbum.h b/mythtv/programs/mythbackend/upnpcdsmusicalbum.h new file mode 100644 index 0000000..e80fd86
- + 1 ////////////////////////////////////////////////////////////////////////////// 2 // Program Name: upnpcdsmusicalbum.h 3 // 4 // Purpose - uPnp Content Directory Extension for Music Albums 5 // 6 // Created By : Joe Bryant Created On : Jul. 29, 2011 7 // Modified By : Modified On: 8 // 9 ////////////////////////////////////////////////////////////////////////////// 10 11 #ifndef UPnpCDSMusicAlbum_H_ 12 #define UPnpCDSMusicAlbum_H_ 13 14 #include <QString> 15 16 #include "upnpcds.h" 17 18 ////////////////////////////////////////////////////////////////////////////// 19 // 20 ////////////////////////////////////////////////////////////////////////////// 21 class MSqlQuery; 22 class UPnpCDSMusicAlbum : public UPnpCDSExtension 23 { 24 private: 25 26 static UPnpCDSRootInfo g_RootNodes[]; 27 static int g_nRootCount; 28 29 protected: 30 31 virtual UPnpCDSRootInfo *GetRootInfo (int nIdx, QString sContainerId = ""); 32 virtual int GetRootCount ( ); 33 virtual QString GetTableName ( QString sColumn ); 34 virtual QString GetItemListSQL( QString sColumn = "" ); 35 36 virtual void BuildItemQuery( MSqlQuery &query, 37 const QStringMap &mapParams ); 38 39 virtual void AddItem( const UPnpCDSRequest *pRequest, 40 const QString &sObjectId, 41 UPnpCDSExtensionResults *pResults, 42 bool bAddRef, 43 MSqlQuery &query ); 44 public: 45 46 UPnpCDSMusicAlbum( ) : UPnpCDSExtension( "Music", "Music", 47 "object.container.album.musicAlbum" ) 48 { 49 } 50 51 virtual ~UPnpCDSMusicAlbum() {} 52 }; 53 54 #endif -
mythtv/programs/mythbackend/upnpcdstv.cpp
diff --git a/mythtv/programs/mythbackend/upnpcdstv.cpp b/mythtv/programs/mythbackend/upnpcdstv.cpp index a29c2ff..6da5e66 100644
a b int UPnpCDSTv::g_nRootCount = sizeof( g_RootNodes ) / sizeof( UPnpCDSRootInfo ); 111 111 // 112 112 ///////////////////////////////////////////////////////////////////////////// 113 113 114 UPnpCDSRootInfo *UPnpCDSTv::GetRootInfo( int nIdx )114 UPnpCDSRootInfo *UPnpCDSTv::GetRootInfo( int nIdx, QString sContainerId ) 115 115 { 116 116 if ((nIdx >=0 ) && ( nIdx < g_nRootCount )) 117 117 return &(g_RootNodes[ nIdx ]); -
mythtv/programs/mythbackend/upnpcdstv.h
diff --git a/mythtv/programs/mythbackend/upnpcdstv.h b/mythtv/programs/mythbackend/upnpcdstv.h index 9b4e620..b3970cc 100644
a b class UPnpCDSTv : public UPnpCDSExtension 32 32 virtual bool IsBrowseRequestForUs( UPnpCDSRequest *pRequest ); 33 33 virtual bool IsSearchRequestForUs( UPnpCDSRequest *pRequest ); 34 34 35 virtual UPnpCDSRootInfo *GetRootInfo ( int nIdx);35 virtual UPnpCDSRootInfo *GetRootInfo ( int nIdx, QString sContainerId = "" ); 36 36 virtual int GetRootCount ( ); 37 37 virtual QString GetTableName ( QString sColumn ); 38 38 virtual QString GetItemListSQL( QString sColumn = "" ); -
mythtv/programs/mythbackend/upnpcdsvideo.cpp
diff --git a/mythtv/programs/mythbackend/upnpcdsvideo.cpp b/mythtv/programs/mythbackend/upnpcdsvideo.cpp index b0b12b3..88637a0 100644
a b int UPnpCDSVideo::g_nRootCount = 1; 44 44 // 45 45 ///////////////////////////////////////////////////////////////////////////// 46 46 47 UPnpCDSRootInfo *UPnpCDSVideo::GetRootInfo( int nIdx )47 UPnpCDSRootInfo *UPnpCDSVideo::GetRootInfo( int nIdx, QString sContainerId ) 48 48 { 49 49 if ((nIdx >=0 ) && ( nIdx < g_nRootCount )) 50 50 return &(g_RootNodes[ nIdx ]); -
mythtv/programs/mythbackend/upnpcdsvideo.h
diff --git a/mythtv/programs/mythbackend/upnpcdsvideo.h b/mythtv/programs/mythbackend/upnpcdsvideo.h index 56e98d5..b547035 100644
a b class UPnpCDSVideo : public UPnpCDSExtension 37 37 38 38 virtual int GetDistinctCount( UPnpCDSRootInfo *pInfo ); 39 39 40 virtual UPnpCDSRootInfo *GetRootInfo ( int nIdx);40 virtual UPnpCDSRootInfo *GetRootInfo ( int nIdx, QString sContainerId = "" ); 41 41 virtual int GetRootCount ( ); 42 42 virtual QString GetTableName ( QString sColumn ); 43 43 virtual QString GetItemListSQL( QString sColumn = ""); -
mythtv/programs/programs-libs.pro
diff --git a/mythtv/programs/programs-libs.pro b/mythtv/programs/programs-libs.pro index d6c936c..4633c76 100644
a b LIBS += -L../../external/FFmpeg/libavutil 13 13 LIBS += -L../../external/FFmpeg/libavcodec 14 14 LIBS += -L../../external/FFmpeg/libavformat 15 15 LIBS += -L../../external/FFmpeg/libswscale 16 LIBS += -L../../external/FFmpeg/libavdevice 16 17 LIBS += -L../../libs/libmythbase 17 18 LIBS += -L../../libs/libmythui 18 19 LIBS += -L../../libs/libmythupnp … … LIBS += -lmythtv-$$LIBVERSION 24 25 LIBS += -lmythswscale 25 26 LIBS += -lmythavformat 26 27 LIBS += -lmythavcodec 28 LIBS += -lmythavdevice 27 29 LIBS += -lmythavutil 28 30 LIBS += -lmythupnp-$$LIBVERSION 29 31 LIBS += -lmythbase-$$LIBVERSION