diff options
Diffstat (limited to 'src/plugins/android/src')
30 files changed, 1188 insertions, 831 deletions
diff --git a/src/plugins/android/src/common/common.pri b/src/plugins/android/src/common/common.pri index f99dad507..9c741bd94 100644 --- a/src/plugins/android/src/common/common.pri +++ b/src/plugins/android/src/common/common.pri @@ -2,9 +2,8 @@ INCLUDEPATH += $$PWD HEADERS += \ $$PWD/qandroidvideooutput.h \ - $$PWD/qandroidvideorendercontrol.h \ $$PWD/qandroidmultimediautils.h SOURCES += \ - $$PWD/qandroidvideorendercontrol.cpp \ + $$PWD/qandroidvideooutput.cpp \ $$PWD/qandroidmultimediautils.cpp diff --git a/src/plugins/android/src/common/qandroidmultimediautils.cpp b/src/plugins/android/src/common/qandroidmultimediautils.cpp index a41cc0e1b..6b6ca3255 100644 --- a/src/plugins/android/src/common/qandroidmultimediautils.cpp +++ b/src/plugins/android/src/common/qandroidmultimediautils.cpp @@ -68,563 +68,39 @@ bool qt_sizeLessThan(const QSize &s1, const QSize &s2) return s1.width() * s1.height() < s2.width() * s2.height(); } -// Pre-computed Y coefficients for all possible y values (0-255). Stored as fixed-point (16:16) -// Y = 1.164 * (y - 16) -static const int coefficientsY[256] = { - -593888, -555746, -517604, -479462, -441320, -403178, -365036, -326894, -288752, -250610, - -212468, -174326, -136184, -98042, -59900, -21758, 16384, 54526, 92668, 130810, - 168952, 207094, 245236, 283378, 321520, 359662, 397804, 435946, 474088, 512230, - 550372, 588514, 626656, 664798, 702940, 741082, 779224, 817366, 855508, 893650, - 931792, 969934, 1008076, 1046218, 1084360, 1122502, 1160644, 1198786, 1236928, 1275070, - 1313212, 1351354, 1389496, 1427638, 1465780, 1503922, 1542064, 1580206, 1618348, 1656490, - 1694632, 1732774, 1770916, 1809058, 1847200, 1885342, 1923484, 1961626, 1999768, 2037910, - 2076052, 2114194, 2152336, 2190478, 2228620, 2266762, 2304904, 2343046, 2381188, 2419330, - 2457472, 2495614, 2533756, 2571898, 2610040, 2648182, 2686324, 2724466, 2762608, 2800750, - 2838892, 2877034, 2915176, 2953318, 2991460, 3029602, 3067744, 3105886, 3144028, 3182170, - 3220312, 3258454, 3296596, 3334738, 3372880, 3411022, 3449164, 3487306, 3525448, 3563590, - 3601732, 3639874, 3678016, 3716158, 3754300, 3792442, 3830584, 3868726, 3906868, 3945010, - 3983152, 4021294, 4059436, 4097578, 4135720, 4173862, 4212004, 4250146, 4288288, 4326430, - 4364572, 4402714, 4440856, 4478998, 4517140, 4555282, 4593424, 4631566, 4669708, 4707850, - 4745992, 4784134, 4822276, 4860418, 4898560, 4936702, 4974844, 5012986, 5051128, 5089270, - 5127412, 5165554, 5203696, 5241838, 5279980, 5318122, 5356264, 5394406, 5432548, 5470690, - 5508832, 5546974, 5585116, 5623258, 5661400, 5699542, 5737684, 5775826, 5813968, 5852110, - 5890252, 5928394, 5966536, 6004678, 6042820, 6080962, 6119104, 6157246, 6195388, 6233530, - 6271672, 6309814, 6347956, 6386098, 6424240, 6462382, 6500524, 6538666, 6576808, 6614950, - 6653092, 6691234, 6729376, 6767518, 6805660, 6843802, 6881944, 6920086, 6958228, 6996370, - 7034512, 7072654, 7110796, 7148938, 7187080, 7225222, 7263364, 7301506, 7339648, 7377790, - 7415932, 7454074, 7492216, 7530358, 7568500, 7606642, 7644784, 7682926, 7721068, 7759210, - 7797352, 7835494, 7873636, 7911778, 7949920, 7988062, 8026204, 8064346, 8102488, 8140630, - 8178772, 8216914, 8255056, 8293198, 8331340, 8369482, 8407624, 8445766, 8483908, 8522050, - 8560192, 8598334, 8636476, 8674618, 8712760, 8750902, 8789044, 8827186, 8865328, 8903470, - 8941612, 8979754, 9017896, 9056038, 9094180, 9132322 -}; - -// V lookup table for the Red component. Stored as fixed-point (16:16). -// V = 1.596 * (v - 128) -static const int coefficientsRV[256] = { - -6694144, -6641846, -6589548, -6537250, -6484952, -6432654, -6380356, -6328058, -6275760, - -6223462, -6171164, -6118866, -6066568, -6014270, -5961972, -5909674, -5857376, -5805078, - -5752780, -5700482, -5648184, -5595886, -5543588, -5491290, -5438992, -5386694, -5334396, - -5282098, -5229800, -5177502, -5125204, -5072906, -5020608, -4968310, -4916012, -4863714, - -4811416, -4759118, -4706820, -4654522, -4602224, -4549926, -4497628, -4445330, -4393032, - -4340734, -4288436, -4236138, -4183840, -4131542, -4079244, -4026946, -3974648, -3922350, - -3870052, -3817754, -3765456, -3713158, -3660860, -3608562, -3556264, -3503966, -3451668, - -3399370, -3347072, -3294774, -3242476, -3190178, -3137880, -3085582, -3033284, -2980986, - -2928688, -2876390, -2824092, -2771794, -2719496, -2667198, -2614900, -2562602, -2510304, - -2458006, -2405708, -2353410, -2301112, -2248814, -2196516, -2144218, -2091920, -2039622, - -1987324, -1935026, -1882728, -1830430, -1778132, -1725834, -1673536, -1621238, -1568940, - -1516642, -1464344, -1412046, -1359748, -1307450, -1255152, -1202854, -1150556, -1098258, - -1045960, -993662, -941364, -889066, -836768, -784470, -732172, -679874, -627576, - -575278, -522980, -470682, -418384, -366086, -313788, -261490, -209192, -156894, - -104596, -52298, 0, 52298, 104596, 156894, 209192, 261490, 313788, - 366086, 418384, 470682, 522980, 575278, 627576, 679874, 732172, 784470, - 836768, 889066, 941364, 993662, 1045960, 1098258, 1150556, 1202854, 1255152, - 1307450, 1359748, 1412046, 1464344, 1516642, 1568940, 1621238, 1673536, 1725834, - 1778132, 1830430, 1882728, 1935026, 1987324, 2039622, 2091920, 2144218, 2196516, - 2248814, 2301112, 2353410, 2405708, 2458006, 2510304, 2562602, 2614900, 2667198, - 2719496, 2771794, 2824092, 2876390, 2928688, 2980986, 3033284, 3085582, 3137880, - 3190178, 3242476, 3294774, 3347072, 3399370, 3451668, 3503966, 3556264, 3608562, - 3660860, 3713158, 3765456, 3817754, 3870052, 3922350, 3974648, 4026946, 4079244, - 4131542, 4183840, 4236138, 4288436, 4340734, 4393032, 4445330, 4497628, 4549926, - 4602224, 4654522, 4706820, 4759118, 4811416, 4863714, 4916012, 4968310, 5020608, - 5072906, 5125204, 5177502, 5229800, 5282098, 5334396, 5386694, 5438992, 5491290, - 5543588, 5595886, 5648184, 5700482, 5752780, 5805078, 5857376, 5909674, 5961972, - 6014270, 6066568, 6118866, 6171164, 6223462, 6275760, 6328058, 6380356, 6432654, - 6484952, 6537250, 6589548, 6641846 -}; - -// U lookup table for the Green component. Stored as fixed-point (16:16). -// U = 0.391 * (u - 128) -static const int coefficientsGU[256] = { - 1639936, 1627124, 1614312, 1601500, 1588688, 1575876, 1563064, 1550252, 1537440, - 1524628, 1511816, 1499004, 1486192, 1473380, 1460568, 1447756, 1434944, 1422132, - 1409320, 1396508, 1383696, 1370884, 1358072, 1345260, 1332448, 1319636, 1306824, - 1294012, 1281200, 1268388, 1255576, 1242764, 1229952, 1217140, 1204328, 1191516, - 1178704, 1165892, 1153080, 1140268, 1127456, 1114644, 1101832, 1089020, 1076208, - 1063396, 1050584, 1037772, 1024960, 1012148, 999336, 986524, 973712, 960900, - 948088, 935276, 922464, 909652, 896840, 884028, 871216, 858404, 845592, - 832780, 819968, 807156, 794344, 781532, 768720, 755908, 743096, 730284, - 717472, 704660, 691848, 679036, 666224, 653412, 640600, 627788, 614976, - 602164, 589352, 576540, 563728, 550916, 538104, 525292, 512480, 499668, - 486856, 474044, 461232, 448420, 435608, 422796, 409984, 397172, 384360, - 371548, 358736, 345924, 333112, 320300, 307488, 294676, 281864, 269052, - 256240, 243428, 230616, 217804, 204992, 192180, 179368, 166556, 153744, - 140932, 128120, 115308, 102496, 89684, 76872, 64060, 51248, 38436, - 25624, 12812, 0, -12812, -25624, -38436, -51248, -64060, -76872, - -89684, -102496, -115308, -128120, -140932, -153744, -166556, -179368, -192180, - -204992, -217804, -230616, -243428, -256240, -269052, -281864, -294676, -307488, - -320300, -333112, -345924, -358736, -371548, -384360, -397172, -409984, -422796, - -435608, -448420, -461232, -474044, -486856, -499668, -512480, -525292, -538104, - -550916, -563728, -576540, -589352, -602164, -614976, -627788, -640600, -653412, - -666224, -679036, -691848, -704660, -717472, -730284, -743096, -755908, -768720, - -781532, -794344, -807156, -819968, -832780, -845592, -858404, -871216, -884028, - -896840, -909652, -922464, -935276, -948088, -960900, -973712, -986524, -999336, - -1012148, -1024960, -1037772, -1050584, -1063396, -1076208, -1089020, -1101832, -1114644, - -1127456, -1140268, -1153080, -1165892, -1178704, -1191516, -1204328, -1217140, -1229952, - -1242764, -1255576, -1268388, -1281200, -1294012, -1306824, -1319636, -1332448, -1345260, - -1358072, -1370884, -1383696, -1396508, -1409320, -1422132, -1434944, -1447756, -1460568, - -1473380, -1486192, -1499004, -1511816, -1524628, -1537440, -1550252, -1563064, -1575876, - -1588688, -1601500, -1614312, -1627124 -}; - -// V lookup table for the Green component. Stored as fixed-point (16:16). -// V = 0.813 * (v - 128) -static const int coefficientsGV[256] = { - 3409920, 3383280, 3356640, 3330000, 3303360, 3276720, 3250080, 3223440, 3196800, - 3170160, 3143520, 3116880, 3090240, 3063600, 3036960, 3010320, 2983680, 2957040, - 2930400, 2903760, 2877120, 2850480, 2823840, 2797200, 2770560, 2743920, 2717280, - 2690640, 2664000, 2637360, 2610720, 2584080, 2557440, 2530800, 2504160, 2477520, - 2450880, 2424240, 2397600, 2370960, 2344320, 2317680, 2291040, 2264400, 2237760, - 2211120, 2184480, 2157840, 2131200, 2104560, 2077920, 2051280, 2024640, 1998000, - 1971360, 1944720, 1918080, 1891440, 1864800, 1838160, 1811520, 1784880, 1758240, - 1731600, 1704960, 1678320, 1651680, 1625040, 1598400, 1571760, 1545120, 1518480, - 1491840, 1465200, 1438560, 1411920, 1385280, 1358640, 1332000, 1305360, 1278720, - 1252080, 1225440, 1198800, 1172160, 1145520, 1118880, 1092240, 1065600, 1038960, - 1012320, 985680, 959040, 932400, 905760, 879120, 852480, 825840, 799200, - 772560, 745920, 719280, 692640, 666000, 639360, 612720, 586080, 559440, - 532800, 506160, 479520, 452880, 426240, 399600, 372960, 346320, 319680, - 293040, 266400, 239760, 213120, 186480, 159840, 133200, 106560, 79920, - 53280, 26640, 0, -26640, -53280, -79920, -106560, -133200, -159840, - -186480, -213120, -239760, -266400, -293040, -319680, -346320, -372960, -399600, - -426240, -452880, -479520, -506160, -532800, -559440, -586080, -612720, -639360, - -666000, -692640, -719280, -745920, -772560, -799200, -825840, -852480, -879120, - -905760, -932400, -959040, -985680, -1012320, -1038960, -1065600, -1092240, -1118880, - -1145520, -1172160, -1198800, -1225440, -1252080, -1278720, -1305360, -1332000, -1358640, - -1385280, -1411920, -1438560, -1465200, -1491840, -1518480, -1545120, -1571760, -1598400, - -1625040, -1651680, -1678320, -1704960, -1731600, -1758240, -1784880, -1811520, -1838160, - -1864800, -1891440, -1918080, -1944720, -1971360, -1998000, -2024640, -2051280, -2077920, - -2104560, -2131200, -2157840, -2184480, -2211120, -2237760, -2264400, -2291040, -2317680, - -2344320, -2370960, -2397600, -2424240, -2450880, -2477520, -2504160, -2530800, -2557440, - -2584080, -2610720, -2637360, -2664000, -2690640, -2717280, -2743920, -2770560, -2797200, - -2823840, -2850480, -2877120, -2903760, -2930400, -2957040, -2983680, -3010320, -3036960, - -3063600, -3090240, -3116880, -3143520, -3170160, -3196800, -3223440, -3250080, -3276720, - -3303360, -3330000, -3356640, -3383280 -}; - -// U lookup table for the Blue component. Stored as fixed-point (16:16). -// U = 2.018 * (u - 128) -static const int coefficientsBU[256] = { - -8464128, -8398002, -8331876, -8265750, -8199624, -8133498, -8067372, -8001246, -7935120, - -7868994, -7802868, -7736742, -7670616, -7604490, -7538364, -7472238, -7406112, -7339986, - -7273860, -7207734, -7141608, -7075482, -7009356, -6943230, -6877104, -6810978, -6744852, - -6678726, -6612600, -6546474, -6480348, -6414222, -6348096, -6281970, -6215844, -6149718, - -6083592, -6017466, -5951340, -5885214, -5819088, -5752962, -5686836, -5620710, -5554584, - -5488458, -5422332, -5356206, -5290080, -5223954, -5157828, -5091702, -5025576, -4959450, - -4893324, -4827198, -4761072, -4694946, -4628820, -4562694, -4496568, -4430442, -4364316, - -4298190, -4232064, -4165938, -4099812, -4033686, -3967560, -3901434, -3835308, -3769182, - -3703056, -3636930, -3570804, -3504678, -3438552, -3372426, -3306300, -3240174, -3174048, - -3107922, -3041796, -2975670, -2909544, -2843418, -2777292, -2711166, -2645040, -2578914, - -2512788, -2446662, -2380536, -2314410, -2248284, -2182158, -2116032, -2049906, -1983780, - -1917654, -1851528, -1785402, -1719276, -1653150, -1587024, -1520898, -1454772, -1388646, - -1322520, -1256394, -1190268, -1124142, -1058016, -991890, -925764, -859638, -793512, - -727386, -661260, -595134, -529008, -462882, -396756, -330630, -264504, -198378, - -132252, -66126, 0, 66126, 132252, 198378, 264504, 330630, 396756, - 462882, 529008, 595134, 661260, 727386, 793512, 859638, 925764, 991890, - 1058016, 1124142, 1190268, 1256394, 1322520, 1388646, 1454772, 1520898, 1587024, - 1653150, 1719276, 1785402, 1851528, 1917654, 1983780, 2049906, 2116032, 2182158, - 2248284, 2314410, 2380536, 2446662, 2512788, 2578914, 2645040, 2711166, 2777292, - 2843418, 2909544, 2975670, 3041796, 3107922, 3174048, 3240174, 3306300, 3372426, - 3438552, 3504678, 3570804, 3636930, 3703056, 3769182, 3835308, 3901434, 3967560, - 4033686, 4099812, 4165938, 4232064, 4298190, 4364316, 4430442, 4496568, 4562694, - 4628820, 4694946, 4761072, 4827198, 4893324, 4959450, 5025576, 5091702, 5157828, - 5223954, 5290080, 5356206, 5422332, 5488458, 5554584, 5620710, 5686836, 5752962, - 5819088, 5885214, 5951340, 6017466, 6083592, 6149718, 6215844, 6281970, 6348096, - 6414222, 6480348, 6546474, 6612600, 6678726, 6744852, 6810978, 6877104, 6943230, - 7009356, 7075482, 7141608, 7207734, 7273860, 7339986, 7406112, 7472238, 7538364, - 7604490, 7670616, 7736742, 7802868, 7868994, 7935120, 8001246, 8067372, 8133498, - 8199624, 8265750, 8331876, 8398002 -}; - -// R = min(max(r, 0), 255) << 16 -// where 'r' is the converted red component from YUV, which is always in the range -320 <= r < 704 -// and needs to be clamped to 0-255. It also precomputes the bitshift needed to create an RGB value. -static const quint32 _clampedR[1024] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 65536, 131072, 196608, 262144, 327680, 393216, 458752, - 524288, 589824, 655360, 720896, 786432, 851968, 917504, 983040, - 1048576, 1114112, 1179648, 1245184, 1310720, 1376256, 1441792, 1507328, - 1572864, 1638400, 1703936, 1769472, 1835008, 1900544, 1966080, 2031616, - 2097152, 2162688, 2228224, 2293760, 2359296, 2424832, 2490368, 2555904, - 2621440, 2686976, 2752512, 2818048, 2883584, 2949120, 3014656, 3080192, - 3145728, 3211264, 3276800, 3342336, 3407872, 3473408, 3538944, 3604480, - 3670016, 3735552, 3801088, 3866624, 3932160, 3997696, 4063232, 4128768, - 4194304, 4259840, 4325376, 4390912, 4456448, 4521984, 4587520, 4653056, - 4718592, 4784128, 4849664, 4915200, 4980736, 5046272, 5111808, 5177344, - 5242880, 5308416, 5373952, 5439488, 5505024, 5570560, 5636096, 5701632, - 5767168, 5832704, 5898240, 5963776, 6029312, 6094848, 6160384, 6225920, - 6291456, 6356992, 6422528, 6488064, 6553600, 6619136, 6684672, 6750208, - 6815744, 6881280, 6946816, 7012352, 7077888, 7143424, 7208960, 7274496, - 7340032, 7405568, 7471104, 7536640, 7602176, 7667712, 7733248, 7798784, - 7864320, 7929856, 7995392, 8060928, 8126464, 8192000, 8257536, 8323072, - 8388608, 8454144, 8519680, 8585216, 8650752, 8716288, 8781824, 8847360, - 8912896, 8978432, 9043968, 9109504, 9175040, 9240576, 9306112, 9371648, - 9437184, 9502720, 9568256, 9633792, 9699328, 9764864, 9830400, 9895936, - 9961472, 10027008, 10092544, 10158080, 10223616, 10289152, 10354688, 10420224, - 10485760, 10551296, 10616832, 10682368, 10747904, 10813440, 10878976, 10944512, - 11010048, 11075584, 11141120, 11206656, 11272192, 11337728, 11403264, 11468800, - 11534336, 11599872, 11665408, 11730944, 11796480, 11862016, 11927552, 11993088, - 12058624, 12124160, 12189696, 12255232, 12320768, 12386304, 12451840, 12517376, - 12582912, 12648448, 12713984, 12779520, 12845056, 12910592, 12976128, 13041664, - 13107200, 13172736, 13238272, 13303808, 13369344, 13434880, 13500416, 13565952, - 13631488, 13697024, 13762560, 13828096, 13893632, 13959168, 14024704, 14090240, - 14155776, 14221312, 14286848, 14352384, 14417920, 14483456, 14548992, 14614528, - 14680064, 14745600, 14811136, 14876672, 14942208, 15007744, 15073280, 15138816, - 15204352, 15269888, 15335424, 15400960, 15466496, 15532032, 15597568, 15663104, - 15728640, 15794176, 15859712, 15925248, 15990784, 16056320, 16121856, 16187392, - 16252928, 16318464, 16384000, 16449536, 16515072, 16580608, 16646144, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, - 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680 -}; - -// G = min(max(g, 0), 255) << 8 -// where 'g' is the converted green component from YUV, which is always in the range -320 <= r < 704 -// and needs to be clamped to 0-255. It also precomputes the bitshift needed to create an RGB value. -static const quint32 _clampedG[1024] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 256, 512, 768, 1024, 1280, 1536, 1792, - 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, - 4096, 4352, 4608, 4864, 5120, 5376, 5632, 5888, - 6144, 6400, 6656, 6912, 7168, 7424, 7680, 7936, - 8192, 8448, 8704, 8960, 9216, 9472, 9728, 9984, - 10240, 10496, 10752, 11008, 11264, 11520, 11776, 12032, - 12288, 12544, 12800, 13056, 13312, 13568, 13824, 14080, - 14336, 14592, 14848, 15104, 15360, 15616, 15872, 16128, - 16384, 16640, 16896, 17152, 17408, 17664, 17920, 18176, - 18432, 18688, 18944, 19200, 19456, 19712, 19968, 20224, - 20480, 20736, 20992, 21248, 21504, 21760, 22016, 22272, - 22528, 22784, 23040, 23296, 23552, 23808, 24064, 24320, - 24576, 24832, 25088, 25344, 25600, 25856, 26112, 26368, - 26624, 26880, 27136, 27392, 27648, 27904, 28160, 28416, - 28672, 28928, 29184, 29440, 29696, 29952, 30208, 30464, - 30720, 30976, 31232, 31488, 31744, 32000, 32256, 32512, - 32768, 33024, 33280, 33536, 33792, 34048, 34304, 34560, - 34816, 35072, 35328, 35584, 35840, 36096, 36352, 36608, - 36864, 37120, 37376, 37632, 37888, 38144, 38400, 38656, - 38912, 39168, 39424, 39680, 39936, 40192, 40448, 40704, - 40960, 41216, 41472, 41728, 41984, 42240, 42496, 42752, - 43008, 43264, 43520, 43776, 44032, 44288, 44544, 44800, - 45056, 45312, 45568, 45824, 46080, 46336, 46592, 46848, - 47104, 47360, 47616, 47872, 48128, 48384, 48640, 48896, - 49152, 49408, 49664, 49920, 50176, 50432, 50688, 50944, - 51200, 51456, 51712, 51968, 52224, 52480, 52736, 52992, - 53248, 53504, 53760, 54016, 54272, 54528, 54784, 55040, - 55296, 55552, 55808, 56064, 56320, 56576, 56832, 57088, - 57344, 57600, 57856, 58112, 58368, 58624, 58880, 59136, - 59392, 59648, 59904, 60160, 60416, 60672, 60928, 61184, - 61440, 61696, 61952, 62208, 62464, 62720, 62976, 63232, - 63488, 63744, 64000, 64256, 64512, 64768, 65024, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, - 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280 -}; - -// B = min(max(b, 0), 255) -// where 'b' is the converted blue component from YUV, which is always in the range -320 <= r < 704 -static const quint32 _clampedB[1024] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, - 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 -}; - -static const quint32 *clampedR = _clampedR + 320; -static const quint32 *clampedG = _clampedG + 320; -static const quint32 *clampedB = _clampedB + 320; - -#define MAKE_RGB(r, g, b) 0xff000000 | clampedR[r] | clampedG[g] | clampedB[b] - -void qt_convert_NV21_to_ARGB32(const uchar *yuv, quint32 *rgb, int width, int height) +QVideoFrame::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f) { - const uchar *y0 = yuv; - const uchar *y1 = yuv + width; - const uchar *vu = yuv + width * height; - - quint32 *rgb0 = rgb; - quint32 *rgb1 = rgb + width; - - for (int i = 0; i < height; i += 2) { - for (int j = 0; j < width; j += 2) { - int v = *vu++; - int u = *vu++; - - int ruv = coefficientsRV[v] >> 15; - int guv = (coefficientsGU[u] + coefficientsGV[v]) >> 15; - int buv = coefficientsBU[u] >> 15; - - int y = coefficientsY[*y0++] >> 15; - int r = y + ruv; - int g = y + guv; - int b = y + buv; - *rgb0++ = MAKE_RGB(r, g, b); - - y = coefficientsY[*y0++] >> 15; - r = y + ruv; - g = y + guv; - b = y + buv; - *rgb0++ = MAKE_RGB(r, g, b); - - y = coefficientsY[*y1++] >> 15; - r = y + ruv; - g = y + guv; - b = y + buv; - *rgb1++ = MAKE_RGB(r, g, b); - - y = coefficientsY[*y1++] >> 15; - r = y + ruv; - g = y + guv; - b = y + buv; - *rgb1++ = MAKE_RGB(r, g, b); - } + switch (f) { + case AndroidCamera::NV21: + return QVideoFrame::Format_NV21; + case AndroidCamera::YV12: + return QVideoFrame::Format_YV12; + case AndroidCamera::RGB565: + return QVideoFrame::Format_RGB565; + case AndroidCamera::YUY2: + return QVideoFrame::Format_YUYV; + case AndroidCamera::JPEG: + return QVideoFrame::Format_Jpeg; + default: + return QVideoFrame::Format_Invalid; + } +} - rgb0 += width; - rgb1 += width; - y0 += width; - y1 += width; +AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrame::PixelFormat f) +{ + switch (f) { + case QVideoFrame::Format_NV21: + return AndroidCamera::NV21; + case QVideoFrame::Format_YV12: + return AndroidCamera::YV12; + case QVideoFrame::Format_RGB565: + return AndroidCamera::RGB565; + case QVideoFrame::Format_YUYV: + return AndroidCamera::YUY2; + case QVideoFrame::Format_Jpeg: + return AndroidCamera::JPEG; + default: + return AndroidCamera::UnknownImageFormat; } } diff --git a/src/plugins/android/src/common/qandroidmultimediautils.h b/src/plugins/android/src/common/qandroidmultimediautils.h index ea6a322e7..622f343f5 100644 --- a/src/plugins/android/src/common/qandroidmultimediautils.h +++ b/src/plugins/android/src/common/qandroidmultimediautils.h @@ -36,6 +36,7 @@ #include <qglobal.h> #include <qsize.h> +#include "androidcamera.h" QT_BEGIN_NAMESPACE @@ -45,7 +46,8 @@ int qt_findClosestValue(const QList<int> &list, int value); bool qt_sizeLessThan(const QSize &s1, const QSize &s2); -void qt_convert_NV21_to_ARGB32(const uchar *yuv, quint32 *rgb, int width, int height); +QVideoFrame::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f); +AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrame::PixelFormat f); QT_END_NAMESPACE diff --git a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp b/src/plugins/android/src/common/qandroidvideooutput.cpp index cd9c9d1d6..82c27035d 100644 --- a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp +++ b/src/plugins/android/src/common/qandroidvideooutput.cpp @@ -31,9 +31,9 @@ ** ****************************************************************************/ -#include "qandroidvideorendercontrol.h" -#include "androidsurfacetexture.h" +#include "qandroidvideooutput.h" +#include "androidsurfacetexture.h" #include <QAbstractVideoSurface> #include <QVideoSurfaceFormat> #include <qevent.h> @@ -59,19 +59,13 @@ static const GLfloat g_texture_data[] = { 0.f, 1.f }; -OpenGLResourcesDeleter::~OpenGLResourcesDeleter() -{ - glDeleteTextures(1, &m_textureID); - delete m_fbo; - delete m_program; -} class AndroidTextureVideoBuffer : public QAbstractVideoBuffer { public: - AndroidTextureVideoBuffer(QAndroidVideoRendererControl *control) + AndroidTextureVideoBuffer(QAndroidTextureVideoOutput *output) : QAbstractVideoBuffer(GLTextureHandle) - , m_control(control) + , m_output(output) , m_textureUpdated(false) , m_mapMode(NotMapped) { @@ -86,7 +80,7 @@ public: if (m_mapMode == NotMapped && mode == ReadOnly) { updateFrame(); m_mapMode = mode; - m_image = m_control->m_fbo->toImage(); + m_image = m_output->m_fbo->toImage(); if (numBytes) *numBytes = m_image.byteCount(); @@ -110,7 +104,7 @@ public: { AndroidTextureVideoBuffer *that = const_cast<AndroidTextureVideoBuffer*>(this); that->updateFrame(); - return m_control->m_fbo->texture(); + return m_output->m_fbo->texture(); } private: @@ -118,19 +112,47 @@ private: { if (!m_textureUpdated) { // update the video texture (called from the render thread) - m_control->renderFrameToFbo(); + m_output->renderFrameToFbo(); m_textureUpdated = true; } } - QAndroidVideoRendererControl *m_control; + QAndroidTextureVideoOutput *m_output; bool m_textureUpdated; MapMode m_mapMode; QImage m_image; }; -QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent) - : QVideoRendererControl(parent) + +class OpenGLResourcesDeleter : public QObject +{ +public: + OpenGLResourcesDeleter() + : m_textureID(0) + , m_fbo(0) + , m_program(0) + { } + + ~OpenGLResourcesDeleter() + { + glDeleteTextures(1, &m_textureID); + delete m_fbo; + delete m_program; + } + + void setTexture(quint32 id) { m_textureID = id; } + void setFbo(QOpenGLFramebufferObject *fbo) { m_fbo = fbo; } + void setShaderProgram(QOpenGLShaderProgram *prog) { m_program = prog; } + +private: + quint32 m_textureID; + QOpenGLFramebufferObject *m_fbo; + QOpenGLShaderProgram *m_program; +}; + + +QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent) + : QAndroidVideoOutput(parent) , m_surface(0) , m_surfaceTexture(0) , m_externalTex(0) @@ -138,9 +160,10 @@ QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent) , m_program(0) , m_glDeleter(0) { + } -QAndroidVideoRendererControl::~QAndroidVideoRendererControl() +QAndroidTextureVideoOutput::~QAndroidTextureVideoOutput() { clearSurfaceTexture(); @@ -148,12 +171,12 @@ QAndroidVideoRendererControl::~QAndroidVideoRendererControl() m_glDeleter->deleteLater(); } -QAbstractVideoSurface *QAndroidVideoRendererControl::surface() const +QAbstractVideoSurface *QAndroidTextureVideoOutput::surface() const { return m_surface; } -void QAndroidVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +void QAndroidTextureVideoOutput::setSurface(QAbstractVideoSurface *surface) { if (surface == m_surface) return; @@ -172,12 +195,12 @@ void QAndroidVideoRendererControl::setSurface(QAbstractVideoSurface *surface) } } -bool QAndroidVideoRendererControl::isReady() +bool QAndroidTextureVideoOutput::isReady() { return QOpenGLContext::currentContext() || m_externalTex; } -bool QAndroidVideoRendererControl::initSurfaceTexture() +bool QAndroidTextureVideoOutput::initSurfaceTexture() { if (m_surfaceTexture) return true; @@ -210,7 +233,7 @@ bool QAndroidVideoRendererControl::initSurfaceTexture() return m_surfaceTexture != 0; } -void QAndroidVideoRendererControl::clearSurfaceTexture() +void QAndroidTextureVideoOutput::clearSurfaceTexture() { if (m_surfaceTexture) { delete m_surfaceTexture; @@ -218,7 +241,7 @@ void QAndroidVideoRendererControl::clearSurfaceTexture() } } -AndroidSurfaceTexture *QAndroidVideoRendererControl::surfaceTexture() +AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture() { if (!initSurfaceTexture()) return 0; @@ -226,7 +249,7 @@ AndroidSurfaceTexture *QAndroidVideoRendererControl::surfaceTexture() return m_surfaceTexture; } -void QAndroidVideoRendererControl::setVideoSize(const QSize &size) +void QAndroidTextureVideoOutput::setVideoSize(const QSize &size) { QMutexLocker locker(&m_mutex); @@ -238,19 +261,19 @@ void QAndroidVideoRendererControl::setVideoSize(const QSize &size) m_nativeSize = size; } -void QAndroidVideoRendererControl::stop() +void QAndroidTextureVideoOutput::stop() { if (m_surface && m_surface->isActive()) m_surface->stop(); m_nativeSize = QSize(); } -void QAndroidVideoRendererControl::reset() +void QAndroidTextureVideoOutput::reset() { clearSurfaceTexture(); } -void QAndroidVideoRendererControl::onFrameAvailable() +void QAndroidTextureVideoOutput::onFrameAvailable() { if (!m_nativeSize.isValid() || !m_surface) return; @@ -274,7 +297,7 @@ void QAndroidVideoRendererControl::onFrameAvailable() m_surface->present(frame); } -void QAndroidVideoRendererControl::renderFrameToFbo() +void QAndroidTextureVideoOutput::renderFrameToFbo() { QMutexLocker locker(&m_mutex); @@ -333,7 +356,7 @@ void QAndroidVideoRendererControl::renderFrameToFbo() glEnable(GL_BLEND); } -void QAndroidVideoRendererControl::createGLResources() +void QAndroidTextureVideoOutput::createGLResources() { if (!m_fbo || m_fbo->size() != m_nativeSize) { delete m_fbo; @@ -374,7 +397,7 @@ void QAndroidVideoRendererControl::createGLResources() } } -void QAndroidVideoRendererControl::customEvent(QEvent *e) +void QAndroidTextureVideoOutput::customEvent(QEvent *e) { if (e->type() == QEvent::User) { // This is running in the render thread (OpenGL enabled) diff --git a/src/plugins/android/src/common/qandroidvideooutput.h b/src/plugins/android/src/common/qandroidvideooutput.h index d45779d12..f4401fa1d 100644 --- a/src/plugins/android/src/common/qandroidvideooutput.h +++ b/src/plugins/android/src/common/qandroidvideooutput.h @@ -34,19 +34,27 @@ #ifndef QANDROIDVIDEOOUTPUT_H #define QANDROIDVIDEOOUTPUT_H -#include <qglobal.h> +#include <qobject.h> #include <qsize.h> +#include <qmutex.h> QT_BEGIN_NAMESPACE class AndroidSurfaceTexture; +class AndroidSurfaceHolder; +class QOpenGLFramebufferObject; +class QOpenGLShaderProgram; +class OpenGLResourcesDeleter; +class QAbstractVideoSurface; -class QAndroidVideoOutput +class QAndroidVideoOutput : public QObject { + Q_OBJECT public: virtual ~QAndroidVideoOutput() { } virtual AndroidSurfaceTexture *surfaceTexture() { return 0; } + virtual AndroidSurfaceHolder *surfaceHolder() { return 0; } virtual bool isReady() { return true; } @@ -54,12 +62,56 @@ public: virtual void stop() { } virtual void reset() { } - // signals: - // void readyChanged(bool); +Q_SIGNALS: + void readyChanged(bool); + +protected: + QAndroidVideoOutput(QObject *parent) : QObject(parent) { } }; -#define QAndroidVideoOutput_iid "org.qt-project.qt.qandroidvideooutput/5.0" -Q_DECLARE_INTERFACE(QAndroidVideoOutput, QAndroidVideoOutput_iid) + +class QAndroidTextureVideoOutput : public QAndroidVideoOutput +{ + Q_OBJECT +public: + explicit QAndroidTextureVideoOutput(QObject *parent = 0); + ~QAndroidTextureVideoOutput() Q_DECL_OVERRIDE; + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + AndroidSurfaceTexture *surfaceTexture() Q_DECL_OVERRIDE; + + bool isReady() Q_DECL_OVERRIDE; + void setVideoSize(const QSize &) Q_DECL_OVERRIDE; + void stop() Q_DECL_OVERRIDE; + void reset() Q_DECL_OVERRIDE; + + void customEvent(QEvent *) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onFrameAvailable(); + +private: + bool initSurfaceTexture(); + void renderFrameToFbo(); + void createGLResources(); + + QMutex m_mutex; + void clearSurfaceTexture(); + + QAbstractVideoSurface *m_surface; + QSize m_nativeSize; + + AndroidSurfaceTexture *m_surfaceTexture; + + quint32 m_externalTex; + QOpenGLFramebufferObject *m_fbo; + QOpenGLShaderProgram *m_program; + OpenGLResourcesDeleter *m_glDeleter; + + friend class AndroidTextureVideoBuffer; +}; QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediacapture/mediacapture.pri b/src/plugins/android/src/mediacapture/mediacapture.pri index fde0e3d6f..2811f0371 100644 --- a/src/plugins/android/src/mediacapture/mediacapture.pri +++ b/src/plugins/android/src/mediacapture/mediacapture.pri @@ -22,7 +22,8 @@ SOURCES += \ $$PWD/qandroidvideoencodersettingscontrol.cpp \ $$PWD/qandroidaudioinputselectorcontrol.cpp \ $$PWD/qandroidmediavideoprobecontrol.cpp \ - $$PWD/qandroidcamerainfocontrol.cpp + $$PWD/qandroidcamerainfocontrol.cpp \ + $$PWD/qandroidcameravideorenderercontrol.cpp HEADERS += \ $$PWD/qandroidcaptureservice.h \ @@ -46,4 +47,5 @@ HEADERS += \ $$PWD/qandroidvideoencodersettingscontrol.h \ $$PWD/qandroidaudioinputselectorcontrol.h \ $$PWD/qandroidmediavideoprobecontrol.h \ - $$PWD/qandroidcamerainfocontrol.h + $$PWD/qandroidcamerainfocontrol.h \ + $$PWD/qandroidcameravideorenderercontrol.h diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp index 743764cb0..7b065c8c4 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp @@ -43,45 +43,11 @@ #include <qguiapplication.h> #include <qdebug.h> #include <qvideoframe.h> +#include <private/qmemoryvideobuffer_p.h> +#include <private/qvideoframe_p.h> QT_BEGIN_NAMESPACE -class DataVideoBuffer : public QAbstractVideoBuffer -{ -public: - DataVideoBuffer(const QByteArray &d, int bpl = -1) - : QAbstractVideoBuffer(NoHandle) - , data(d) - , mode(NotMapped) - , bytesPerLine(bpl) - { } - - MapMode mapMode() const { return mode; } - - uchar *map(MapMode m, int *numBytes, int *bpl) - { - if (mode != NotMapped || m == NotMapped) - return 0; - - mode = m; - - if (numBytes) - *numBytes = data.size(); - - if (bpl) - *bpl = bytesPerLine; - - return reinterpret_cast<uchar *>(data.data()); - } - - void unmap() { mode = NotMapped; } - -private: - QByteArray data; - MapMode mode; - int bytesPerLine; -}; - Q_GLOBAL_STATIC(QList<AndroidCameraInfo>, g_availableCameras) QAndroidCameraSession::QAndroidCameraSession(QObject *parent) @@ -102,6 +68,7 @@ QAndroidCameraSession::QAndroidCameraSession(QObject *parent) , m_readyForCapture(false) , m_captureCanceled(false) , m_currentImageCaptureId(-1) + , m_previewCallback(0) { m_mediaStorageLocation.addStorageLocation( QMediaStorageLocation::Pictures, @@ -206,10 +173,11 @@ bool QAndroidCameraSession::open() if (m_camera) { connect(m_camera, SIGNAL(pictureExposed()), this, SLOT(onCameraPictureExposed())); - connect(m_camera, SIGNAL(lastPreviewFrameFetched(QByteArray,int,int)), - this, SLOT(onLastPreviewFrameFetched(QByteArray,int,int))); - connect(m_camera, SIGNAL(newPreviewFrame(QByteArray,int,int)), - this, SLOT(onNewPreviewFrame(QByteArray,int,int)), + connect(m_camera, SIGNAL(lastPreviewFrameFetched(QVideoFrame)), + this, SLOT(onLastPreviewFrameFetched(QVideoFrame)), + Qt::DirectConnection); + connect(m_camera, SIGNAL(newPreviewFrame(QVideoFrame)), + this, SLOT(onNewPreviewFrame(QVideoFrame)), Qt::DirectConnection); connect(m_camera, SIGNAL(pictureCaptured(QByteArray)), this, SLOT(onCameraPictureCaptured(QByteArray))); connect(m_camera, SIGNAL(previewStarted()), this, SLOT(onCameraPreviewStarted())); @@ -224,7 +192,7 @@ bool QAndroidCameraSession::open() if (m_camera->getPreviewFormat() != AndroidCamera::NV21) m_camera->setPreviewFormat(AndroidCamera::NV21); - m_camera->notifyNewFrames(m_videoProbes.count()); + m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback); emit opened(); } else { @@ -259,16 +227,19 @@ void QAndroidCameraSession::close() emit statusChanged(m_status); } -void QAndroidCameraSession::setVideoPreview(QObject *videoOutput) +void QAndroidCameraSession::setVideoOutput(QAndroidVideoOutput *output) { if (m_videoOutput) { m_videoOutput->stop(); m_videoOutput->reset(); } - if (videoOutput) { - connect(videoOutput, SIGNAL(readyChanged(bool)), this, SLOT(onVideoOutputReady(bool))); - m_videoOutput = qobject_cast<QAndroidVideoOutput *>(videoOutput); + if (output) { + m_videoOutput = output; + if (m_videoOutput->isReady()) + onVideoOutputReady(true); + else + connect(m_videoOutput, SIGNAL(readyChanged(bool)), this, SLOT(onVideoOutputReady(bool))); } else { m_videoOutput = 0; } @@ -336,7 +307,10 @@ bool QAndroidCameraSession::startPreview() if (!m_videoOutput->isReady()) return true; // delay starting until the video output is ready - if (!m_camera->setPreviewTexture(m_videoOutput->surfaceTexture())) + Q_ASSERT(m_videoOutput->surfaceTexture() || m_videoOutput->surfaceHolder()); + + if ((m_videoOutput->surfaceTexture() && !m_camera->setPreviewTexture(m_videoOutput->surfaceTexture())) + || (m_videoOutput->surfaceHolder() && !m_camera->setPreviewDisplay(m_videoOutput->surfaceHolder()))) return false; m_status = QCamera::StartingStatus; @@ -366,6 +340,7 @@ void QAndroidCameraSession::stopPreview() m_camera->stopPreview(); m_camera->setPreviewSize(QSize()); m_camera->setPreviewTexture(0); + m_camera->setPreviewDisplay(0); if (m_videoOutput) { m_videoOutput->stop(); @@ -413,7 +388,7 @@ void QAndroidCameraSession::addProbe(QAndroidMediaVideoProbeControl *probe) if (probe) m_videoProbes << probe; if (m_camera) - m_camera->notifyNewFrames(m_videoProbes.count()); + m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback); m_videoProbesMutex.unlock(); } @@ -422,7 +397,24 @@ void QAndroidCameraSession::removeProbe(QAndroidMediaVideoProbeControl *probe) m_videoProbesMutex.lock(); m_videoProbes.remove(probe); if (m_camera) - m_camera->notifyNewFrames(m_videoProbes.count()); + m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback); + m_videoProbesMutex.unlock(); +} + +void QAndroidCameraSession::setPreviewFormat(AndroidCamera::ImageFormat format) +{ + if (format == AndroidCamera::UnknownImageFormat) + return; + + m_camera->setPreviewFormat(format); +} + +void QAndroidCameraSession::setPreviewCallback(PreviewCallback *callback) +{ + m_videoProbesMutex.lock(); + m_previewCallback = callback; + if (m_camera) + m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback); m_videoProbesMutex.unlock(); } @@ -571,58 +563,37 @@ void QAndroidCameraSession::onCameraPictureExposed() m_camera->fetchLastPreviewFrame(); } -void QAndroidCameraSession::onLastPreviewFrameFetched(const QByteArray &preview, int width, int height) -{ - if (preview.size()) { - QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage, - m_currentImageCaptureId, - preview, - width, - height, - m_camera->getRotation()); - } -} - -void QAndroidCameraSession::processPreviewImage(int id, const QByteArray &data, int width, int height, int rotation) +void QAndroidCameraSession::onLastPreviewFrameFetched(const QVideoFrame &frame) { - emit imageCaptured(id, prepareImageFromPreviewData(data, width, height, rotation)); + QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage, + m_currentImageCaptureId, + frame, + m_camera->getRotation()); } -QImage QAndroidCameraSession::prepareImageFromPreviewData(const QByteArray &data, int width, int height, int rotation) +void QAndroidCameraSession::processPreviewImage(int id, const QVideoFrame &frame, int rotation) { - QImage result(width, height, QImage::Format_ARGB32); - qt_convert_NV21_to_ARGB32((const uchar *)data.constData(), - (quint32 *)result.bits(), - width, - height); - - QTransform transform; - // Preview display of front-facing cameras is flipped horizontally, but the frame data // we get here is not. Flip it ourselves if the camera is front-facing to match what the user // sees on the viewfinder. + QTransform transform; if (m_camera->getFacing() == AndroidCamera::CameraFacingFront) transform.scale(-1, 1); - transform.rotate(rotation); - result = result.transformed(transform); - - return result; + emit imageCaptured(id, qt_imageFromVideoFrame(frame).transformed(transform)); } -void QAndroidCameraSession::onNewPreviewFrame(const QByteArray &frame, int width, int height) +void QAndroidCameraSession::onNewPreviewFrame(const QVideoFrame &frame) { m_videoProbesMutex.lock(); - if (frame.size() && m_videoProbes.count()) { - // Bytes per line should be only for the first plane. For NV21, the Y plane has 8 bits - // per sample, so bpl == width - QVideoFrame videoFrame(new DataVideoBuffer(frame, width), - QSize(width, height), - QVideoFrame::Format_NV21); - foreach (QAndroidMediaVideoProbeControl *probe, m_videoProbes) - probe->newFrameProbed(videoFrame); - } + + foreach (QAndroidMediaVideoProbeControl *probe, m_videoProbes) + probe->newFrameProbed(frame); + + if (m_previewCallback) + m_previewCallback->onFrameAvailable(frame); + m_videoProbesMutex.unlock(); } @@ -720,7 +691,7 @@ void QAndroidCameraSession::processCapturedImage(int id, } if (dest & QCameraImageCapture::CaptureToBuffer) { - QVideoFrame frame(new DataVideoBuffer(data), resolution, QVideoFrame::Format_Jpeg); + QVideoFrame frame(new QMemoryVideoBuffer(data, -1), resolution, QVideoFrame::Format_Jpeg); emit imageAvailable(id, frame); } } diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.h b/src/plugins/android/src/mediacapture/qandroidcamerasession.h index c1772053d..d15509fe8 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerasession.h +++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.h @@ -68,7 +68,8 @@ public: void setCaptureMode(QCamera::CaptureModes mode); bool isCaptureModeSupported(QCamera::CaptureModes mode) const; - void setVideoPreview(QObject *videoOutput); + QAndroidVideoOutput *videoOutput() const { return m_videoOutput; } + void setVideoOutput(QAndroidVideoOutput *output); void adjustViewfinderSize(const QSize &captureSize, bool restartPreview = true); QImageEncoderSettings imageSettings() const { return m_imageSettings; } @@ -90,6 +91,14 @@ public: void addProbe(QAndroidMediaVideoProbeControl *probe); void removeProbe(QAndroidMediaVideoProbeControl *probe); + void setPreviewFormat(AndroidCamera::ImageFormat format); + + struct PreviewCallback + { + virtual void onFrameAvailable(const QVideoFrame &frame) = 0; + }; + void setPreviewCallback(PreviewCallback *callback); + Q_SIGNALS: void statusChanged(QCamera::Status status); void stateChanged(QCamera::State); @@ -115,8 +124,8 @@ private Q_SLOTS: void onCameraTakePictureFailed(); void onCameraPictureExposed(); void onCameraPictureCaptured(const QByteArray &data); - void onLastPreviewFrameFetched(const QByteArray &preview, int width, int height); - void onNewPreviewFrame(const QByteArray &frame, int width, int height); + void onLastPreviewFrameFetched(const QVideoFrame &frame); + void onNewPreviewFrame(const QVideoFrame &frame); void onCameraPreviewStarted(); void onCameraPreviewFailedToStart(); void onCameraPreviewStopped(); @@ -131,8 +140,8 @@ private: void stopPreview(); void applyImageSettings(); - void processPreviewImage(int id, const QByteArray &data, int width, int height, int rotation); - QImage prepareImageFromPreviewData(const QByteArray &data, int width, int height, int rotation); + + void processPreviewImage(int id, const QVideoFrame &frame, int rotation); void processCapturedImage(int id, const QByteArray &data, const QSize &resolution, @@ -164,6 +173,7 @@ private: QSet<QAndroidMediaVideoProbeControl *> m_videoProbes; QMutex m_videoProbesMutex; + PreviewCallback *m_previewCallback; }; QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp new file mode 100644 index 000000000..1d5b521b8 --- /dev/null +++ b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidcameravideorenderercontrol.h" + +#include "qandroidcamerasession.h" +#include "qandroidvideooutput.h" +#include "androidsurfaceview.h" +#include "qandroidmultimediautils.h" +#include <qabstractvideosurface.h> +#include <qvideosurfaceformat.h> +#include <qcoreapplication.h> +#include <qthread.h> + +QT_BEGIN_NAMESPACE + +class QAndroidCameraDataVideoOutput : public QAndroidVideoOutput + , public QAndroidCameraSession::PreviewCallback +{ + Q_OBJECT +public: + explicit QAndroidCameraDataVideoOutput(QAndroidCameraVideoRendererControl *control); + ~QAndroidCameraDataVideoOutput() Q_DECL_OVERRIDE; + + AndroidSurfaceHolder *surfaceHolder() Q_DECL_OVERRIDE; + + bool isReady() Q_DECL_OVERRIDE; + + void stop() Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onSurfaceCreated(); + void configureFormat(); + +private: + void onFrameAvailable(const QVideoFrame &frame); + void presentFrame(); + bool event(QEvent *); + + QAndroidCameraVideoRendererControl *m_control; + AndroidSurfaceView *m_surfaceView; + QMutex m_mutex; + QVideoFrame::PixelFormat m_pixelFormat; + QVideoFrame m_lastFrame; +}; + +QAndroidCameraDataVideoOutput::QAndroidCameraDataVideoOutput(QAndroidCameraVideoRendererControl *control) + : QAndroidVideoOutput(control) + , m_control(control) + , m_pixelFormat(QVideoFrame::Format_Invalid) +{ + // The camera preview cannot be started unless we set a SurfaceTexture or a + // SurfaceHolder. In this case we don't actually care about either of these, but since + // we need to, we setup an offscreen dummy SurfaceView in order to be able to start + // the camera preview. We'll then be able to use setPreviewCallbackWithBuffer() to + // get the raw data. + + m_surfaceView = new AndroidSurfaceView; + + connect(m_surfaceView, &AndroidSurfaceView::surfaceCreated, + this, &QAndroidCameraDataVideoOutput::onSurfaceCreated); + + m_surfaceView->setGeometry(-1, -1, 1, 1); + m_surfaceView->setVisible(true); + + connect(m_control->cameraSession(), &QAndroidCameraSession::opened, + this, &QAndroidCameraDataVideoOutput::configureFormat); + connect(m_control->surface(), &QAbstractVideoSurface::supportedFormatsChanged, + this, &QAndroidCameraDataVideoOutput::configureFormat); + configureFormat(); +} + +QAndroidCameraDataVideoOutput::~QAndroidCameraDataVideoOutput() +{ + m_control->cameraSession()->setPreviewCallback(Q_NULLPTR); + delete m_surfaceView; +} + +AndroidSurfaceHolder *QAndroidCameraDataVideoOutput::surfaceHolder() +{ + return m_surfaceView->holder(); +} + +bool QAndroidCameraDataVideoOutput::isReady() +{ + return m_surfaceView->holder() && m_surfaceView->holder()->isSurfaceCreated(); +} + +void QAndroidCameraDataVideoOutput::onSurfaceCreated() +{ + emit readyChanged(true); +} + +void QAndroidCameraDataVideoOutput::configureFormat() +{ + m_pixelFormat = QVideoFrame::Format_Invalid; + + if (!m_control->cameraSession()->camera()) + return; + + QList<QVideoFrame::PixelFormat> surfaceFormats = m_control->surface()->supportedPixelFormats(); + QList<AndroidCamera::ImageFormat> previewFormats = m_control->cameraSession()->camera()->getSupportedPreviewFormats(); + for (int i = 0; i < surfaceFormats.size(); ++i) { + QVideoFrame::PixelFormat pixFormat = surfaceFormats.at(i); + AndroidCamera::ImageFormat f = qt_androidImageFormatFromPixelFormat(pixFormat); + if (previewFormats.contains(f)) { + m_pixelFormat = pixFormat; + break; + } + } + + if (m_pixelFormat == QVideoFrame::Format_Invalid) { + m_control->cameraSession()->setPreviewCallback(Q_NULLPTR); + qWarning("The video surface is not compatible with any format supported by the camera"); + } else { + m_control->cameraSession()->setPreviewCallback(this); + + if (m_control->cameraSession()->status() > QCamera::LoadedStatus) + m_control->cameraSession()->camera()->stopPreview(); + + m_control->cameraSession()->setPreviewFormat(qt_androidImageFormatFromPixelFormat(m_pixelFormat)); + + if (m_control->cameraSession()->status() > QCamera::LoadedStatus) + m_control->cameraSession()->camera()->startPreview(); + } +} + +void QAndroidCameraDataVideoOutput::stop() +{ + m_mutex.lock(); + m_lastFrame = QVideoFrame(); + m_mutex.unlock(); + + if (m_control->surface() && m_control->surface()->isActive()) + m_control->surface()->stop(); +} + +void QAndroidCameraDataVideoOutput::onFrameAvailable(const QVideoFrame &frame) +{ + m_mutex.lock(); + m_lastFrame = frame; + m_mutex.unlock(); + + if (thread() == QThread::currentThread()) + presentFrame(); + else + QCoreApplication::postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); +} + +bool QAndroidCameraDataVideoOutput::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + presentFrame(); + return true; + } + + return QObject::event(e); +} + +void QAndroidCameraDataVideoOutput::presentFrame() +{ + Q_ASSERT(thread() == QThread::currentThread()); + + QMutexLocker locker(&m_mutex); + + if (m_control->surface() && m_lastFrame.isValid() && m_lastFrame.pixelFormat() == m_pixelFormat) { + + if (m_control->surface()->isActive() && (m_control->surface()->surfaceFormat().pixelFormat() != m_lastFrame.pixelFormat() + || m_control->surface()->surfaceFormat().frameSize() != m_lastFrame.size())) { + m_control->surface()->stop(); + } + + if (!m_control->surface()->isActive()) { + QVideoSurfaceFormat format(m_lastFrame.size(), m_lastFrame.pixelFormat(), m_lastFrame.handleType()); + // Front camera frames are automatically mirrored when using SurfaceTexture or SurfaceView, + // but the buffers we get from the data callback are not. Tell the QAbstractVideoSurface + // that it needs to mirror the frames. + if (m_control->cameraSession()->camera()->getFacing() == AndroidCamera::CameraFacingFront) + format.setProperty("mirrored", true); + + m_control->surface()->start(format); + } + + if (m_control->surface()->isActive()) + m_control->surface()->present(m_lastFrame); + } + + m_lastFrame = QVideoFrame(); +} + + +QAndroidCameraVideoRendererControl::QAndroidCameraVideoRendererControl(QAndroidCameraSession *session, QObject *parent) + : QVideoRendererControl(parent) + , m_cameraSession(session) + , m_surface(0) + , m_textureOutput(0) + , m_dataOutput(0) +{ +} + +QAndroidCameraVideoRendererControl::~QAndroidCameraVideoRendererControl() +{ + m_cameraSession->setVideoOutput(0); +} + +QAbstractVideoSurface *QAndroidCameraVideoRendererControl::surface() const +{ + return m_surface; +} + +void QAndroidCameraVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + if (m_surface == surface) + return; + + m_surface = surface; + QAndroidVideoOutput *oldOutput = m_textureOutput ? static_cast<QAndroidVideoOutput*>(m_textureOutput) + : static_cast<QAndroidVideoOutput*>(m_dataOutput); + QAndroidVideoOutput *newOutput = 0; + + if (m_surface) { + if (!m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty()) { + if (!m_textureOutput) { + m_dataOutput = 0; + newOutput = m_textureOutput = new QAndroidTextureVideoOutput(this); + } + } else if (!m_dataOutput) { + m_textureOutput = 0; + newOutput = m_dataOutput = new QAndroidCameraDataVideoOutput(this); + } + + if (m_textureOutput) + m_textureOutput->setSurface(m_surface); + } + + if (newOutput != oldOutput) { + m_cameraSession->setVideoOutput(newOutput); + delete oldOutput; + } +} + +QT_END_NAMESPACE + +#include "qandroidcameravideorenderercontrol.moc" + diff --git a/src/plugins/android/src/common/qandroidvideorendercontrol.h b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h index c660758fb..4b6428ba0 100644 --- a/src/plugins/android/src/common/qandroidvideorendercontrol.h +++ b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h @@ -31,88 +31,36 @@ ** ****************************************************************************/ -#ifndef QANDROIDVIDEORENDERCONTROL_H -#define QANDROIDVIDEORENDERCONTROL_H +#ifndef QANDROIDCAMERAVIDEORENDERERCONTROL_H +#define QANDROIDCAMERAVIDEORENDERERCONTROL_H #include <qvideorenderercontrol.h> -#include <qmutex.h> -#include "qandroidvideooutput.h" QT_BEGIN_NAMESPACE -class QOpenGLTexture; -class QOpenGLFramebufferObject; -class QOpenGLShaderProgram; -class AndroidSurfaceTexture; +class QAndroidCameraSession; +class QAndroidTextureVideoOutput; +class QAndroidCameraDataVideoOutput; -class OpenGLResourcesDeleter : public QObject +class QAndroidCameraVideoRendererControl : public QVideoRendererControl { Q_OBJECT public: - OpenGLResourcesDeleter() - : m_textureID(0) - , m_fbo(0) - , m_program(0) - { } - - ~OpenGLResourcesDeleter(); - - void setTexture(quint32 id) { m_textureID = id; } - void setFbo(QOpenGLFramebufferObject *fbo) { m_fbo = fbo; } - void setShaderProgram(QOpenGLShaderProgram *prog) { m_program = prog; } - -private: - quint32 m_textureID; - QOpenGLFramebufferObject *m_fbo; - QOpenGLShaderProgram *m_program; -}; - -class QAndroidVideoRendererControl : public QVideoRendererControl, public QAndroidVideoOutput -{ - Q_OBJECT - Q_INTERFACES(QAndroidVideoOutput) -public: - explicit QAndroidVideoRendererControl(QObject *parent = 0); - ~QAndroidVideoRendererControl() Q_DECL_OVERRIDE; + QAndroidCameraVideoRendererControl(QAndroidCameraSession *session, QObject *parent = 0); + ~QAndroidCameraVideoRendererControl() Q_DECL_OVERRIDE; QAbstractVideoSurface *surface() const Q_DECL_OVERRIDE; void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE; - AndroidSurfaceTexture *surfaceTexture() Q_DECL_OVERRIDE; - bool isReady() Q_DECL_OVERRIDE; - void setVideoSize(const QSize &size) Q_DECL_OVERRIDE; - void stop() Q_DECL_OVERRIDE; - void reset() Q_DECL_OVERRIDE; - - void customEvent(QEvent *) Q_DECL_OVERRIDE; - -Q_SIGNALS: - void readyChanged(bool); - -private Q_SLOTS: - void onFrameAvailable(); + QAndroidCameraSession *cameraSession() const { return m_cameraSession; } private: - bool initSurfaceTexture(); - void renderFrameToFbo(); - void createGLResources(); - - QMutex m_mutex; - void clearSurfaceTexture(); - + QAndroidCameraSession *m_cameraSession; QAbstractVideoSurface *m_surface; - QSize m_nativeSize; - - AndroidSurfaceTexture *m_surfaceTexture; - - quint32 m_externalTex; - QOpenGLFramebufferObject *m_fbo; - QOpenGLShaderProgram *m_program; - OpenGLResourcesDeleter *m_glDeleter; - - friend class AndroidTextureVideoBuffer; + QAndroidTextureVideoOutput *m_textureOutput; + QAndroidCameraDataVideoOutput *m_dataOutput; }; QT_END_NAMESPACE -#endif // QANDROIDVIDEORENDERCONTROL_H +#endif // QANDROIDCAMERAVIDEORENDERERCONTROL_H diff --git a/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp b/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp index e9cdb1e78..d2107e8a5 100644 --- a/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp @@ -40,7 +40,7 @@ #include "qandroidvideodeviceselectorcontrol.h" #include "qandroidaudioinputselectorcontrol.h" #include "qandroidcamerasession.h" -#include "qandroidvideorendercontrol.h" +#include "qandroidcameravideorenderercontrol.h" #include "qandroidcamerazoomcontrol.h" #include "qandroidcameraexposurecontrol.h" #include "qandroidcameraflashcontrol.h" @@ -196,8 +196,7 @@ QMediaControl *QAndroidCaptureService::requestControl(const char *name) if (qstrcmp(name, QVideoRendererControl_iid) == 0 && m_service == QLatin1String(Q_MEDIASERVICE_CAMERA) && !m_videoRendererControl) { - m_videoRendererControl = new QAndroidVideoRendererControl; - m_cameraSession->setVideoPreview(m_videoRendererControl); + m_videoRendererControl = new QAndroidCameraVideoRendererControl(m_cameraSession); return m_videoRendererControl; } @@ -217,7 +216,6 @@ void QAndroidCaptureService::releaseControl(QMediaControl *control) { if (control) { if (control == m_videoRendererControl) { - m_cameraSession->setVideoPreview(0); delete m_videoRendererControl; m_videoRendererControl = 0; return; diff --git a/src/plugins/android/src/mediacapture/qandroidcaptureservice.h b/src/plugins/android/src/mediacapture/qandroidcaptureservice.h index fc84ac124..02f063444 100644 --- a/src/plugins/android/src/mediacapture/qandroidcaptureservice.h +++ b/src/plugins/android/src/mediacapture/qandroidcaptureservice.h @@ -46,7 +46,7 @@ class QAndroidCameraInfoControl; class QAndroidVideoDeviceSelectorControl; class QAndroidAudioInputSelectorControl; class QAndroidCameraSession; -class QAndroidVideoRendererControl; +class QAndroidCameraVideoRendererControl; class QAndroidCameraZoomControl; class QAndroidCameraExposureControl; class QAndroidCameraFlashControl; @@ -82,7 +82,7 @@ private: QAndroidVideoDeviceSelectorControl *m_videoInputControl; QAndroidAudioInputSelectorControl *m_audioInputControl; QAndroidCameraSession *m_cameraSession; - QMediaControl *m_videoRendererControl; + QAndroidCameraVideoRendererControl *m_videoRendererControl; QAndroidCameraZoomControl *m_cameraZoomControl; QAndroidCameraExposureControl *m_cameraExposureControl; QAndroidCameraFlashControl *m_cameraFlashControl; diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp index f2ea1b9d7..d25438ec7 100644 --- a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp @@ -37,6 +37,7 @@ #include "qandroidcamerasession.h" #include "androidmultimediautils.h" #include "qandroidmultimediautils.h" +#include "qandroidvideooutput.h" QT_BEGIN_NAMESPACE @@ -217,6 +218,20 @@ void QAndroidCaptureSession::start() m_usedOutputLocation = QUrl::fromLocalFile(filePath); m_mediaRecorder->setOutputFile(filePath); + // Even though the Android doc explicitly says that calling MediaRecorder.setPreviewDisplay() + // is not necessary when the Camera already has a Surface, it doesn't actually work on some + // devices. For example on the Samsung Galaxy Tab 2, the camera server dies after prepare() + // and start() if MediaRecorder.setPreviewDispaly() is not called. + if (m_cameraSession) { + // When using a SurfaceTexture, we need to pass a new one to the MediaRecorder, not the same + // one that is set on the Camera or it will crash, hence the reset(). + m_cameraSession->videoOutput()->reset(); + if (m_cameraSession->videoOutput()->surfaceTexture()) + m_mediaRecorder->setSurfaceTexture(m_cameraSession->videoOutput()->surfaceTexture()); + else if (m_cameraSession->videoOutput()->surfaceHolder()) + m_mediaRecorder->setSurfaceHolder(m_cameraSession->videoOutput()->surfaceHolder()); + } + if (!m_mediaRecorder->prepare()) { emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder.")); restartViewfinder(); @@ -412,13 +427,23 @@ void QAndroidCaptureSession::applySettings() void QAndroidCaptureSession::updateViewfinder() { - m_cameraSession->camera()->stopPreview(); + m_cameraSession->camera()->stopPreviewSynchronous(); m_cameraSession->adjustViewfinderSize(m_videoSettings.resolution(), false); } void QAndroidCaptureSession::restartViewfinder() { m_cameraSession->camera()->reconnect(); + + // This is not necessary on most devices, but it crashes on some if we don't stop the + // preview and reset the preview display on the camera when recording is over. + m_cameraSession->camera()->stopPreviewSynchronous(); + m_cameraSession->videoOutput()->reset(); + if (m_cameraSession->videoOutput()->surfaceTexture()) + m_cameraSession->camera()->setPreviewTexture(m_cameraSession->videoOutput()->surfaceTexture()); + else if (m_cameraSession->videoOutput()->surfaceHolder()) + m_cameraSession->camera()->setPreviewDisplay(m_cameraSession->videoOutput()->surfaceHolder()); + m_cameraSession->camera()->startPreview(); m_cameraSession->setReadyForCapture(true); } diff --git a/src/plugins/android/src/mediaplayer/mediaplayer.pri b/src/plugins/android/src/mediaplayer/mediaplayer.pri index c386d996b..9f758a993 100644 --- a/src/plugins/android/src/mediaplayer/mediaplayer.pri +++ b/src/plugins/android/src/mediaplayer/mediaplayer.pri @@ -3,9 +3,11 @@ INCLUDEPATH += $$PWD HEADERS += \ $$PWD/qandroidmediaplayercontrol.h \ $$PWD/qandroidmediaservice.h \ - $$PWD/qandroidmetadatareadercontrol.h + $$PWD/qandroidmetadatareadercontrol.h \ + $$PWD/qandroidmediaplayervideorenderercontrol.h SOURCES += \ $$PWD/qandroidmediaplayercontrol.cpp \ $$PWD/qandroidmediaservice.cpp \ - $$PWD/qandroidmetadatareadercontrol.cpp + $$PWD/qandroidmetadatareadercontrol.cpp \ + $$PWD/qandroidmediaplayervideorenderercontrol.cpp diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp index 9a050e7ad..90beeabe7 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp @@ -345,7 +345,7 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent, mReloadingMedia = false; } -void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput) +void QAndroidMediaPlayerControl::setVideoOutput(QAndroidVideoOutput *videoOutput) { if (mVideoOutput) { mMediaPlayer->setDisplay(0); @@ -353,7 +353,7 @@ void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput) mVideoOutput->reset(); } - mVideoOutput = qobject_cast<QAndroidVideoOutput *>(videoOutput); + mVideoOutput = videoOutput; if (!mVideoOutput) return; diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h index 3f92d809c..a015a6809 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h @@ -67,7 +67,7 @@ public: const QIODevice *mediaStream() const Q_DECL_OVERRIDE; void setMedia(const QMediaContent &mediaContent, QIODevice *stream) Q_DECL_OVERRIDE; - void setVideoOutput(QObject *videoOutput); + void setVideoOutput(QAndroidVideoOutput *videoOutput); Q_SIGNALS: void metaDataUpdated(); diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp new file mode 100644 index 000000000..5dd51c395 --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidmediaplayervideorenderercontrol.h" + +#include "qandroidmediaplayercontrol.h" +#include "qandroidvideooutput.h" +#include <qabstractvideosurface.h> + +QT_BEGIN_NAMESPACE + +QAndroidMediaPlayerVideoRendererControl::QAndroidMediaPlayerVideoRendererControl(QAndroidMediaPlayerControl *mediaPlayer, QObject *parent) + : QVideoRendererControl(parent) + , m_mediaPlayerControl(mediaPlayer) + , m_surface(0) + , m_textureOutput(new QAndroidTextureVideoOutput(this)) +{ + m_mediaPlayerControl->setVideoOutput(m_textureOutput); +} + +QAndroidMediaPlayerVideoRendererControl::~QAndroidMediaPlayerVideoRendererControl() +{ + m_mediaPlayerControl->setVideoOutput(0); +} + +QAbstractVideoSurface *QAndroidMediaPlayerVideoRendererControl::surface() const +{ + return m_surface; +} + +void QAndroidMediaPlayerVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + if (m_surface == surface) + return; + + m_surface = surface; + m_textureOutput->setSurface(m_surface); +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h new file mode 100644 index 000000000..cfa41980d --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H +#define QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H + +#include <qvideorenderercontrol.h> + +QT_BEGIN_NAMESPACE + +class QAndroidMediaPlayerControl; +class QAndroidTextureVideoOutput; + +class QAndroidMediaPlayerVideoRendererControl : public QVideoRendererControl +{ + Q_OBJECT +public: + QAndroidMediaPlayerVideoRendererControl(QAndroidMediaPlayerControl *mediaPlayer, QObject *parent = 0); + ~QAndroidMediaPlayerVideoRendererControl() Q_DECL_OVERRIDE; + + QAbstractVideoSurface *surface() const Q_DECL_OVERRIDE; + void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE; + +private: + QAndroidMediaPlayerControl *m_mediaPlayerControl; + QAbstractVideoSurface *m_surface; + QAndroidTextureVideoOutput *m_textureOutput; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp index 74943ca64..992bcead2 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp @@ -35,7 +35,7 @@ #include "qandroidmediaplayercontrol.h" #include "qandroidmetadatareadercontrol.h" -#include "qandroidvideorendercontrol.h" +#include "qandroidmediaplayervideorenderercontrol.h" QT_BEGIN_NAMESPACE @@ -53,9 +53,9 @@ QAndroidMediaService::QAndroidMediaService(QObject *parent) QAndroidMediaService::~QAndroidMediaService() { - delete mMediaControl; - delete mMetadataControl; delete mVideoRendererControl; + delete mMetadataControl; + delete mMediaControl; } QMediaControl *QAndroidMediaService::requestControl(const char *name) @@ -68,8 +68,7 @@ QMediaControl *QAndroidMediaService::requestControl(const char *name) if (qstrcmp(name, QVideoRendererControl_iid) == 0) { if (!mVideoRendererControl) { - mVideoRendererControl = new QAndroidVideoRendererControl; - mMediaControl->setVideoOutput(mVideoRendererControl); + mVideoRendererControl = new QAndroidMediaPlayerVideoRendererControl(mMediaControl); return mVideoRendererControl; } } @@ -80,7 +79,6 @@ QMediaControl *QAndroidMediaService::requestControl(const char *name) void QAndroidMediaService::releaseControl(QMediaControl *control) { if (control == mVideoRendererControl) { - mMediaControl->setVideoOutput(0); delete mVideoRendererControl; mVideoRendererControl = 0; } diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h index 6babbb15f..798d6ef39 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h @@ -40,6 +40,7 @@ QT_BEGIN_NAMESPACE class QAndroidMediaPlayerControl; class QAndroidMetaDataReaderControl; +class QAndroidMediaPlayerVideoRendererControl; class QAndroidMediaService : public QMediaService { @@ -54,7 +55,7 @@ public: private: QAndroidMediaPlayerControl *mMediaControl; QAndroidMetaDataReaderControl *mMetadataControl; - QMediaControl *mVideoRendererControl; + QAndroidMediaPlayerVideoRendererControl *mVideoRendererControl; }; QT_END_NAMESPACE diff --git a/src/plugins/android/src/qandroidmediaserviceplugin.cpp b/src/plugins/android/src/qandroidmediaserviceplugin.cpp index 5d35ddf51..bf89badb3 100644 --- a/src/plugins/android/src/qandroidmediaserviceplugin.cpp +++ b/src/plugins/android/src/qandroidmediaserviceplugin.cpp @@ -43,6 +43,7 @@ #include "androidcamera.h" #include "androidmultimediautils.h" #include "androidmediarecorder.h" +#include "androidsurfaceview.h" #include <qdebug.h> QT_BEGIN_NAMESPACE @@ -160,7 +161,8 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) if (!AndroidMediaPlayer::initJNI(jniEnv) || !AndroidCamera::initJNI(jniEnv) || - !AndroidMediaRecorder::initJNI(jniEnv)) { + !AndroidMediaRecorder::initJNI(jniEnv) || + !AndroidSurfaceHolder::initJNI(jniEnv)) { return JNI_ERR; } diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.cpp b/src/plugins/android/src/wrappers/jni/androidcamera.cpp index 7d8e4532d..23200462e 100644 --- a/src/plugins/android/src/wrappers/jni/androidcamera.cpp +++ b/src/plugins/android/src/wrappers/jni/androidcamera.cpp @@ -33,20 +33,24 @@ #include "androidcamera.h" #include "androidsurfacetexture.h" +#include "androidsurfaceview.h" #include "qandroidmultimediautils.h" #include <qstringlist.h> #include <qdebug.h> -#include <qmutex.h> #include <QtCore/private/qjnihelpers_p.h> #include <QtCore/qthread.h> +#include <QtCore/qreadwritelock.h> +#include <QtCore/qmutex.h> +#include <QtMultimedia/private/qmemoryvideobuffer_p.h> QT_BEGIN_NAMESPACE static const char QtCameraListenerClassName[] = "org/qtproject/qt5/android/multimedia/QtCameraListener"; -static QMutex g_cameraMapMutex; -typedef QMap<int, AndroidCamera *> CameraMap; -Q_GLOBAL_STATIC(CameraMap, g_cameraMap) + +typedef QHash<int, AndroidCamera *> CameraMap; +Q_GLOBAL_STATIC(CameraMap, cameras) +Q_GLOBAL_STATIC(QReadWriteLock, rwLock) static inline bool exceptionCheckAndClear(JNIEnv *env) { @@ -88,43 +92,57 @@ static QJNIObjectPrivate rectToArea(const QRect &rect) // native method for QtCameraLisener.java static void notifyAutoFocusComplete(JNIEnv* , jobject, int id, jboolean success) { - QMutexLocker locker(&g_cameraMapMutex); - AndroidCamera *obj = g_cameraMap->value(id, 0); - if (obj) - Q_EMIT obj->autoFocusComplete(success); + QReadLocker locker(rwLock); + const auto it = cameras->constFind(id); + if (Q_UNLIKELY(it == cameras->cend())) + return; + + Q_EMIT (*it)->autoFocusComplete(success); } static void notifyPictureExposed(JNIEnv* , jobject, int id) { - QMutexLocker locker(&g_cameraMapMutex); - AndroidCamera *obj = g_cameraMap->value(id, 0); - if (obj) - Q_EMIT obj->pictureExposed(); + QReadLocker locker(rwLock); + const auto it = cameras->constFind(id); + if (Q_UNLIKELY(it == cameras->cend())) + return; + + Q_EMIT (*it)->pictureExposed(); } static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data) { - QMutexLocker locker(&g_cameraMapMutex); - AndroidCamera *obj = g_cameraMap->value(id, 0); - if (obj) { - const int arrayLength = env->GetArrayLength(data); - QByteArray bytes(arrayLength, Qt::Uninitialized); - env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); - Q_EMIT obj->pictureCaptured(bytes); - } + QReadLocker locker(rwLock); + const auto it = cameras->constFind(id); + if (Q_UNLIKELY(it == cameras->cend())) + return; + + const int arrayLength = env->GetArrayLength(data); + QByteArray bytes(arrayLength, Qt::Uninitialized); + env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); + Q_EMIT (*it)->pictureCaptured(bytes); } -static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data, int width, int height) +static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data, + int width, int height, int format, int bpl) { - QMutexLocker locker(&g_cameraMapMutex); - AndroidCamera *obj = g_cameraMap->value(id, 0); - if (obj) { - const int arrayLength = env->GetArrayLength(data); - QByteArray bytes(arrayLength, Qt::Uninitialized); - env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); + QReadLocker locker(rwLock); + const auto it = cameras->constFind(id); + if (Q_UNLIKELY(it == cameras->cend())) + return; - Q_EMIT obj->newPreviewFrame(bytes, width, height); - } + const int arrayLength = env->GetArrayLength(data); + if (arrayLength == 0) + return; + + QByteArray bytes(arrayLength, Qt::Uninitialized); + env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); + + QVideoFrame frame(new QMemoryVideoBuffer(bytes, bpl), + QSize(width, height), + qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format))); + + Q_EMIT (*it)->newPreviewFrame(frame); } class AndroidCameraPrivate : public QObject @@ -149,10 +167,12 @@ public: Q_INVOKABLE AndroidCamera::ImageFormat getPreviewFormat(); Q_INVOKABLE void setPreviewFormat(AndroidCamera::ImageFormat fmt); + Q_INVOKABLE QList<AndroidCamera::ImageFormat> getSupportedPreviewFormats(); Q_INVOKABLE QSize previewSize() const { return m_previewSize; } Q_INVOKABLE void updatePreviewSize(); Q_INVOKABLE bool setPreviewTexture(void *surfaceTexture); + Q_INVOKABLE bool setPreviewDisplay(void *surfaceHolder); Q_INVOKABLE bool isZoomSupported(); Q_INVOKABLE int getMaxZoom(); @@ -233,7 +253,7 @@ Q_SIGNALS: void takePictureFailed(); - void lastPreviewFrameFetched(const QByteArray &preview, int width, int height); + void lastPreviewFrameFetched(const QVideoFrame &frame); }; AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker) @@ -245,6 +265,7 @@ AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker) qRegisterMetaType<QList<int> >(); qRegisterMetaType<QList<QSize> >(); qRegisterMetaType<QList<QRect> >(); + qRegisterMetaType<ImageFormat>(); connect(d, &AndroidCameraPrivate::previewSizeChanged, this, &AndroidCamera::previewSizeChanged); connect(d, &AndroidCameraPrivate::previewStarted, this, &AndroidCamera::previewStarted); @@ -260,12 +281,11 @@ AndroidCamera::~AndroidCamera() { Q_D(AndroidCamera); if (d->m_camera.isValid()) { - g_cameraMapMutex.lock(); - g_cameraMap->remove(d->m_cameraId); - g_cameraMapMutex.unlock(); + release(); + QWriteLocker locker(rwLock); + cameras->remove(cameraId()); } - release(); m_worker->exit(); m_worker->wait(5000); } @@ -288,9 +308,9 @@ AndroidCamera *AndroidCamera::open(int cameraId) } AndroidCamera *q = new AndroidCamera(d, worker); - g_cameraMapMutex.lock(); - g_cameraMap->insert(cameraId, q); - g_cameraMapMutex.unlock(); + QWriteLocker locker(rwLock); + cameras->insert(cameraId, q); + return q; } @@ -366,6 +386,12 @@ void AndroidCamera::setPreviewFormat(ImageFormat fmt) QMetaObject::invokeMethod(d, "setPreviewFormat", Q_ARG(AndroidCamera::ImageFormat, fmt)); } +QList<AndroidCamera::ImageFormat> AndroidCamera::getSupportedPreviewFormats() +{ + Q_D(AndroidCamera); + return d->getSupportedPreviewFormats(); +} + QSize AndroidCamera::previewSize() const { Q_D(const AndroidCamera); @@ -397,6 +423,18 @@ bool AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture) return ok; } +bool AndroidCamera::setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder) +{ + Q_D(AndroidCamera); + bool ok = true; + QMetaObject::invokeMethod(d, + "setPreviewDisplay", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, ok), + Q_ARG(void *, surfaceHolder ? surfaceHolder->surfaceHolder() : 0)); + return ok; +} + bool AndroidCamera::isZoomSupported() { Q_D(AndroidCamera); @@ -711,6 +749,12 @@ void AndroidCamera::stopPreview() QMetaObject::invokeMethod(d, "stopPreview"); } +void AndroidCamera::stopPreviewSynchronous() +{ + Q_D(AndroidCamera); + QMetaObject::invokeMethod(d, "stopPreview", Qt::BlockingQueuedConnection); +} + AndroidCameraPrivate::AndroidCameraPrivate() : QObject(), m_parametersMutex(QMutex::Recursive) @@ -832,7 +876,7 @@ AndroidCamera::ImageFormat AndroidCameraPrivate::getPreviewFormat() QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) - return AndroidCamera::Unknown; + return AndroidCamera::UnknownImageFormat; return AndroidCamera::ImageFormat(m_parameters.callMethod<jint>("getPreviewFormat")); } @@ -848,6 +892,27 @@ void AndroidCameraPrivate::setPreviewFormat(AndroidCamera::ImageFormat fmt) applyParameters(); } +QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats() +{ + QList<AndroidCamera::ImageFormat> list; + + QMutexLocker parametersLocker(&m_parametersMutex); + + if (m_parameters.isValid()) { + QJNIObjectPrivate formatList = m_parameters.callObjectMethod("getSupportedPreviewFormats", + "()Ljava/util/List;"); + int count = formatList.callMethod<jint>("size"); + for (int i = 0; i < count; ++i) { + QJNIObjectPrivate format = formatList.callObjectMethod("get", + "(I)Ljava/lang/Object;", + i); + list.append(AndroidCamera::ImageFormat(format.callMethod<jint>("intValue"))); + } + } + + return list; +} + void AndroidCameraPrivate::updatePreviewSize() { QMutexLocker parametersLocker(&m_parametersMutex); @@ -869,6 +934,15 @@ bool AndroidCameraPrivate::setPreviewTexture(void *surfaceTexture) return !exceptionCheckAndClear(env); } +bool AndroidCameraPrivate::setPreviewDisplay(void *surfaceHolder) +{ + QJNIEnvironmentPrivate env; + m_camera.callMethod<void>("setPreviewDisplay", + "(Landroid/view/SurfaceHolder;)V", + static_cast<jobject>(surfaceHolder)); + return !exceptionCheckAndClear(env); +} + bool AndroidCameraPrivate::isZoomSupported() { QMutexLocker parametersLocker(&m_parametersMutex); @@ -1380,15 +1454,25 @@ void AndroidCameraPrivate::fetchLastPreviewFrame() return; const int arrayLength = env->GetArrayLength(static_cast<jbyteArray>(data.object())); + if (arrayLength == 0) + return; + QByteArray bytes(arrayLength, Qt::Uninitialized); env->GetByteArrayRegion(static_cast<jbyteArray>(data.object()), 0, arrayLength, reinterpret_cast<jbyte *>(bytes.data())); - emit lastPreviewFrameFetched(bytes, - m_cameraListener.callMethod<jint>("previewWidth"), - m_cameraListener.callMethod<jint>("previewHeight")); + const int width = m_cameraListener.callMethod<jint>("previewWidth"); + const int height = m_cameraListener.callMethod<jint>("previewHeight"); + const int format = m_cameraListener.callMethod<jint>("previewFormat"); + const int bpl = m_cameraListener.callMethod<jint>("previewBytesPerLine"); + + QVideoFrame frame(new QMemoryVideoBuffer(bytes, bpl), + QSize(width, height), + qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format))); + + emit lastPreviewFrameFetched(frame); } void AndroidCameraPrivate::applyParameters() @@ -1433,7 +1517,7 @@ bool AndroidCamera::initJNI(JNIEnv *env) {"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete}, {"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed}, {"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured}, - {"notifyNewPreviewFrame", "(I[BII)V", (void *)notifyNewPreviewFrame} + {"notifyNewPreviewFrame", "(I[BIIII)V", (void *)notifyNewPreviewFrame} }; if (clazz && env->RegisterNatives(clazz, diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.h b/src/plugins/android/src/wrappers/jni/androidcamera.h index b5903688d..a5e0294c0 100644 --- a/src/plugins/android/src/wrappers/jni/androidcamera.h +++ b/src/plugins/android/src/wrappers/jni/androidcamera.h @@ -46,6 +46,7 @@ class QThread; class AndroidCameraPrivate; class AndroidSurfaceTexture; +class AndroidSurfaceHolder; struct AndroidCameraInfo { @@ -67,7 +68,7 @@ public: }; enum ImageFormat { // same values as in android.graphics.ImageFormat Java class - Unknown = 0, + UnknownImageFormat = 0, RGB565 = 4, NV16 = 16, NV21 = 17, @@ -95,10 +96,12 @@ public: ImageFormat getPreviewFormat(); void setPreviewFormat(ImageFormat fmt); + QList<ImageFormat> getSupportedPreviewFormats(); QSize previewSize() const; void setPreviewSize(const QSize &size); bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture); + bool setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder); bool isZoomSupported(); int getMaxZoom(); @@ -152,6 +155,7 @@ public: void startPreview(); void stopPreview(); + void stopPreviewSynchronous(); void takePicture(); @@ -179,8 +183,8 @@ Q_SIGNALS: void takePictureFailed(); void pictureExposed(); void pictureCaptured(const QByteArray &data); - void lastPreviewFrameFetched(const QByteArray &preview, int width, int height); - void newPreviewFrame(const QByteArray &frame, int width, int height); + void lastPreviewFrameFetched(const QVideoFrame &frame); + void newPreviewFrame(const QVideoFrame &frame); private: AndroidCamera(AndroidCameraPrivate *d, QThread *worker); @@ -190,6 +194,8 @@ private: QScopedPointer<QThread> m_worker; }; +Q_DECLARE_METATYPE(AndroidCamera::ImageFormat) + QT_END_NAMESPACE #endif // ANDROIDCAMERA_H diff --git a/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp b/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp index fa32f31ef..34063056f 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp +++ b/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp @@ -34,6 +34,8 @@ #include "androidmediarecorder.h" #include "androidcamera.h" +#include "androidsurfacetexture.h" +#include "androidsurfaceview.h" #include <QtCore/private/qjni_p.h> #include <qmap.h> @@ -339,6 +341,41 @@ void AndroidMediaRecorder::setOutputFile(const QString &path) } } +void AndroidMediaRecorder::setSurfaceTexture(AndroidSurfaceTexture *texture) +{ + QJNIEnvironmentPrivate env; + m_mediaRecorder.callMethod<void>("setPreviewDisplay", + "(Landroid/view/Surface;)V", + texture->surface()); + if (env->ExceptionCheck()) { +#ifdef QT_DEBUG + env->ExceptionDescribe(); +#endif + env->ExceptionClear(); + } +} + +void AndroidMediaRecorder::setSurfaceHolder(AndroidSurfaceHolder *holder) +{ + QJNIEnvironmentPrivate env; + QJNIObjectPrivate surfaceHolder(holder->surfaceHolder()); + QJNIObjectPrivate surface = surfaceHolder.callObjectMethod("getSurface", + "()Landroid/view/Surface;"); + if (!surface.isValid()) + return; + + m_mediaRecorder.callMethod<void>("setPreviewDisplay", + "(Landroid/view/Surface;)V", + surface.object()); + if (env->ExceptionCheck()) { +#ifdef QT_DEBUG + env->ExceptionDescribe(); +#endif + env->ExceptionClear(); + } +} + + bool AndroidMediaRecorder::initJNI(JNIEnv *env) { jclass clazz = QJNIEnvironmentPrivate::findClass(QtMediaRecorderListenerClassName, diff --git a/src/plugins/android/src/wrappers/jni/androidmediarecorder.h b/src/plugins/android/src/wrappers/jni/androidmediarecorder.h index 1aa83a201..95b48ed47 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediarecorder.h +++ b/src/plugins/android/src/wrappers/jni/androidmediarecorder.h @@ -41,6 +41,8 @@ QT_BEGIN_NAMESPACE class AndroidCamera; +class AndroidSurfaceTexture; +class AndroidSurfaceHolder; class AndroidCamcorderProfile { @@ -149,6 +151,9 @@ public: void setOutputFormat(OutputFormat format); void setOutputFile(const QString &path); + void setSurfaceTexture(AndroidSurfaceTexture *texture); + void setSurfaceHolder(AndroidSurfaceHolder *holder); + static bool initJNI(JNIEnv *env); Q_SIGNALS: diff --git a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp index ffa37d7d4..9a25b7e28 100644 --- a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp +++ b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp @@ -78,8 +78,8 @@ AndroidSurfaceTexture::AndroidSurfaceTexture(unsigned int texName) AndroidSurfaceTexture::~AndroidSurfaceTexture() { - if (QtAndroidPrivate::androidSdkVersion() > 13 && m_surfaceView.isValid()) - m_surfaceView.callMethod<void>("release"); + if (QtAndroidPrivate::androidSdkVersion() > 13 && m_surface.isValid()) + m_surface.callMethod<void>("release"); if (m_surfaceTexture.isValid()) { release(); @@ -124,21 +124,23 @@ jobject AndroidSurfaceTexture::surfaceTexture() return m_surfaceTexture.object(); } -jobject AndroidSurfaceTexture::surfaceView() +jobject AndroidSurfaceTexture::surface() { - return m_surfaceView.object(); + if (!m_surface.isValid()) { + m_surface = QJNIObjectPrivate("android/view/Surface", + "(Landroid/graphics/SurfaceTexture;)V", + m_surfaceTexture.object()); + } + + return m_surface.object(); } jobject AndroidSurfaceTexture::surfaceHolder() { if (!m_surfaceHolder.isValid()) { - m_surfaceView = QJNIObjectPrivate("android/view/Surface", - "(Landroid/graphics/SurfaceTexture;)V", - m_surfaceTexture.object()); - m_surfaceHolder = QJNIObjectPrivate("org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder", "(Landroid/view/Surface;)V", - m_surfaceView.object()); + surface()); } return m_surfaceHolder.object(); diff --git a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h index 2618ed6c9..ac2af694e 100644 --- a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h +++ b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h @@ -50,7 +50,7 @@ public: int textureID() const { return m_texID; } jobject surfaceTexture(); - jobject surfaceView(); + jobject surface(); jobject surfaceHolder(); inline bool isValid() const { return m_surfaceTexture.isValid(); } @@ -66,7 +66,7 @@ Q_SIGNALS: private: int m_texID; QJNIObjectPrivate m_surfaceTexture; - QJNIObjectPrivate m_surfaceView; + QJNIObjectPrivate m_surface; QJNIObjectPrivate m_surfaceHolder; }; diff --git a/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp b/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp new file mode 100644 index 000000000..67560baf4 --- /dev/null +++ b/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "androidsurfaceview.h" + +#include <QtCore/private/qjnihelpers_p.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qvector.h> +#include <QtCore/qdebug.h> +#include <QtCore/qmutex.h> +#include <QtGui/qwindow.h> + +QT_BEGIN_NAMESPACE + +static const char QtSurfaceHolderCallbackClassName[] = "org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback"; +typedef QVector<AndroidSurfaceHolder *> SurfaceHolders; +Q_GLOBAL_STATIC(SurfaceHolders, surfaceHolders) +Q_GLOBAL_STATIC(QMutex, shLock) + +AndroidSurfaceHolder::AndroidSurfaceHolder(QJNIObjectPrivate object) + : m_surfaceHolder(object) + , m_surfaceCreated(false) +{ + if (!m_surfaceHolder.isValid()) + return; + + { + QMutexLocker locker(shLock); + surfaceHolders->append(this); + } + + QJNIObjectPrivate callback(QtSurfaceHolderCallbackClassName, "(J)V", reinterpret_cast<jlong>(this)); + m_surfaceHolder.callMethod<void>("addCallback", + "(Landroid/view/SurfaceHolder$Callback;)V", + callback.object()); +} + +AndroidSurfaceHolder::~AndroidSurfaceHolder() +{ + QMutexLocker locker(shLock); + const int i = surfaceHolders->indexOf(this); + if (Q_UNLIKELY(i == -1)) + return; + + surfaceHolders->remove(i); +} + +jobject AndroidSurfaceHolder::surfaceHolder() const +{ + return m_surfaceHolder.object(); +} + +bool AndroidSurfaceHolder::isSurfaceCreated() const +{ + QMutexLocker locker(shLock); + return m_surfaceCreated; +} + +void AndroidSurfaceHolder::handleSurfaceCreated(JNIEnv*, jobject, jlong id) +{ + QMutexLocker locker(shLock); + const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id)); + if (Q_UNLIKELY(i == -1)) + return; + + (*surfaceHolders)[i]->m_surfaceCreated = true; + Q_EMIT (*surfaceHolders)[i]->surfaceCreated(); +} + +void AndroidSurfaceHolder::handleSurfaceDestroyed(JNIEnv*, jobject, jlong id) +{ + QMutexLocker locker(shLock); + const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id)); + if (Q_UNLIKELY(i == -1)) + return; + + (*surfaceHolders)[i]->m_surfaceCreated = false; +} + +bool AndroidSurfaceHolder::initJNI(JNIEnv *env) +{ + jclass clazz = QJNIEnvironmentPrivate::findClass(QtSurfaceHolderCallbackClassName, + env); + + static const JNINativeMethod methods[] = { + {"notifySurfaceCreated", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceCreated}, + {"notifySurfaceDestroyed", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceDestroyed} + }; + + if (clazz && env->RegisterNatives(clazz, + methods, + sizeof(methods) / sizeof(methods[0])) != JNI_OK) { + return false; + } + + return true; +} + +AndroidSurfaceView::AndroidSurfaceView() + : m_window(0) + , m_surfaceHolder(0) + , m_pendingVisible(-1) +{ + setAutoDelete(false); + QtAndroidPrivate::runOnUiThread(this, QJNIEnvironmentPrivate()); +} + +AndroidSurfaceView::~AndroidSurfaceView() +{ + delete m_surfaceHolder; + delete m_window; +} + +AndroidSurfaceHolder *AndroidSurfaceView::holder() const +{ + return m_surfaceHolder; +} + +void AndroidSurfaceView::setVisible(bool v) +{ + if (m_window) + m_window->setVisible(v); + else + m_pendingVisible = int(v); +} + +void AndroidSurfaceView::setGeometry(int x, int y, int width, int height) +{ + if (m_window) + m_window->setGeometry(x, y, width, height); + else + m_pendingGeometry = QRect(x, y, width, height); +} + +bool AndroidSurfaceView::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + Q_ASSERT(m_surfaceView.isValid()); + + QJNIObjectPrivate holder = m_surfaceView.callObjectMethod("getHolder", + "()Landroid/view/SurfaceHolder;"); + if (!holder.isValid()) { + m_surfaceView = QJNIObjectPrivate(); + } else { + m_surfaceHolder = new AndroidSurfaceHolder(holder); + connect(m_surfaceHolder, &AndroidSurfaceHolder::surfaceCreated, + this, &AndroidSurfaceView::surfaceCreated); + { // Lock now to avoid a race with handleSurfaceCreated() + QMutexLocker locker(shLock); + m_window = QWindow::fromWinId(WId(m_surfaceView.object())); + + if (m_pendingVisible != -1) + m_window->setVisible(m_pendingVisible); + if (m_pendingGeometry.isValid()) + m_window->setGeometry(m_pendingGeometry); + } + } + + return true; + } + + return QObject::event(e); +} + +// Called on the Android UI thread. +void AndroidSurfaceView::run() +{ + m_surfaceView = QJNIObjectPrivate("android/view/SurfaceView", + "(Landroid/content/Context;)V", + QtAndroidPrivate::activity()); + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/src/wrappers/jni/androidsurfaceview.h b/src/plugins/android/src/wrappers/jni/androidsurfaceview.h new file mode 100644 index 000000000..661a5959f --- /dev/null +++ b/src/plugins/android/src/wrappers/jni/androidsurfaceview.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROIDSURFACEVIEW_H +#define ANDROIDSURFACEVIEW_H + +#include <QtCore/private/qjni_p.h> +#include <qrect.h> +#include <QtCore/qrunnable.h> + +QT_BEGIN_NAMESPACE + +class QWindow; + +class AndroidSurfaceHolder : public QObject +{ + Q_OBJECT +public: + ~AndroidSurfaceHolder(); + + jobject surfaceHolder() const; + bool isSurfaceCreated() const; + + static bool initJNI(JNIEnv *env); + +Q_SIGNALS: + void surfaceCreated(); + +private: + AndroidSurfaceHolder(QJNIObjectPrivate object); + + static void handleSurfaceCreated(JNIEnv*, jobject, jlong id); + static void handleSurfaceDestroyed(JNIEnv*, jobject, jlong id); + + QJNIObjectPrivate m_surfaceHolder; + bool m_surfaceCreated; + + friend class AndroidSurfaceView; +}; + +class AndroidSurfaceView : public QObject, public QRunnable +{ + Q_OBJECT +public: + AndroidSurfaceView(); + ~AndroidSurfaceView(); + + AndroidSurfaceHolder *holder() const; + + void setVisible(bool v); + void setGeometry(int x, int y, int width, int height); + + bool event(QEvent *); + +Q_SIGNALS: + void surfaceCreated(); + +protected: + void run() override; + +private: + QJNIObjectPrivate m_surfaceView; + QWindow *m_window; + AndroidSurfaceHolder *m_surfaceHolder; + int m_pendingVisible; + QRect m_pendingGeometry; +}; + +QT_END_NAMESPACE + +#endif // ANDROIDSURFACEVIEW_H diff --git a/src/plugins/android/src/wrappers/jni/jni.pri b/src/plugins/android/src/wrappers/jni/jni.pri index ba2ad0801..930d7e922 100644 --- a/src/plugins/android/src/wrappers/jni/jni.pri +++ b/src/plugins/android/src/wrappers/jni/jni.pri @@ -1,4 +1,4 @@ -QT += platformsupport-private +QT += core-private INCLUDEPATH += $$PWD @@ -8,7 +8,8 @@ HEADERS += \ $$PWD/androidmediametadataretriever.h \ $$PWD/androidcamera.h \ $$PWD/androidmultimediautils.h \ - $$PWD/androidmediarecorder.h + $$PWD/androidmediarecorder.h \ + $$PWD/androidsurfaceview.h SOURCES += \ $$PWD/androidmediaplayer.cpp \ @@ -16,4 +17,5 @@ SOURCES += \ $$PWD/androidmediametadataretriever.cpp \ $$PWD/androidcamera.cpp \ $$PWD/androidmultimediautils.cpp \ - $$PWD/androidmediarecorder.cpp + $$PWD/androidmediarecorder.cpp \ + $$PWD/androidsurfaceview.cpp |