2023-03-25 08:37:51 +08:00
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2023-04-02 01:51:36 +08:00
|
|
|
#if !defined(AXME_NO_AXMOL)
|
2023-06-11 13:08:08 +08:00
|
|
|
# include "base/Console.h"
|
|
|
|
# include "platform/PlatformMacros.h"
|
2023-04-02 01:51:36 +08:00
|
|
|
# define AXME_TRACE AXLOG
|
|
|
|
#else
|
|
|
|
# define AXME_TRACE printf
|
|
|
|
# define NS_AX_BEGIN \
|
|
|
|
namespace ax \
|
|
|
|
{
|
|
|
|
# define NS_AX_END }
|
|
|
|
# define AX_BREAK_IF(cond) \
|
|
|
|
if (cond) \
|
|
|
|
break
|
2023-06-10 22:05:49 +08:00
|
|
|
# define USING_NS_AX using namespace ax
|
2023-04-02 01:51:36 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// #define AXME_USE_IMFME 1
|
|
|
|
|
|
|
|
#if __has_include(<winapifamily.h>)
|
|
|
|
# include <winapifamily.h>
|
|
|
|
#endif
|
|
|
|
|
2023-03-25 08:37:51 +08:00
|
|
|
#include <functional>
|
|
|
|
#include <memory>
|
2023-04-02 01:51:36 +08:00
|
|
|
#include <chrono>
|
2023-06-10 22:05:49 +08:00
|
|
|
|
2023-06-24 21:18:27 +08:00
|
|
|
#include "yasio/string_view.hpp"
|
2023-06-24 09:17:14 +08:00
|
|
|
#include "yasio/byte_buffer.hpp"
|
2023-03-25 08:37:51 +08:00
|
|
|
|
2023-04-02 01:51:36 +08:00
|
|
|
using namespace std::string_view_literals;
|
|
|
|
|
2023-03-25 08:37:51 +08:00
|
|
|
NS_AX_BEGIN
|
|
|
|
|
|
|
|
enum class MEMediaEventType
|
|
|
|
{
|
|
|
|
Playing = 0,
|
|
|
|
Paused,
|
|
|
|
Stopped,
|
|
|
|
Error,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Possible states of media playback.
|
|
|
|
*/
|
|
|
|
enum class MEMediaState
|
|
|
|
{
|
|
|
|
/** Media has been closed and cannot be played again. */
|
|
|
|
Closed,
|
|
|
|
|
2023-06-10 22:05:49 +08:00
|
|
|
/** Media is being prepared for playback. */
|
|
|
|
Preparing,
|
2023-03-25 08:37:51 +08:00
|
|
|
|
|
|
|
/** Media is currently playing. */
|
|
|
|
Playing,
|
|
|
|
|
2023-06-10 22:05:49 +08:00
|
|
|
/** Playback has been paused, but can be resumed. */
|
|
|
|
Paused,
|
2023-03-25 08:37:51 +08:00
|
|
|
|
|
|
|
/** Playback has been stopped, but can be restarted. */
|
|
|
|
Stopped,
|
|
|
|
|
2023-06-10 22:05:49 +08:00
|
|
|
/** Unrecoverable error occurred during playback. */
|
|
|
|
Error,
|
2023-03-25 08:37:51 +08:00
|
|
|
};
|
|
|
|
|
2023-04-02 01:51:36 +08:00
|
|
|
/**
|
|
|
|
* SampleVideo: (1928x1080)
|
|
|
|
* - YUY2,RGB32,BGR32: works well
|
|
|
|
* - NV12: has green border
|
|
|
|
* - Y420V/F: on apple, needs test
|
|
|
|
*/
|
|
|
|
|
|
|
|
enum class MEVideoPixelFormat
|
2023-03-25 08:37:51 +08:00
|
|
|
{
|
2023-04-02 01:51:36 +08:00
|
|
|
INVALID,
|
2023-03-25 08:37:51 +08:00
|
|
|
YUY2,
|
2023-04-02 01:51:36 +08:00
|
|
|
NV12, // '420v' '420f'
|
2023-03-25 08:37:51 +08:00
|
|
|
RGB32,
|
|
|
|
BGR32,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MEIntPoint
|
|
|
|
{
|
|
|
|
MEIntPoint() : x(0), y(0) {}
|
|
|
|
MEIntPoint(int x_, int y_) : x(x_), y(y_) {}
|
2023-06-10 22:05:49 +08:00
|
|
|
bool equals(const MEIntPoint& rhs) const { return this->x == rhs.x && this->y == rhs.y; }
|
|
|
|
void set(int x_, int y_)
|
|
|
|
{
|
|
|
|
this->x = x_;
|
|
|
|
this->y = y_;
|
|
|
|
}
|
|
|
|
|
2023-03-25 08:37:51 +08:00
|
|
|
int x;
|
|
|
|
int y;
|
|
|
|
};
|
|
|
|
|
2023-04-02 01:51:36 +08:00
|
|
|
#if defined(_DEBUG) || !defined(_NDEBUG)
|
|
|
|
struct YCbCrBiPlanarPixelInfo
|
|
|
|
{
|
|
|
|
unsigned int YPitch = 0;
|
|
|
|
MEIntPoint YDim;
|
|
|
|
unsigned int CbCrPitch = 0;
|
|
|
|
MEIntPoint CbCrDim;
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* RGB32/BGR32: _dim==_videoDim
|
|
|
|
* H264(YUY2):
|
|
|
|
* LumaTexture(LA8, RG8):
|
|
|
|
* - _dim.x = ALIGN(_videoDim.x, 16),
|
|
|
|
* - _dim.y = _videoDim.y
|
|
|
|
* CHromaTexture(RGBA8)
|
|
|
|
* - chromaDim.x = _dim.x / 2
|
|
|
|
* - chromaDim.y = _dim.y
|
|
|
|
* NV12/HEVC:
|
|
|
|
* LumaTexture(
|
|
|
|
* - _dim.x = ALIGN(_videoDim.x, 32)
|
|
|
|
* - _dim.y = ALIGN(_videoDim.y, 32)
|
|
|
|
* ChromaTexture(RG8)
|
|
|
|
* - chromaDim.x = _dim.x / 2
|
|
|
|
* - chromaDim.y = _dim.y / 2
|
|
|
|
*/
|
|
|
|
struct MEVideoPixelDesc
|
|
|
|
{
|
|
|
|
MEVideoPixelDesc() : _PF(MEVideoPixelFormat::INVALID), _dim() {}
|
|
|
|
MEVideoPixelDesc(MEVideoPixelFormat pixelFormat, const MEIntPoint& dim) : _PF(pixelFormat), _dim(dim) {}
|
|
|
|
MEVideoPixelFormat _PF; // the pixel format
|
|
|
|
MEIntPoint _dim; // the aligned frame size
|
|
|
|
bool _fullRange = true;
|
|
|
|
bool equals(const MEVideoPixelDesc& rhs) const
|
|
|
|
{
|
|
|
|
return _dim.equals(rhs._dim) && _PF == rhs._PF && _fullRange == rhs._fullRange;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MEVideoFrame
|
2023-03-25 08:37:51 +08:00
|
|
|
{
|
2023-04-02 01:51:36 +08:00
|
|
|
MEVideoFrame(const uint8_t* data,
|
|
|
|
const uint8_t* cbcrData,
|
|
|
|
size_t len,
|
|
|
|
const MEVideoPixelDesc& vpd,
|
|
|
|
const MEIntPoint& videoDim)
|
|
|
|
: _vpd(vpd), _dataPointer(data), _cbcrDataPointer(cbcrData), _dataLen(len), _videoDim(videoDim){};
|
|
|
|
const uint8_t* _dataPointer; // the video data
|
|
|
|
const size_t _dataLen; // the video data len
|
|
|
|
const uint8_t* _cbcrDataPointer;
|
|
|
|
MEVideoPixelDesc _vpd; // the video pixel desc
|
|
|
|
MEIntPoint _videoDim; // the aligned frame size
|
|
|
|
#if defined(_DEBUG) || !defined(_NDEBUG)
|
|
|
|
YCbCrBiPlanarPixelInfo _ycbcrDesc{};
|
|
|
|
#endif
|
2023-03-25 08:37:51 +08:00
|
|
|
};
|
|
|
|
|
2023-04-02 01:51:36 +08:00
|
|
|
//
|
2023-06-10 22:05:49 +08:00
|
|
|
// file uri helper: https://www.ietf.org/rfc/rfc3986.txt
|
|
|
|
//
|
|
|
|
static constexpr std::string_view LOCAL_FILE_URI_PREFIX = "file:///"sv; // The localhost file prefix
|
|
|
|
|
|
|
|
inline std::string& path2uri(std::string& path)
|
|
|
|
{
|
|
|
|
// windows: file:///D:/xxx/xxx.mp4
|
|
|
|
// unix: file:///home/xxx/xxx.mp4
|
|
|
|
// android_asset:
|
|
|
|
// - file:///android_asset/xxx/xxx.mp4
|
|
|
|
// - asset://android_asset/xxx/xxx.mp4
|
|
|
|
if (!path.empty())
|
|
|
|
{
|
|
|
|
if (path[0] == '/')
|
|
|
|
path.insert(0, LOCAL_FILE_URI_PREFIX.data(), LOCAL_FILE_URI_PREFIX.length() - 1);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!cxx20::starts_with(path, "assets/"sv)) // not android asset
|
|
|
|
path.insert(0, LOCAL_FILE_URI_PREFIX.data(), LOCAL_FILE_URI_PREFIX.length());
|
|
|
|
else
|
|
|
|
path.replace(0, "assets/"sv.length(), "file:///android_asset/");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2023-04-02 01:51:36 +08:00
|
|
|
//
|
2023-06-10 22:05:49 +08:00
|
|
|
// redisigned corss-platform MediaEngine, inspired from microsoft media foundation: IMFMediaEngine
|
2023-06-28 16:17:55 +08:00
|
|
|
//
|
2023-03-25 08:37:51 +08:00
|
|
|
class MediaEngine
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual ~MediaEngine() {}
|
2023-06-10 22:05:49 +08:00
|
|
|
virtual void setCallbacks(std::function<void(MEMediaEventType)> onMediaEvent,
|
|
|
|
std::function<void(const MEVideoFrame&)> onVideoFrame) = 0;
|
|
|
|
virtual void setAutoPlay(bool bAutoPlay) = 0;
|
|
|
|
virtual bool open(std::string_view sourceUri) = 0;
|
|
|
|
virtual bool close() = 0;
|
|
|
|
virtual bool setLoop(bool bLooping) = 0;
|
|
|
|
virtual bool setRate(double fRate) = 0;
|
|
|
|
virtual bool setCurrentTime(double fSeekTimeInSec) = 0;
|
|
|
|
virtual bool play() = 0;
|
|
|
|
virtual bool pause() = 0;
|
|
|
|
virtual bool stop() = 0;
|
|
|
|
virtual bool isPlaybackEnded() const = 0;
|
|
|
|
virtual MEMediaState getState() const = 0;
|
|
|
|
virtual bool transferVideoFrame() = 0;
|
2023-03-25 08:37:51 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class MediaEngineFactory
|
|
|
|
{
|
|
|
|
public:
|
2023-06-28 16:17:55 +08:00
|
|
|
virtual ~MediaEngineFactory() {}
|
2023-06-10 22:05:49 +08:00
|
|
|
static std::unique_ptr<MediaEngineFactory> create();
|
2023-06-28 16:17:55 +08:00
|
|
|
|
2023-06-10 22:05:49 +08:00
|
|
|
virtual MediaEngine* createMediaEngine() = 0;
|
|
|
|
virtual void destroyMediaEngine(MediaEngine* me) = 0;
|
2023-03-25 08:37:51 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
NS_AX_END
|