00001 #include <unistd.h>
00002
00003 #include <QRegion>
00004 #include <QBitArray>
00005 #include <QVector>
00006
00007 #include "mhi.h"
00008 #include "interactivescreen.h"
00009 #include "mythpainter.h"
00010 #include "mythimage.h"
00011 #include "mythuiimage.h"
00012 #include "osd.h"
00013 #include "mythdirs.h"
00014 #include "myth_imgconvert.h"
00015 #include "mythlogging.h"
00016
00017 static bool ft_loaded = false;
00018 static FT_Library ft_library;
00019
00020 #define FONT_WIDTHRES 48
00021 #define FONT_HEIGHTRES 72
00022 #define FONT_TO_USE "FreeSans.ttf"
00023
00024 #define SCALED_X(arg1) (int)(((float)arg1 * m_xScale) + 0.5f)
00025 #define SCALED_Y(arg1) (int)(((float)arg1 * m_yScale) + 0.5f)
00026
00027
00028 const unsigned kTuneQuietly = 1U<<0;
00029 const unsigned kTuneKeepApp = 1U<<1;
00030 const unsigned kTuneCarId = 1U<<2;
00031 const unsigned kTuneCarReset = 1U<<3;
00032 const unsigned kTuneBcastDisa = 1U<<4;
00033
00034
00035
00036 const unsigned kTuneKeepChnl = 1U<<16;
00037
00041 class MHIImageData
00042 {
00043 public:
00044 QImage m_image;
00045 int m_x;
00046 int m_y;
00047 };
00048
00049
00050 #define NBI_VERSION_UNSET 257
00051
00052 MHIContext::MHIContext(InteractiveTV *parent)
00053 : m_parent(parent), m_dsmcc(NULL),
00054 m_keyProfile(0),
00055 m_engine(NULL), m_stop(false),
00056 m_updated(false),
00057 m_displayWidth(StdDisplayWidth), m_displayHeight(StdDisplayHeight),
00058 m_face_loaded(false), m_engineThread(NULL), m_currentChannel(-1),
00059 m_currentStream(-1), m_isLive(false), m_currentSource(-1),
00060 m_audioTag(-1), m_videoTag(-1),
00061 m_lastNbiVersion(NBI_VERSION_UNSET),
00062 m_videoRect(0, 0, StdDisplayWidth, StdDisplayHeight),
00063 m_displayRect(0, 0, StdDisplayWidth, StdDisplayHeight)
00064 {
00065 m_xScale = (float)m_displayWidth / (float)MHIContext::StdDisplayWidth;
00066 m_yScale = (float)m_displayHeight / (float)MHIContext::StdDisplayHeight;
00067
00068 if (!ft_loaded)
00069 {
00070 FT_Error error = FT_Init_FreeType(&ft_library);
00071 if (!error)
00072 ft_loaded = true;
00073 }
00074
00075 if (ft_loaded)
00076 {
00077
00078 if (LoadFont(FONT_TO_USE))
00079 m_face_loaded = true;
00080 }
00081 }
00082
00083
00084 bool MHIContext::LoadFont(QString name)
00085 {
00086 QString fullnameA = GetConfDir() + "/" + name;
00087 QByteArray fnameA = fullnameA.toAscii();
00088 FT_Error errorA = FT_New_Face(ft_library, fnameA.constData(), 0, &m_face);
00089 if (!errorA)
00090 return true;
00091
00092 QString fullnameB = GetFontsDir() + name;
00093 QByteArray fnameB = fullnameB.toAscii();
00094 FT_Error errorB = FT_New_Face(ft_library, fnameB.constData(), 0, &m_face);
00095 if (!errorB)
00096 return true;
00097
00098 QString fullnameC = GetShareDir() + "themes/" + name;
00099 QByteArray fnameC = fullnameC.toAscii();
00100 FT_Error errorC = FT_New_Face(ft_library, fnameC.constData(), 0, &m_face);
00101 if (!errorC)
00102 return true;
00103
00104 QString fullnameD = name;
00105 QByteArray fnameD = fullnameD.toAscii();
00106 FT_Error errorD = FT_New_Face(ft_library, fnameD.constData(), 0, &m_face);
00107 if (!errorD)
00108 return true;
00109
00110 LOG(VB_GENERAL, LOG_ERR, QString("Unable to find font: %1").arg(name));
00111 return false;
00112 }
00113
00114 MHIContext::~MHIContext()
00115 {
00116 StopEngine();
00117 delete(m_engine);
00118 delete(m_dsmcc);
00119 if (m_face_loaded) FT_Done_Face(m_face);
00120
00121 ClearDisplay();
00122 ClearQueue();
00123 }
00124
00125 void MHIContext::ClearDisplay(void)
00126 {
00127 list<MHIImageData*>::iterator it = m_display.begin();
00128 for (; it != m_display.end(); ++it)
00129 delete *it;
00130 m_display.clear();
00131 }
00132
00133 void MHIContext::ClearQueue(void)
00134 {
00135 MythDeque<DSMCCPacket*>::iterator it = m_dsmccQueue.begin();
00136 for (; it != m_dsmccQueue.end(); ++it)
00137 delete *it;
00138 m_dsmccQueue.clear();
00139 }
00140
00141
00142 void MHIContext::StopEngine(void)
00143 {
00144 if (NULL == m_engineThread)
00145 return;
00146
00147 m_runLock.lock();
00148 m_stop = true;
00149 m_engine_wait.wakeAll();
00150 m_runLock.unlock();
00151
00152 m_engineThread->wait();
00153 delete m_engineThread;
00154 m_engineThread = NULL;
00155 }
00156
00157
00158
00159 void MHIContext::Restart(int chanid, int sourceid, bool isLive)
00160 {
00161 int tuneinfo = m_tuneinfo.isEmpty() ? 0 : m_tuneinfo.takeFirst();
00162
00163 LOG(VB_MHEG, LOG_INFO,
00164 QString("[mhi] Restart ch=%1 source=%2 live=%3 tuneinfo=0x%4")
00165 .arg(chanid).arg(sourceid).arg(isLive).arg(tuneinfo,0,16));
00166
00167 m_currentSource = sourceid;
00168 m_currentStream = chanid ? chanid : -1;
00169 if (!(tuneinfo & kTuneKeepChnl))
00170 m_currentChannel = m_currentStream;
00171
00172 if (tuneinfo & kTuneKeepApp)
00173 {
00174
00175
00176
00177
00178 if (!m_dsmcc)
00179 m_dsmcc = new Dsmcc();
00180 {
00181 QMutexLocker locker(&m_dsmccLock);
00182 if (tuneinfo & kTuneCarReset)
00183 m_dsmcc->Reset();
00184 ClearQueue();
00185 }
00186
00187 if (tuneinfo & (kTuneCarReset|kTuneCarId))
00188 m_engine->EngineEvent(10);
00189 }
00190 else
00191 {
00192 StopEngine();
00193
00194 m_audioTag = -1;
00195 m_videoTag = -1;
00196
00197 if (!m_dsmcc)
00198 m_dsmcc = new Dsmcc();
00199
00200 {
00201 QMutexLocker locker(&m_dsmccLock);
00202 m_dsmcc->Reset();
00203 ClearQueue();
00204 }
00205
00206 {
00207 QMutexLocker locker(&m_keyLock);
00208 m_keyQueue.clear();
00209 }
00210
00211 if (!m_engine)
00212 m_engine = MHCreateEngine(this);
00213
00214 m_engine->SetBooting();
00215 ClearDisplay();
00216 m_updated = true;
00217 m_stop = false;
00218 m_isLive = isLive;
00219
00220
00221 m_engineThread = new MThread("MHEG", this);
00222 m_engineThread->start();
00223 }
00224 }
00225
00226 void MHIContext::run(void)
00227 {
00228 QMutexLocker locker(&m_runLock);
00229
00230 QTime t; t.start();
00231 while (!m_stop)
00232 {
00233 locker.unlock();
00234
00235 int toWait;
00236
00237 int key = 0;
00238 do
00239 {
00240 (void)NetworkBootRequested();
00241 ProcessDSMCCQueue();
00242 {
00243 QMutexLocker locker(&m_keyLock);
00244 key = m_keyQueue.dequeue();
00245 }
00246
00247 if (key != 0)
00248 m_engine->GenerateUserAction(key);
00249
00250
00251 toWait = m_engine->RunAll();
00252 if (toWait < 0)
00253 return;
00254 } while (key != 0);
00255
00256 toWait = (toWait > 1000 || toWait <= 0) ? 1000 : toWait;
00257
00258 locker.relock();
00259 if (!m_stop && (toWait > 0))
00260 m_engine_wait.wait(locker.mutex(), toWait);
00261 }
00262 }
00263
00264
00265 void MHIContext::ProcessDSMCCQueue(void)
00266 {
00267 DSMCCPacket *packet = NULL;
00268 do
00269 {
00270 {
00271 QMutexLocker locker(&m_dsmccLock);
00272 packet = m_dsmccQueue.dequeue();
00273 }
00274
00275 if (packet)
00276 {
00277 m_dsmcc->ProcessSection(
00278 packet->m_data, packet->m_length,
00279 packet->m_componentTag, packet->m_carouselId,
00280 packet->m_dataBroadcastId);
00281
00282 delete packet;
00283 }
00284 } while (packet);
00285 }
00286
00287 void MHIContext::QueueDSMCCPacket(
00288 unsigned char *data, int length, int componentTag,
00289 unsigned carouselId, int dataBroadcastId)
00290 {
00291 unsigned char *dataCopy =
00292 (unsigned char*) malloc(length * sizeof(unsigned char));
00293
00294 if (dataCopy == NULL)
00295 return;
00296
00297 memcpy(dataCopy, data, length*sizeof(unsigned char));
00298 {
00299 QMutexLocker locker(&m_dsmccLock);
00300 m_dsmccQueue.enqueue(new DSMCCPacket(dataCopy, length,
00301 componentTag, carouselId,
00302 dataBroadcastId));
00303 }
00304 QMutexLocker locker(&m_runLock);
00305 m_engine_wait.wakeAll();
00306 }
00307
00308
00309 void MHIContext::SetNetBootInfo(const unsigned char *data, uint length)
00310 {
00311 if (length < 2)
00312 return;
00313
00314 LOG(VB_MHEG, LOG_INFO, QString("[mhi] SetNetBootInfo version %1 mode %2 len %3")
00315 .arg(data[0]).arg(data[1]).arg(length));
00316
00317 QMutexLocker locker(&m_dsmccLock);
00318
00319 m_nbiData.resize(0);
00320 m_nbiData.reserve(length);
00321 m_nbiData.insert(m_nbiData.begin(), data, data+length);
00322
00323
00324 if (m_lastNbiVersion == NBI_VERSION_UNSET)
00325 m_lastNbiVersion = data[0];
00326 else
00327 {
00328 locker.unlock();
00329 QMutexLocker locker2(&m_runLock);
00330 m_engine_wait.wakeAll();
00331 }
00332 }
00333
00334 void MHIContext::NetworkBootRequested(void)
00335 {
00336 QMutexLocker locker(&m_dsmccLock);
00337 if (m_nbiData.size() >= 2 && m_nbiData[0] != m_lastNbiVersion)
00338 {
00339 m_lastNbiVersion = m_nbiData[0];
00340 switch (m_nbiData[1])
00341 {
00342 case 1:
00343 m_dsmcc->Reset();
00344 m_engine->SetBooting();
00345 ClearDisplay();
00346 m_updated = true;
00347 break;
00348 case 2:
00349 m_engine->EngineEvent(9);
00350 break;
00351 default:
00352 LOG(VB_MHEG, LOG_INFO, QString("[mhi] Unknown NetworkBoot type %1")
00353 .arg(m_nbiData[1]));
00354 break;
00355 }
00356 }
00357 }
00358
00359
00360 bool MHIContext::CheckCarouselObject(QString objectPath)
00361 {
00362 QStringList path = objectPath.split(QChar('/'), QString::SkipEmptyParts);
00363 QByteArray result;
00364 int res = m_dsmcc->GetDSMCCObject(path, result);
00365 return res == 0;
00366 }
00367
00368
00369 bool MHIContext::GetCarouselData(QString objectPath, QByteArray &result)
00370 {
00371
00372
00373 QStringList path = objectPath.split(QChar('/'), QString::SkipEmptyParts);
00374
00375
00376
00377
00378 QMutexLocker locker(&m_runLock);
00379 QTime t; t.start();
00380 while (!m_stop)
00381 {
00382 locker.unlock();
00383
00384 int res = m_dsmcc->GetDSMCCObject(path, result);
00385 if (res == 0)
00386 return true;
00387 else if (res < 0)
00388 return false;
00389 else if (t.elapsed() > 60000)
00390 return false;
00391
00392
00393
00394
00395 ProcessDSMCCQueue();
00396
00397 locker.relock();
00398 if (!m_stop)
00399 m_engine_wait.wait(locker.mutex(), 1000);
00400 }
00401 return false;
00402 }
00403
00404
00405
00406
00407 bool MHIContext::OfferKey(QString key)
00408 {
00409 int action = 0;
00410 QMutexLocker locker(&m_keyLock);
00411
00412
00413
00414
00415
00416 if (key == ACTION_UP)
00417 {
00418 if (m_keyProfile == 4 || m_keyProfile == 5 ||
00419 m_keyProfile == 14 || m_keyProfile == 15)
00420 action = 1;
00421 }
00422 else if (key == ACTION_DOWN)
00423 {
00424 if (m_keyProfile == 4 || m_keyProfile == 5 ||
00425 m_keyProfile == 14 || m_keyProfile == 15)
00426 action = 2;
00427 }
00428 else if (key == ACTION_LEFT)
00429 {
00430 if (m_keyProfile == 4 || m_keyProfile == 5 ||
00431 m_keyProfile == 14 || m_keyProfile == 15)
00432 action = 3;
00433 }
00434 else if (key == ACTION_RIGHT)
00435 {
00436 if (m_keyProfile == 4 || m_keyProfile == 5 ||
00437 m_keyProfile == 14 || m_keyProfile == 15)
00438 action = 4;
00439 }
00440 else if (key == ACTION_0 || key == ACTION_1 || key == ACTION_2 ||
00441 key == ACTION_3 || key == ACTION_4 || key == ACTION_5 ||
00442 key == ACTION_6 || key == ACTION_7 || key == ACTION_8 ||
00443 key == ACTION_9)
00444 {
00445 if (m_keyProfile == 4 || m_keyProfile == 14)
00446 action = key.toInt() + 5;
00447 }
00448 else if (key == ACTION_SELECT)
00449 {
00450 if (m_keyProfile == 4 || m_keyProfile == 5 ||
00451 m_keyProfile == 14 || m_keyProfile == 15)
00452 action = 15;
00453 }
00454 else if (key == ACTION_TEXTEXIT)
00455 action = 16;
00456 else if (key == ACTION_MENURED)
00457 action = 100;
00458 else if (key == ACTION_MENUGREEN)
00459 action = 101;
00460 else if (key == ACTION_MENUYELLOW)
00461 action = 102;
00462 else if (key == ACTION_MENUBLUE)
00463 action = 103;
00464 else if (key == ACTION_MENUTEXT)
00465 action = m_keyProfile > 12 ? 105 : 104;
00466 else if (key == ACTION_MENUEPG)
00467 action = m_keyProfile > 12 ? 300 : 0;
00468
00469 if (action != 0)
00470 {
00471 m_keyQueue.enqueue(action);
00472 LOG(VB_GENERAL, LOG_INFO, QString("Adding MHEG key %1:%2:%3").arg(key)
00473 .arg(action).arg(m_keyQueue.size()));
00474 locker.unlock();
00475 QMutexLocker locker2(&m_runLock);
00476 m_engine_wait.wakeAll();
00477 return true;
00478 }
00479
00480 return false;
00481 }
00482
00483 void MHIContext::Reinit(const QRect &display)
00484 {
00485 m_displayWidth = display.width();
00486 m_displayHeight = display.height();
00487 m_xScale = (float)m_displayWidth / (float)MHIContext::StdDisplayWidth;
00488 m_yScale = (float)m_displayHeight / (float)MHIContext::StdDisplayHeight;
00489 m_videoRect = QRect(QPoint(0,0), display.size());
00490 m_displayRect = display;
00491 }
00492
00493 void MHIContext::SetInputRegister(int num)
00494 {
00495 QMutexLocker locker(&m_keyLock);
00496 m_keyQueue.clear();
00497 m_keyProfile = num;
00498 }
00499
00500
00501
00502 void MHIContext::UpdateOSD(InteractiveScreen *osdWindow,
00503 MythPainter *osdPainter)
00504 {
00505 if (!osdWindow || !osdPainter)
00506 return;
00507
00508 QMutexLocker locker(&m_display_lock);
00509 m_updated = false;
00510 osdWindow->DeleteAllChildren();
00511
00512 list<MHIImageData*>::iterator it = m_display.begin();
00513 for (int count = 0; it != m_display.end(); ++it, count++)
00514 {
00515 MHIImageData *data = *it;
00516 MythImage* image = osdPainter->GetFormatImage();
00517 if (!image)
00518 continue;
00519
00520 image->Assign(data->m_image);
00521 MythUIImage *uiimage = new MythUIImage(osdWindow, QString("itv%1")
00522 .arg(count));
00523 if (uiimage)
00524 {
00525 uiimage->SetImage(image);
00526 uiimage->SetArea(MythRect(data->m_x, data->m_y,
00527 data->m_image.width(), data->m_image.height()));
00528 }
00529 }
00530 osdWindow->OptimiseDisplayedArea();
00531
00532 osdWindow->SetVisible(true);
00533 }
00534
00535 void MHIContext::GetInitialStreams(int &audioTag, int &videoTag)
00536 {
00537 audioTag = m_audioTag;
00538 videoTag = m_videoTag;
00539 }
00540
00541
00542
00543
00544
00545 void MHIContext::RequireRedraw(const QRegion &)
00546 {
00547 m_display_lock.lock();
00548 ClearDisplay();
00549 m_display_lock.unlock();
00550
00551 m_engine->DrawDisplay(QRegion(0, 0, StdDisplayWidth, StdDisplayHeight));
00552 m_updated = true;
00553 }
00554
00555 void MHIContext::AddToDisplay(const QImage &image, int x, int y)
00556 {
00557 MHIImageData *data = new MHIImageData;
00558 int dispx = x + m_displayRect.left();
00559 int dispy = y + m_displayRect.top();
00560
00561 data->m_image = image;
00562 data->m_x = dispx;
00563 data->m_y = dispy;
00564 QMutexLocker locker(&m_display_lock);
00565 m_display.push_back(data);
00566 }
00567
00568
00569
00570
00571
00572
00573
00574
00575 void MHIContext::DrawVideo(const QRect &videoRect, const QRect &dispRect)
00576 {
00577
00578 if (m_parent->GetNVP())
00579 {
00580 QRect vidRect(SCALED_X(videoRect.x()),
00581 SCALED_Y(videoRect.y()),
00582 SCALED_X(videoRect.width()),
00583 SCALED_Y(videoRect.height()));
00584 if (m_videoRect != vidRect)
00585 {
00586 m_parent->GetNVP()->SetVideoResize(vidRect.translated(m_displayRect.topLeft()));
00587 m_videoRect = vidRect;
00588 }
00589 }
00590
00591 QMutexLocker locker(&m_display_lock);
00592 QRect displayRect(SCALED_X(dispRect.x()),
00593 SCALED_Y(dispRect.y()),
00594 SCALED_X(dispRect.width()),
00595 SCALED_Y(dispRect.height()));
00596
00597 list<MHIImageData*>::iterator it = m_display.begin();
00598 for (; it != m_display.end(); ++it)
00599 {
00600 MHIImageData *data = *it;
00601 QRect imageRect(data->m_x, data->m_y,
00602 data->m_image.width(), data->m_image.height());
00603 if (displayRect.intersects(imageRect))
00604 {
00605
00606 it = m_display.erase(it);
00607
00608 QVector<QRect> rects =
00609 (QRegion(imageRect) - QRegion(displayRect)).rects();
00610 for (uint j = 0; j < (uint)rects.size(); j++)
00611 {
00612 QRect &rect = rects[j];
00613 QImage image =
00614 data->m_image.copy(rect.x()-data->m_x, rect.y()-data->m_y,
00615 rect.width(), rect.height());
00616 MHIImageData *newData = new MHIImageData;
00617 newData->m_image = image;
00618 newData->m_x = rect.x();
00619 newData->m_y = rect.y();
00620 m_display.insert(it, newData);
00621 ++it;
00622 }
00623 delete data;
00624 }
00625 }
00626 }
00627
00628
00629
00630
00631
00632
00633
00634
00635 int MHIContext::GetChannelIndex(const QString &str)
00636 {
00637 int nResult = -1;
00638
00639 do if (str.startsWith("dvb://"))
00640 {
00641 QStringList list = str.mid(6).split('.');
00642 MSqlQuery query(MSqlQuery::InitCon());
00643 if (list.size() != 3)
00644 break;
00645
00646
00647 bool ok;
00648 int netID = list[0].toInt(&ok, 16);
00649 if (!ok)
00650 break;
00651 int serviceID = list[2].toInt(&ok, 16);
00652 if (!ok)
00653 break;
00654
00655 if (list[1].isEmpty())
00656 {
00657 query.prepare(
00658 "SELECT chanid "
00659 "FROM channel, dtv_multiplex "
00660 "WHERE networkid = :NETID AND"
00661 " channel.mplexid = dtv_multiplex.mplexid AND "
00662 " serviceid = :SERVICEID AND "
00663 " channel.sourceid = :SOURCEID");
00664 }
00665 else
00666 {
00667 int transportID = list[1].toInt(&ok, 16);
00668 if (!ok)
00669 break;
00670 query.prepare(
00671 "SELECT chanid "
00672 "FROM channel, dtv_multiplex "
00673 "WHERE networkid = :NETID AND"
00674 " channel.mplexid = dtv_multiplex.mplexid AND "
00675 " serviceid = :SERVICEID AND "
00676 " transportid = :TRANSID AND "
00677 " channel.sourceid = :SOURCEID");
00678 query.bindValue(":TRANSID", transportID);
00679 }
00680 query.bindValue(":NETID", netID);
00681 query.bindValue(":SERVICEID", serviceID);
00682 query.bindValue(":SOURCEID", m_currentSource);
00683 if (query.exec() && query.isActive() && query.next())
00684 nResult = query.value(0).toInt();
00685 }
00686 else if (str.startsWith("rec://svc/lcn/"))
00687 {
00688
00689 bool ok;
00690 int channelNo = str.mid(14).toInt(&ok);
00691 if (!ok)
00692 break;
00693 MSqlQuery query(MSqlQuery::InitCon());
00694 query.prepare("SELECT chanid "
00695 "FROM channel "
00696 "WHERE channum = :CHAN AND "
00697 " channel.sourceid = :SOURCEID");
00698 query.bindValue(":CHAN", channelNo);
00699 query.bindValue(":SOURCEID", m_currentSource);
00700 if (query.exec() && query.isActive() && query.next())
00701 nResult = query.value(0).toInt();
00702 }
00703 else if (str == "rec://svc/cur")
00704 nResult = m_currentStream;
00705 else if (str == "rec://svc/def")
00706 nResult = m_currentChannel;
00707 else if (str.startsWith("rec://"))
00708 ;
00709 while(0);
00710
00711 LOG(VB_MHEG, LOG_INFO, QString("[mhi] GetChannelIndex %1 => %2")
00712 .arg(str).arg(nResult));
00713 return nResult;
00714 }
00715
00716
00717 bool MHIContext::GetServiceInfo(int channelId, int &netId, int &origNetId,
00718 int &transportId, int &serviceId)
00719 {
00720 LOG(VB_MHEG, LOG_INFO, QString("[mhi] GetServiceInfo %1").arg(channelId));
00721 MSqlQuery query(MSqlQuery::InitCon());
00722 query.prepare("SELECT networkid, transportid, serviceid "
00723 "FROM channel, dtv_multiplex "
00724 "WHERE chanid = :CHANID AND "
00725 " channel.mplexid = dtv_multiplex.mplexid");
00726 query.bindValue(":CHANID", channelId);
00727 if (query.exec() && query.isActive() && query.next())
00728 {
00729 netId = query.value(0).toInt();
00730 origNetId = netId;
00731 transportId = query.value(1).toInt();
00732 serviceId = query.value(2).toInt();
00733 return true;
00734 }
00735 else return false;
00736 }
00737
00738 bool MHIContext::TuneTo(int channel, int tuneinfo)
00739 {
00740 LOG(VB_MHEG, LOG_INFO, QString("[mhi] TuneTo %1 0x%2")
00741 .arg(channel).arg(tuneinfo,0,16));
00742
00743 if (!m_isLive)
00744 return false;
00745
00746 m_tuneinfo.append(tuneinfo);
00747
00748
00749 MythEvent me(QString("NETWORK_CONTROL CHANID %1").arg(channel));
00750 gCoreContext->dispatch(me);
00751
00752 QMutexLocker locker(&m_dsmccLock);
00753 m_lastNbiVersion = NBI_VERSION_UNSET;
00754 m_nbiData.resize(0);
00755 return true;
00756 }
00757
00758
00759 bool MHIContext::BeginAudio(const QString &stream, int tag)
00760 {
00761 LOG(VB_MHEG, LOG_INFO, QString("[mhi] BeginAudio %1 %2").arg(stream).arg(tag));
00762
00763 int chan = GetChannelIndex(stream);
00764 if (chan < 0)
00765 return false;
00766
00767 if (chan != m_currentStream)
00768 {
00769
00770
00771
00772 m_currentStream = chan;
00773 m_audioTag = tag;
00774 return TuneTo(chan, kTuneKeepChnl|kTuneQuietly|kTuneKeepApp);
00775 }
00776
00777 if (tag < 0)
00778 return true;
00779 else if (m_parent->GetNVP())
00780 return m_parent->GetNVP()->SetAudioByComponentTag(tag);
00781 else
00782 return false;
00783 }
00784
00785
00786 void MHIContext::StopAudio(void)
00787 {
00788
00789 }
00790
00791
00792 bool MHIContext::BeginVideo(const QString &stream, int tag)
00793 {
00794 LOG(VB_MHEG, LOG_INFO, QString("[mhi] BeginVideo %1 %2").arg(stream).arg(tag));
00795
00796 int chan = GetChannelIndex(stream);
00797 if (chan < 0)
00798 return false;
00799 if (chan != m_currentStream)
00800 {
00801
00802 m_currentStream = chan;
00803 m_videoTag = tag;
00804 return TuneTo(chan, kTuneKeepChnl|kTuneQuietly|kTuneKeepApp);
00805 }
00806 if (tag < 0)
00807 return true;
00808 else if (m_parent->GetNVP())
00809 return m_parent->GetNVP()->SetVideoByComponentTag(tag);
00810
00811 return false;
00812 }
00813
00814
00815 void MHIContext::StopVideo(void)
00816 {
00817
00818 }
00819
00820
00821 MHDLADisplay *MHIContext::CreateDynamicLineArt(
00822 bool isBoxed, MHRgba lineColour, MHRgba fillColour)
00823 {
00824 return new MHIDLA(this, isBoxed, lineColour, fillColour);
00825 }
00826
00827
00828 MHTextDisplay *MHIContext::CreateText()
00829 {
00830 return new MHIText(this);
00831 }
00832
00833
00834 MHBitmapDisplay *MHIContext::CreateBitmap(bool tiled)
00835 {
00836 return new MHIBitmap(this, tiled);
00837 }
00838
00839
00840 void MHIContext::DrawRect(int xPos, int yPos, int width, int height,
00841 MHRgba colour)
00842 {
00843 if (colour.alpha() == 0 || height == 0 || width == 0)
00844 return;
00845
00846 QRgb qColour = qRgba(colour.red(), colour.green(),
00847 colour.blue(), colour.alpha());
00848
00849 int scaledWidth = SCALED_X(width);
00850 int scaledHeight = SCALED_Y(height);
00851 QImage qImage(scaledWidth, scaledHeight, QImage::Format_ARGB32);
00852
00853 for (int i = 0; i < scaledHeight; i++)
00854 {
00855 for (int j = 0; j < scaledWidth; j++)
00856 {
00857 qImage.setPixel(j, i, qColour);
00858 }
00859 }
00860
00861 AddToDisplay(qImage, SCALED_X(xPos), SCALED_Y(yPos));
00862 }
00863
00864
00865
00866
00867
00868
00869 void MHIContext::DrawImage(int x, int y, const QRect &clipRect,
00870 const QImage &qImage)
00871 {
00872 if (qImage.isNull())
00873 return;
00874
00875 QRect imageRect(x, y, qImage.width(), qImage.height());
00876 QRect displayRect = QRect(clipRect.x(), clipRect.y(),
00877 clipRect.width(), clipRect.height()) & imageRect;
00878
00879 if (displayRect == imageRect)
00880 {
00881 QImage q_scaled =
00882 qImage.scaled(
00883 SCALED_X(displayRect.width()),
00884 SCALED_Y(displayRect.height()),
00885 Qt::IgnoreAspectRatio,
00886 Qt::SmoothTransformation);
00887 AddToDisplay(q_scaled.convertToFormat(QImage::Format_ARGB32),
00888 SCALED_X(x), SCALED_Y(y));
00889 }
00890 else if (!displayRect.isEmpty())
00891 {
00892 QImage clipped = qImage.convertToFormat(QImage::Format_ARGB32)
00893 .copy(displayRect.x() - x, displayRect.y() - y,
00894 displayRect.width(), displayRect.height());
00895 QImage q_scaled =
00896 clipped.scaled(
00897 SCALED_X(displayRect.width()),
00898 SCALED_Y(displayRect.height()),
00899 Qt::IgnoreAspectRatio,
00900 Qt::SmoothTransformation);
00901 AddToDisplay(q_scaled,
00902 SCALED_X(displayRect.x()),
00903 SCALED_Y(displayRect.y()));
00904 }
00905
00906 }
00907
00908
00909
00910 void MHIContext::DrawBackground(const QRegion ®)
00911 {
00912 if (reg.isEmpty())
00913 return;
00914
00915 QRect bounds = reg.boundingRect();
00916 DrawRect(bounds.x(), bounds.y(), bounds.width(), bounds.height(),
00917 MHRgba(0, 0, 0, 255));
00918 }
00919
00920 MHIText::MHIText(MHIContext *parent): m_parent(parent)
00921 {
00922 m_fontsize = 12;
00923 m_fontItalic = false;
00924 m_fontBold = false;
00925 m_width = 0;
00926 m_height = 0;
00927 }
00928
00929 void MHIText::Draw(int x, int y)
00930 {
00931 m_parent->DrawImage(x, y, QRect(x, y, m_width, m_height), m_image);
00932 }
00933
00934 void MHIText::SetSize(int width, int height)
00935 {
00936 m_width = width;
00937 m_height = height;
00938 }
00939
00940 void MHIText::SetFont(int size, bool isBold, bool isItalic)
00941 {
00942 m_fontsize = size;
00943 m_fontItalic = isItalic;
00944 m_fontBold = isBold;
00945
00946
00947 }
00948
00949
00950
00951
00952
00953
00954
00955
00956 QRect MHIText::GetBounds(const QString &str, int &strLen, int maxSize)
00957 {
00958 if (!m_parent->IsFaceLoaded())
00959 return QRect(0,0,0,0);
00960
00961 FT_Face face = m_parent->GetFontFace();
00962 FT_Error error = FT_Set_Char_Size(face, 0, m_fontsize*64,
00963 FONT_WIDTHRES, FONT_HEIGHTRES);
00964 if (error)
00965 return QRect(0,0,0,0);
00966
00967 FT_GlyphSlot slot = face->glyph;
00968
00969 int maxAscent = 0, maxDescent = 0, width = 0;
00970 FT_Bool useKerning = FT_HAS_KERNING(face);
00971 FT_UInt previous = 0;
00972
00973 for (int n = 0; n < strLen; n++)
00974 {
00975 QChar ch = str[n];
00976 FT_UInt glyphIndex = FT_Get_Char_Index(face, ch.unicode());
00977 int kerning = 0;
00978
00979 if (useKerning && previous != 0 && glyphIndex != 0)
00980 {
00981 FT_Vector delta;
00982 FT_Get_Kerning(face, previous, glyphIndex,
00983 FT_KERNING_DEFAULT, &delta);
00984 kerning = delta.x;
00985 }
00986
00987 error = FT_Load_Glyph(face, glyphIndex, 0);
00988
00989 if (error)
00990 continue;
00991
00992 if (maxSize >= 0)
00993 {
00994 if ((width + slot->advance.x + kerning + (1<<6)-1) >> 6 > maxSize)
00995 {
00996
00997 strLen = n;
00998 break;
00999 }
01000 }
01001
01002 int descent = slot->metrics.height - slot->metrics.horiBearingY;
01003
01004 if (slot->metrics.horiBearingY > maxAscent)
01005 maxAscent = slot->metrics.horiBearingY;
01006
01007 if (descent > maxDescent)
01008 maxDescent = descent;
01009
01010 width += slot->advance.x + kerning;
01011 previous = glyphIndex;
01012 }
01013
01014 maxAscent = (maxAscent + (1<<6)-1) >> 6;
01015 maxDescent = (maxDescent + (1<<6)-1) >> 6;
01016
01017 return QRect(0, -maxAscent,
01018 (width+(1<<6)-1) >> 6, maxAscent + maxDescent);
01019 }
01020
01021
01022
01023
01024
01025 void MHIText::Clear(void)
01026 {
01027 m_image = QImage(m_width, m_height, QImage::Format_ARGB32);
01028
01029 for (int i = 0; i < m_height; i++)
01030 {
01031 for (int j = 0; j < m_width; j++)
01032 {
01033 m_image.setPixel(j, i, qRgba(0, 0, 0, 0));
01034 }
01035 }
01036 }
01037
01038
01039
01040
01041 void MHIText::AddText(int x, int y, const QString &str, MHRgba colour)
01042 {
01043 if (!m_parent->IsFaceLoaded()) return;
01044 FT_Face face = m_parent->GetFontFace();
01045 FT_GlyphSlot slot = face->glyph;
01046 FT_Error error = FT_Set_Char_Size(face, 0, m_fontsize*64,
01047 FONT_WIDTHRES, FONT_HEIGHTRES);
01048
01049
01050
01051 int posX = x << 6;
01052 int pixelY = y;
01053 FT_Bool useKerning = FT_HAS_KERNING(face);
01054 FT_UInt previous = 0;
01055
01056 int len = str.length();
01057 for (int n = 0; n < len; n++)
01058 {
01059
01060 QChar ch = str[n];
01061 FT_UInt glyphIndex = FT_Get_Char_Index(face, ch.unicode());
01062 if (useKerning && previous != 0 && glyphIndex != 0)
01063 {
01064 FT_Vector delta;
01065 FT_Get_Kerning(face, previous, glyphIndex,
01066 FT_KERNING_DEFAULT, &delta);
01067 posX += delta.x;
01068 }
01069 error = FT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER);
01070
01071 if (error)
01072 continue;
01073
01074 if (slot->format != FT_GLYPH_FORMAT_BITMAP)
01075 continue;
01076
01077 if ((enum FT_Pixel_Mode_)slot->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
01078 continue;
01079
01080 unsigned char *source = slot->bitmap.buffer;
01081
01082 int baseX = ((posX + (1 << 5)) >> 6) + slot->bitmap_left;
01083 int baseY = pixelY - slot->bitmap_top;
01084
01085 for (int i = 0; i < slot->bitmap.rows; i++)
01086 {
01087 for (int j = 0; j < slot->bitmap.width; j++)
01088 {
01089 int greyLevel = source[j];
01090
01091
01092 int red = colour.red();
01093 int green = colour.green();
01094 int blue = colour.blue();
01095 int alpha = colour.alpha() *
01096 greyLevel / slot->bitmap.num_grays;
01097 int xBit = j + baseX;
01098 int yBit = i + baseY;
01099
01100
01101
01102
01103 if (xBit >= 0 && xBit < m_width &&
01104 yBit >= 0 && yBit < m_height)
01105 {
01106 m_image.setPixel(xBit, yBit,
01107 qRgba(red, green, blue, alpha));
01108 }
01109 }
01110 source += slot->bitmap.pitch;
01111 }
01112 posX += slot->advance.x;
01113 previous = glyphIndex;
01114 }
01115 }
01116
01117
01118 void MHIDLA::DrawRect(int x, int y, int width, int height, MHRgba colour)
01119 {
01120 QRgb qColour = qRgba(colour.red(), colour.green(),
01121 colour.blue(), colour.alpha());
01122
01123
01124 if (x < 0)
01125 {
01126 width += x;
01127 x = 0;
01128 }
01129
01130 if (y < 0)
01131 {
01132 height += y;
01133 y = 0;
01134 }
01135
01136 if (width <= 0 || height <= 0)
01137 return;
01138
01139 int imageWidth = m_image.width(), imageHeight = m_image.height();
01140 if (x+width > imageWidth)
01141 width = imageWidth - x;
01142
01143 if (y+height > imageHeight)
01144 height = imageHeight - y;
01145
01146 if (width <= 0 || height <= 0)
01147 return;
01148
01149 for (int i = 0; i < height; i++)
01150 {
01151 for (int j = 0; j < width; j++)
01152 {
01153 m_image.setPixel(x+j, y+i, qColour);
01154 }
01155 }
01156 }
01157
01158
01159 void MHIDLA::Clear()
01160 {
01161 if (m_width == 0 || m_height == 0)
01162 {
01163 m_image = QImage();
01164 return;
01165 }
01166 m_image = QImage(m_width, m_height, QImage::Format_ARGB32);
01167
01168 DrawRect(0, 0, m_width, m_height, MHRgba(0, 0, 0, 0));
01169 }
01170
01171 void MHIDLA::Draw(int x, int y)
01172 {
01173 QRect bounds(x, y, m_width, m_height);
01174 if (m_boxed && m_lineWidth != 0)
01175 {
01176
01177
01178 m_parent->DrawRect(x, y, m_width,
01179 m_lineWidth, m_boxLineColour);
01180
01181 m_parent->DrawRect(x, y + m_height - m_lineWidth,
01182 m_width, m_lineWidth, m_boxLineColour);
01183
01184 m_parent->DrawRect(x, y + m_lineWidth,
01185 m_lineWidth, m_height - m_lineWidth * 2,
01186 m_boxLineColour);
01187
01188 m_parent->DrawRect(x + m_width - m_lineWidth, y + m_lineWidth,
01189 m_lineWidth, m_height - m_lineWidth * 2,
01190 m_boxLineColour);
01191
01192
01193 bounds = QRect(bounds.x() + m_lineWidth,
01194 bounds.y() + m_lineWidth,
01195 bounds.width() - 2*m_lineWidth,
01196 bounds.height() - 2*m_lineWidth);
01197 }
01198
01199
01200 m_parent->DrawRect(x + m_lineWidth,
01201 y + m_lineWidth,
01202 m_width - m_lineWidth * 2,
01203 m_height - m_lineWidth * 2,
01204 m_boxFillColour);
01205
01206
01207 m_parent->DrawImage(x, y, bounds, m_image);
01208 }
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219 void MHIDLA::DrawLine(int x1, int y1, int x2, int y2)
01220 {
01221
01222
01223 if (abs(y2-y1) > abs(x2-x1))
01224 {
01225 if (y2 > y1)
01226 DrawLineSub(y1, x1, y2, x2, true);
01227 else
01228 DrawLineSub(y2, x2, y1, x1, true);
01229 }
01230 else
01231 {
01232 if (x2 > x1)
01233 DrawLineSub(x1, y1, x2, y2, false);
01234 else
01235 DrawLineSub(x2, y2, x1, y1, false);
01236 }
01237 }
01238
01239
01240
01241 void MHIDLA::DrawLineSub(int x1, int y1, int x2, int y2, bool swapped)
01242 {
01243 QRgb colour = qRgba(m_lineColour.red(), m_lineColour.green(),
01244 m_lineColour.blue(), m_lineColour.alpha());
01245 int dx = x2-x1, dy = abs(y2-y1);
01246 int yStep = y2 >= y1 ? 1 : -1;
01247
01248
01249 int error2 = dx/2;
01250 for (int k = 0; k < m_lineWidth/2; k++)
01251 {
01252 y1--;
01253 y2--;
01254 error2 += dy;
01255 if (error2*2 > dx)
01256 {
01257 error2 -= dx;
01258 x1 += yStep;
01259 x2 += yStep;
01260 }
01261 }
01262
01263 int y = y1;
01264 int error = dx/2;
01265 for (int x = x1; x <= x2; x++)
01266 {
01267 error2 = dx/2;
01268 int j = 0;
01269
01270
01271 for (int i = 0; i < m_lineWidth; i++)
01272 {
01273 if (swapped)
01274 {
01275 if (x+j >= 0 && y+i >= 0 && y+i < m_width && x+j < m_height)
01276 m_image.setPixel(y+i, x+j, colour);
01277 }
01278 else
01279 {
01280 if (x+j >= 0 && y+i >= 0 && x+j < m_width && y+i < m_height)
01281 m_image.setPixel(x+j, y+i, colour);
01282 }
01283 error2 += dy;
01284 if (error2*2 > dx)
01285 {
01286 error2 -= dx;
01287 j -= yStep;
01288 if (i < m_lineWidth-1)
01289 {
01290
01291 if (swapped)
01292 {
01293 if (x+j >= 0 && y+i >= 0 && y+i < m_width && x+j < m_height)
01294 m_image.setPixel(y+i, x+j, colour);
01295 }
01296 else
01297 {
01298 if (x+j >= 0 && y+i >= 0 && x+j < m_width && y+i < m_height)
01299 m_image.setPixel(x+j, y+i, colour);
01300 }
01301 }
01302 }
01303 }
01304 error += dy;
01305 if (error*2 > dx)
01306 {
01307 error -= dx;
01308 y += yStep;
01309 }
01310 }
01311 }
01312
01313
01314 void MHIDLA::DrawBorderedRectangle(int x, int y, int width, int height)
01315 {
01316 if (m_lineWidth != 0)
01317 {
01318
01319 DrawRect(x, y, width, m_lineWidth,
01320 m_lineColour);
01321
01322 DrawRect(x, y + height - m_lineWidth,
01323 width, m_lineWidth,
01324 m_lineColour);
01325
01326 DrawRect(x, y + m_lineWidth,
01327 m_lineWidth, height - m_lineWidth * 2,
01328 m_lineColour);
01329
01330 DrawRect(x + width - m_lineWidth, y + m_lineWidth,
01331 m_lineWidth, height - m_lineWidth * 2,
01332 m_lineColour);
01333
01334
01335 DrawRect(x + m_lineWidth, y + m_lineWidth,
01336 width - m_lineWidth * 2, height - m_lineWidth * 2,
01337 m_fillColour);
01338 }
01339 else
01340 {
01341 DrawRect(x, y, width, height, m_fillColour);
01342 }
01343 }
01344
01345
01346 void MHIDLA::DrawOval(int x, int y, int width, int height)
01347 {
01348
01349 }
01350
01351
01352 void MHIDLA::DrawArcSector(int x, int y, int width, int height,
01353 int start, int arc, bool isSector)
01354 {
01355
01356 }
01357
01358
01359
01360
01361
01362 typedef struct { int yBottom, yTop, xBottom; float slope; } lineSeg;
01363
01364 void MHIDLA::DrawPoly(bool isFilled, int nPoints, const int *xArray, const int *yArray)
01365 {
01366 if (nPoints < 2)
01367 return;
01368
01369 if (isFilled)
01370 {
01371 QVector <lineSeg> lineArray(nPoints);
01372 int nLines = 0;
01373
01374
01375
01376 int lastX = xArray[nPoints-1];
01377 int lastY = yArray[nPoints-1];
01378 int yMin = lastY, yMax = lastY;
01379 for (int k = 0; k < nPoints; k++)
01380 {
01381 int thisX = xArray[k];
01382 int thisY = yArray[k];
01383 if (lastY != thisY)
01384 {
01385 if (lastY > thisY)
01386 {
01387 lineArray[nLines].yBottom = thisY;
01388 lineArray[nLines].yTop = lastY;
01389 lineArray[nLines].xBottom = thisX;
01390 }
01391 else
01392 {
01393 lineArray[nLines].yBottom = lastY;
01394 lineArray[nLines].yTop = thisY;
01395 lineArray[nLines].xBottom = lastX;
01396 }
01397 lineArray[nLines++].slope =
01398 (float)(thisX-lastX) / (float)(thisY-lastY);
01399 }
01400 if (thisY < yMin)
01401 yMin = thisY;
01402 if (thisY > yMax)
01403 yMax = thisY;
01404 lastX = thisX;
01405 lastY = thisY;
01406 }
01407
01408
01409
01410
01411 QRgb fillColour = qRgba(m_fillColour.red(), m_fillColour.green(),
01412 m_fillColour.blue(), m_fillColour.alpha());
01413 for (int y = yMin; y < yMax; y++)
01414 {
01415 int crossings = 0, xMin = 0, xMax = 0;
01416 for (int l = 0; l < nLines; l++)
01417 {
01418 if (y >= lineArray[l].yBottom && y < lineArray[l].yTop)
01419 {
01420 int x = (int)round((float)(y - lineArray[l].yBottom) *
01421 lineArray[l].slope) + lineArray[l].xBottom;
01422 if (crossings == 0 || x < xMin)
01423 xMin = x;
01424 if (crossings == 0 || x > xMax)
01425 xMax = x;
01426 crossings++;
01427 }
01428 }
01429 if (crossings == 2)
01430 {
01431 for (int x = xMin; x <= xMax; x++)
01432 m_image.setPixel(x, y, fillColour);
01433 }
01434 }
01435
01436
01437 int lastXpoint = xArray[nPoints-1];
01438 int lastYpoint = yArray[nPoints-1];
01439 for (int i = 0; i < nPoints; i++)
01440 {
01441 DrawLine(xArray[i], yArray[i], lastXpoint, lastYpoint);
01442 lastXpoint = xArray[i];
01443 lastYpoint = yArray[i];
01444 }
01445 }
01446 else
01447 {
01448 for (int i = 1; i < nPoints; i++)
01449 {
01450 DrawLine(xArray[i], yArray[i], xArray[i-1], yArray[i-1]);
01451 }
01452 }
01453 }
01454
01455
01456 void MHIBitmap::Draw(int x, int y, QRect rect, bool tiled)
01457 {
01458 if (tiled)
01459 {
01460 if (m_image.width() == 0 || m_image.height() == 0)
01461 return;
01462
01463
01464 QImage tiledImage = QImage(rect.width(), rect.height(),
01465 QImage::Format_ARGB32);
01466
01467 for (int i = 0; i < rect.width(); i++)
01468 {
01469 for (int j = 0; j < rect.height(); j++)
01470 {
01471 tiledImage.setPixel(i, j, m_image.pixel(i % m_image.width(), j % m_image.height()));
01472 }
01473 }
01474 m_parent->DrawImage(rect.x(), rect.y(), rect, tiledImage);
01475 }
01476 else
01477 {
01478 m_parent->DrawImage(x, y, rect, m_image);
01479 }
01480 }
01481
01482
01483 void MHIBitmap::CreateFromPNG(const unsigned char *data, int length)
01484 {
01485 m_image = QImage();
01486
01487 if (!m_image.loadFromData(data, length, "PNG"))
01488 {
01489 m_image = QImage();
01490 return;
01491 }
01492
01493
01494 m_opaque = ! m_image.hasAlphaChannel();
01495 }
01496
01497
01498
01499
01500
01501
01502 void MHIBitmap::CreateFromMPEG(const unsigned char *data, int length)
01503 {
01504 AVCodecContext *c = NULL;
01505 AVFrame *picture = NULL;
01506 AVPacket pkt;
01507 uint8_t *buff = NULL;
01508 int gotPicture = 0, len;
01509 m_image = QImage();
01510
01511
01512 AVCodec *codec = avcodec_find_decoder(CODEC_ID_MPEG2VIDEO);
01513 if (!codec)
01514 return;
01515
01516 c = avcodec_alloc_context3(NULL);
01517 picture = avcodec_alloc_frame();
01518
01519 if (avcodec_open2(c, codec, NULL) < 0)
01520 goto Close;
01521
01522
01523 if (av_new_packet(&pkt, length) < 0)
01524 goto Close;
01525
01526 memcpy(pkt.data, data, length);
01527 buff = pkt.data;
01528
01529 while (pkt.size > 0 && ! gotPicture)
01530 {
01531 len = avcodec_decode_video2(c, picture, &gotPicture, &pkt);
01532 if (len < 0)
01533 goto Close;
01534 pkt.data += len;
01535 pkt.size -= len;
01536 }
01537
01538 if (!gotPicture)
01539 {
01540 pkt.data = NULL;
01541 pkt.size = 0;
01542
01543 if (avcodec_decode_video2(c, picture, &gotPicture, &pkt) < 0)
01544 goto Close;
01545 }
01546
01547 if (gotPicture)
01548 {
01549 int nContentWidth = c->width;
01550 int nContentHeight = c->height;
01551 m_image = QImage(nContentWidth, nContentHeight, QImage::Format_ARGB32);
01552 m_opaque = true;
01553
01554 AVPicture retbuf;
01555 memset(&retbuf, 0, sizeof(AVPicture));
01556
01557 int bufflen = nContentWidth * nContentHeight * 3;
01558 unsigned char *outputbuf = new unsigned char[bufflen];
01559
01560 avpicture_fill(&retbuf, outputbuf, PIX_FMT_RGB24,
01561 nContentWidth, nContentHeight);
01562
01563 myth_sws_img_convert(
01564 &retbuf, PIX_FMT_RGB24, (AVPicture*)picture, c->pix_fmt,
01565 nContentWidth, nContentHeight);
01566
01567 uint8_t * buf = outputbuf;
01568
01569
01570
01571 for (int i = 0; i < nContentHeight; i++)
01572 {
01573 for (int j = 0; j < nContentWidth; j++)
01574 {
01575 int red = *buf++;
01576 int green = *buf++;
01577 int blue = *buf++;
01578 m_image.setPixel(j, i, qRgb(red, green, blue));
01579 }
01580 }
01581 delete [] outputbuf;
01582 }
01583
01584 Close:
01585 pkt.data = buff;
01586 av_free_packet(&pkt);
01587 avcodec_close(c);
01588 av_free(c);
01589 av_free(picture);
01590 }
01591
01592
01593 void MHIBitmap::ScaleImage(int newWidth, int newHeight)
01594 {
01595 if (m_image.isNull())
01596 return;
01597
01598 if (newWidth == m_image.width() && newHeight == m_image.height())
01599 return;
01600
01601 if (newWidth <= 0 || newHeight <= 0)
01602 {
01603 m_image = QImage();
01604 return;
01605 }
01606
01607 m_image = m_image.scaled(newWidth, newHeight,
01608 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
01609 }
01610
01611