mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2026-06-18 14:51:30 +00:00
Add H.265 video support
This commit is contained in:
@@ -15,6 +15,7 @@ STREAM_CONFIGURATION StreamConfig;
|
|||||||
CONNECTION_LISTENER_CALLBACKS ListenerCallbacks;
|
CONNECTION_LISTENER_CALLBACKS ListenerCallbacks;
|
||||||
DECODER_RENDERER_CALLBACKS VideoCallbacks;
|
DECODER_RENDERER_CALLBACKS VideoCallbacks;
|
||||||
AUDIO_RENDERER_CALLBACKS AudioCallbacks;
|
AUDIO_RENDERER_CALLBACKS AudioCallbacks;
|
||||||
|
int NegotiatedVideoFormat;
|
||||||
|
|
||||||
// Connection stages
|
// Connection stages
|
||||||
static const char* stageNames[STAGE_MAX] = {
|
static const char* stageNames[STAGE_MAX] = {
|
||||||
@@ -177,6 +178,7 @@ int LiStartConnection(const char* host, PSTREAM_CONFIGURATION streamConfig, PCON
|
|||||||
void* renderContext, int drFlags, int _serverMajorVersion) {
|
void* renderContext, int drFlags, int _serverMajorVersion) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
NegotiatedVideoFormat = 0;
|
||||||
ServerMajorVersion = _serverMajorVersion;
|
ServerMajorVersion = _serverMajorVersion;
|
||||||
memcpy(&StreamConfig, streamConfig, sizeof(StreamConfig));
|
memcpy(&StreamConfig, streamConfig, sizeof(StreamConfig));
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "Limelight-internal.h"
|
#include "Limelight-internal.h"
|
||||||
|
|
||||||
static void fakeDrSetup(int width, int height, int redrawRate, void* context, int drFlags) {}
|
static void fakeDrSetup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {}
|
||||||
static void fakeDrCleanup(void) {}
|
static void fakeDrCleanup(void) {}
|
||||||
static int fakeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) { return DR_OK; }
|
static int fakeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) { return DR_OK; }
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ extern STREAM_CONFIGURATION StreamConfig;
|
|||||||
extern CONNECTION_LISTENER_CALLBACKS ListenerCallbacks;
|
extern CONNECTION_LISTENER_CALLBACKS ListenerCallbacks;
|
||||||
extern DECODER_RENDERER_CALLBACKS VideoCallbacks;
|
extern DECODER_RENDERER_CALLBACKS VideoCallbacks;
|
||||||
extern AUDIO_RENDERER_CALLBACKS AudioCallbacks;
|
extern AUDIO_RENDERER_CALLBACKS AudioCallbacks;
|
||||||
|
extern int NegotiatedVideoFormat;
|
||||||
|
|
||||||
int isBeforeSignedInt(int numA, int numB, int ambiguousCase);
|
int isBeforeSignedInt(int numA, int numB, int ambiguousCase);
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ typedef struct _STREAM_CONFIGURATION {
|
|||||||
// See AUDIO_CONFIGURATION_XXX constants below.
|
// See AUDIO_CONFIGURATION_XXX constants below.
|
||||||
int audioConfiguration;
|
int audioConfiguration;
|
||||||
|
|
||||||
|
// Specifies that the client can accept an H.265 video stream
|
||||||
|
// if the server is able to provide one.
|
||||||
|
int supportsHevc;
|
||||||
|
|
||||||
// AES encryption data for the remote input stream. This must be
|
// AES encryption data for the remote input stream. This must be
|
||||||
// the same as what was passed as rikey and rikeyid
|
// the same as what was passed as rikey and rikeyid
|
||||||
// in /launch and /resume requests.
|
// in /launch and /resume requests.
|
||||||
@@ -54,7 +58,7 @@ typedef struct _LENTRY {
|
|||||||
int length;
|
int length;
|
||||||
} LENTRY, *PLENTRY;
|
} LENTRY, *PLENTRY;
|
||||||
|
|
||||||
// A decode unit describes a buffer chain of H264 data from multiple packets
|
// A decode unit describes a buffer chain of video data from multiple packets
|
||||||
typedef struct _DECODE_UNIT {
|
typedef struct _DECODE_UNIT {
|
||||||
// Length of the entire buffer chain in bytes
|
// Length of the entire buffer chain in bytes
|
||||||
int fullLength;
|
int fullLength;
|
||||||
@@ -69,6 +73,14 @@ typedef struct _DECODE_UNIT {
|
|||||||
// Specifies that the audio stream should be in 5.1 surround sound if the PC is able
|
// Specifies that the audio stream should be in 5.1 surround sound if the PC is able
|
||||||
#define AUDIO_CONFIGURATION_51_SURROUND 1
|
#define AUDIO_CONFIGURATION_51_SURROUND 1
|
||||||
|
|
||||||
|
// Passed to DecoderRendererSetup to indicate that the following video stream will be
|
||||||
|
// in H.264 format
|
||||||
|
#define VIDEO_FORMAT_H264 1
|
||||||
|
|
||||||
|
// Passed to DecoderRendererSetup to indicate that the following video stream will be
|
||||||
|
// in H.265 format
|
||||||
|
#define VIDEO_FORMAT_H265 2
|
||||||
|
|
||||||
// If set in the renderer capabilities field, this flag will cause audio/video data to
|
// If set in the renderer capabilities field, this flag will cause audio/video data to
|
||||||
// be submitted directly from the receive thread. This should only be specified if the
|
// be submitted directly from the receive thread. This should only be specified if the
|
||||||
// renderer is non-blocking. This flag is valid on both audio and video renderers.
|
// renderer is non-blocking. This flag is valid on both audio and video renderers.
|
||||||
@@ -80,17 +92,17 @@ typedef struct _DECODE_UNIT {
|
|||||||
#define CAPABILITY_REFERENCE_FRAME_INVALIDATION 0x2
|
#define CAPABILITY_REFERENCE_FRAME_INVALIDATION 0x2
|
||||||
|
|
||||||
// If set in the video renderer capabilities field, this macro specifies that the renderer
|
// If set in the video renderer capabilities field, this macro specifies that the renderer
|
||||||
// supports H264 slicing to increase decoding performance. The parameter specifies the desired
|
// supports slicing to increase decoding performance. The parameter specifies the desired
|
||||||
// number of slices per frame. This capability is only valid on video renderers.
|
// number of slices per frame. This capability is only valid on video renderers.
|
||||||
#define CAPABILITY_SLICES_PER_FRAME(x) (((unsigned char)(x)) << 24)
|
#define CAPABILITY_SLICES_PER_FRAME(x) (((unsigned char)(x)) << 24)
|
||||||
|
|
||||||
// This callback is invoked to provide details about the video stream and allow configuration of the decoder
|
// This callback is invoked to provide details about the video stream and allow configuration of the decoder
|
||||||
typedef void(*DecoderRendererSetup)(int width, int height, int redrawRate, void* context, int drFlags);
|
typedef void(*DecoderRendererSetup)(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags);
|
||||||
|
|
||||||
// This callback performs the teardown of the video decoder
|
// This callback performs the teardown of the video decoder
|
||||||
typedef void(*DecoderRendererCleanup)(void);
|
typedef void(*DecoderRendererCleanup)(void);
|
||||||
|
|
||||||
// This callback provides Annex B formatted H264 elementary stream data to the
|
// This callback provides Annex B formatted elementary stream data to the
|
||||||
// decoder. If the decoder is unable to process the submitted data for some reason,
|
// decoder. If the decoder is unable to process the submitted data for some reason,
|
||||||
// it must return DR_NEED_IDR to generate a keyframe.
|
// it must return DR_NEED_IDR to generate a keyframe.
|
||||||
#define DR_OK 0
|
#define DR_OK 0
|
||||||
|
|||||||
@@ -339,6 +339,19 @@ int performRtspHandshake(void) {
|
|||||||
return response.message.response.statusCode;
|
return response.message.response.statusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The RTSP DESCRIBE reply will contain a collection of SDP media attributes that
|
||||||
|
// describe the various supported video stream formats and include the SPS, PPS,
|
||||||
|
// and VPS (if applicable). We will use this information to determine whether the
|
||||||
|
// server can support HEVC. For some reason, they still set the MIME type of the HEVC
|
||||||
|
// format to H264, so we can't just look for the HEVC MIME type. What we'll do instead is
|
||||||
|
// look for the base 64 encoded VPS NALU prefix that is unique to the HEVC bitstream.
|
||||||
|
if (StreamConfig.supportsHevc && strstr(response.payload, "sprop-parameter-sets=AAAAAU")) {
|
||||||
|
NegotiatedVideoFormat = VIDEO_FORMAT_H265;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NegotiatedVideoFormat = VIDEO_FORMAT_H264;
|
||||||
|
}
|
||||||
|
|
||||||
freeMessage(&response);
|
freeMessage(&response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,22 +138,10 @@ static int addGen3Options(PSDP_OPTION* head, char* addrStr) {
|
|||||||
static int addGen4Options(PSDP_OPTION* head, char* addrStr) {
|
static int addGen4Options(PSDP_OPTION* head, char* addrStr) {
|
||||||
char payloadStr[92];
|
char payloadStr[92];
|
||||||
int err = 0;
|
int err = 0;
|
||||||
unsigned char slicesPerFrame;
|
|
||||||
|
|
||||||
sprintf(payloadStr, "rtsp://%s:48010", addrStr);
|
sprintf(payloadStr, "rtsp://%s:48010", addrStr);
|
||||||
err |= addAttributeString(head, "x-nv-general.serverAddress", payloadStr);
|
err |= addAttributeString(head, "x-nv-general.serverAddress", payloadStr);
|
||||||
|
|
||||||
err |= addAttributeString(head, "x-nv-video[0].rateControlMode", "4");
|
|
||||||
|
|
||||||
// Use slicing for increased performance on some decoders
|
|
||||||
slicesPerFrame = (unsigned char)(VideoCallbacks.capabilities >> 24);
|
|
||||||
if (slicesPerFrame == 0) {
|
|
||||||
// If not using slicing, we request 1 slice per frame
|
|
||||||
slicesPerFrame = 1;
|
|
||||||
}
|
|
||||||
sprintf(payloadStr, "%d", slicesPerFrame);
|
|
||||||
err |= addAttributeString(head, "x-nv-video[0].videoEncoderSlicesPerFrame", payloadStr);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,6 +230,29 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ServerMajorVersion >= 4) {
|
if (ServerMajorVersion >= 4) {
|
||||||
|
if (NegotiatedVideoFormat == VIDEO_FORMAT_H265) {
|
||||||
|
err |= addAttributeString(&optionHead, "x-nv-clientSupportHevc", "1");
|
||||||
|
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bitStreamFormat", "1");
|
||||||
|
|
||||||
|
// Disable slicing on HEVC
|
||||||
|
err |= addAttributeString(&optionHead, "x-nv-video[0].videoEncoderSlicesPerFrame", "1");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unsigned char slicesPerFrame;
|
||||||
|
|
||||||
|
err |= addAttributeString(&optionHead, "x-nv-clientSupportHevc", "0");
|
||||||
|
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bitStreamFormat", "0");
|
||||||
|
|
||||||
|
// Use slicing for increased performance on some decoders
|
||||||
|
slicesPerFrame = (unsigned char)(VideoCallbacks.capabilities >> 24);
|
||||||
|
if (slicesPerFrame == 0) {
|
||||||
|
// If not using slicing, we request 1 slice per frame
|
||||||
|
slicesPerFrame = 1;
|
||||||
|
}
|
||||||
|
sprintf(payloadStr, "%d", slicesPerFrame);
|
||||||
|
err |= addAttributeString(&optionHead, "x-nv-video[0].videoEncoderSlicesPerFrame", payloadStr);
|
||||||
|
}
|
||||||
|
|
||||||
if (StreamConfig.audioConfiguration == AUDIO_CONFIGURATION_51_SURROUND) {
|
if (StreamConfig.audioConfiguration == AUDIO_CONFIGURATION_51_SURROUND) {
|
||||||
audioChannelCount = CHANNEL_COUNT_51_SURROUND;
|
audioChannelCount = CHANNEL_COUNT_51_SURROUND;
|
||||||
audioChannelMask = CHANNEL_MASK_51_SURROUND;
|
audioChannelMask = CHANNEL_MASK_51_SURROUND;
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ void initializeVideoDepacketizer(int pktSize) {
|
|||||||
strictIdrFrameWait = !(VideoCallbacks.capabilities & CAPABILITY_REFERENCE_FRAME_INVALIDATION);
|
strictIdrFrameWait = !(VideoCallbacks.capabilities & CAPABILITY_REFERENCE_FRAME_INVALIDATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free malloced memory in AvcFrameState*/
|
// Free the NAL chain
|
||||||
static void cleanupAvcFrameState(void) {
|
static void cleanupFrameState(void) {
|
||||||
PLENTRY lastEntry;
|
PLENTRY lastEntry;
|
||||||
|
|
||||||
while (nalChainHead != NULL) {
|
while (nalChainHead != NULL) {
|
||||||
@@ -59,8 +59,8 @@ static void cleanupAvcFrameState(void) {
|
|||||||
nalChainDataLength = 0;
|
nalChainDataLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup AVC frame state and set that we're waiting for an IDR Frame*/
|
// Cleanup frame state and set that we're waiting for an IDR Frame
|
||||||
static void dropAvcFrameState(void) {
|
static void dropFrameState(void) {
|
||||||
// We'll need an IDR frame now if we're in strict mode
|
// We'll need an IDR frame now if we're in strict mode
|
||||||
if (strictIdrFrameWait) {
|
if (strictIdrFrameWait) {
|
||||||
waitingForIdrFrame = 1;
|
waitingForIdrFrame = 1;
|
||||||
@@ -81,7 +81,7 @@ static void dropAvcFrameState(void) {
|
|||||||
requestIdrOnDemand();
|
requestIdrOnDemand();
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanupAvcFrameState();
|
cleanupFrameState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup the list of decode units
|
// Cleanup the list of decode units
|
||||||
@@ -109,7 +109,7 @@ void destroyVideoDepacketizer(void) {
|
|||||||
freeDecodeUnitList(LbqDestroyLinkedBlockingQueue(&decodeUnitQueue));
|
freeDecodeUnitList(LbqDestroyLinkedBlockingQueue(&decodeUnitQueue));
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanupAvcFrameState();
|
cleanupFrameState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns 1 if candidate is a frame start and 0 otherwise
|
// Returns 1 if candidate is a frame start and 0 otherwise
|
||||||
@@ -117,8 +117,8 @@ static int isSeqFrameStart(PBUFFER_DESC candidate) {
|
|||||||
return (candidate->length == 4 && candidate->data[candidate->offset + candidate->length - 1] == 1);
|
return (candidate->length == 4 && candidate->data[candidate->offset + candidate->length - 1] == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns 1 if candidate an AVC start and 0 otherwise
|
// Returns 1 if candidate is an Annex B start and 0 otherwise
|
||||||
static int isSeqAvcStart(PBUFFER_DESC candidate) {
|
static int isSeqAnnexBStart(PBUFFER_DESC candidate) {
|
||||||
return (candidate->data[candidate->offset + candidate->length - 1] == 1);
|
return (candidate->data[candidate->offset + candidate->length - 1] == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,8 +188,39 @@ void freeQueuedDecodeUnit(PQUEUED_DECODE_UNIT qdu) {
|
|||||||
free(qdu);
|
free(qdu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns 1 if the special sequence describes an I-frame
|
||||||
|
static int isSeqReferenceFrameStart(PBUFFER_DESC specialSeq) {
|
||||||
|
switch (specialSeq->data[specialSeq->offset + specialSeq->length]) {
|
||||||
|
case 0x20:
|
||||||
|
case 0x22:
|
||||||
|
case 0x24:
|
||||||
|
case 0x26:
|
||||||
|
case 0x28:
|
||||||
|
case 0x2A:
|
||||||
|
// H265
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case 0x65:
|
||||||
|
// H264
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 1 if this buffer describes an IDR frame
|
||||||
|
static int isIdrFrameStart(PBUFFER_DESC buffer) {
|
||||||
|
BUFFER_DESC specialSeq;
|
||||||
|
return getSpecialSeq(buffer, &specialSeq) &&
|
||||||
|
isSeqFrameStart(&specialSeq) &&
|
||||||
|
(specialSeq.data[specialSeq.offset + specialSeq.length] == 0x67 || // H264 SPS
|
||||||
|
specialSeq.data[specialSeq.offset + specialSeq.length] == 0x40); // H265 VPS
|
||||||
|
}
|
||||||
|
|
||||||
// Reassemble the frame with the given frame number
|
// Reassemble the frame with the given frame number
|
||||||
static void reassembleAvcFrame(int frameNumber) {
|
static void reassembleFrame(int frameNumber) {
|
||||||
if (nalChainHead != NULL) {
|
if (nalChainHead != NULL) {
|
||||||
PQUEUED_DECODE_UNIT qdu = (PQUEUED_DECODE_UNIT)malloc(sizeof(*qdu));
|
PQUEUED_DECODE_UNIT qdu = (PQUEUED_DECODE_UNIT)malloc(sizeof(*qdu));
|
||||||
if (qdu != NULL) {
|
if (qdu != NULL) {
|
||||||
@@ -206,7 +237,7 @@ static void reassembleAvcFrame(int frameNumber) {
|
|||||||
// Clear frame state and wait for an IDR
|
// Clear frame state and wait for an IDR
|
||||||
nalChainHead = qdu->decodeUnit.bufferList;
|
nalChainHead = qdu->decodeUnit.bufferList;
|
||||||
nalChainDataLength = qdu->decodeUnit.fullLength;
|
nalChainDataLength = qdu->decodeUnit.fullLength;
|
||||||
dropAvcFrameState();
|
dropFrameState();
|
||||||
|
|
||||||
// Free the DU
|
// Free the DU
|
||||||
free(qdu);
|
free(qdu);
|
||||||
@@ -268,25 +299,25 @@ static void queueFragment(char*data, int offset, int length) {
|
|||||||
// Process an RTP Payload
|
// Process an RTP Payload
|
||||||
static void processRtpPayloadSlow(PNV_VIDEO_PACKET videoPacket, PBUFFER_DESC currentPos) {
|
static void processRtpPayloadSlow(PNV_VIDEO_PACKET videoPacket, PBUFFER_DESC currentPos) {
|
||||||
BUFFER_DESC specialSeq;
|
BUFFER_DESC specialSeq;
|
||||||
int decodingAvc = 0;
|
int decodingVideo = 0;
|
||||||
|
|
||||||
while (currentPos->length != 0) {
|
while (currentPos->length != 0) {
|
||||||
int start = currentPos->offset;
|
int start = currentPos->offset;
|
||||||
|
|
||||||
if (getSpecialSeq(currentPos, &specialSeq)) {
|
if (getSpecialSeq(currentPos, &specialSeq)) {
|
||||||
if (isSeqAvcStart(&specialSeq)) {
|
if (isSeqAnnexBStart(&specialSeq)) {
|
||||||
// Now we're decoding AVC
|
// Now we're decoding video
|
||||||
decodingAvc = 1;
|
decodingVideo = 1;
|
||||||
|
|
||||||
if (isSeqFrameStart(&specialSeq)) {
|
if (isSeqFrameStart(&specialSeq)) {
|
||||||
// Now we're working on a frame
|
// Now we're working on a frame
|
||||||
decodingFrame = 1;
|
decodingFrame = 1;
|
||||||
|
|
||||||
// Reassemble any pending frame
|
// Reassemble any pending frame
|
||||||
reassembleAvcFrame(videoPacket->frameIndex);
|
reassembleFrame(videoPacket->frameIndex);
|
||||||
|
|
||||||
if (specialSeq.data[specialSeq.offset + specialSeq.length] == 0x65) {
|
if (isSeqReferenceFrameStart(&specialSeq)) {
|
||||||
// This is the NALU code for I-frame data
|
// No longer waiting for an IDR frame
|
||||||
waitingForIdrFrame = 0;
|
waitingForIdrFrame = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,13 +327,13 @@ static void processRtpPayloadSlow(PNV_VIDEO_PACKET videoPacket, PBUFFER_DESC cur
|
|||||||
currentPos->offset += specialSeq.length;
|
currentPos->offset += specialSeq.length;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Check if this is padding after a full AVC frame
|
// Check if this is padding after a full frame
|
||||||
if (decodingAvc && isSeqPadding(currentPos)) {
|
if (decodingVideo && isSeqPadding(currentPos)) {
|
||||||
reassembleAvcFrame(videoPacket->frameIndex);
|
reassembleFrame(videoPacket->frameIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not decoding AVC
|
// Not decoding video
|
||||||
decodingAvc = 0;
|
decodingVideo = 0;
|
||||||
|
|
||||||
// Just skip this byte
|
// Just skip this byte
|
||||||
currentPos->length--;
|
currentPos->length--;
|
||||||
@@ -314,7 +345,7 @@ static void processRtpPayloadSlow(PNV_VIDEO_PACKET videoPacket, PBUFFER_DESC cur
|
|||||||
while (currentPos->length != 0) {
|
while (currentPos->length != 0) {
|
||||||
// Check if this should end the current NAL
|
// Check if this should end the current NAL
|
||||||
if (getSpecialSeq(currentPos, &specialSeq)) {
|
if (getSpecialSeq(currentPos, &specialSeq)) {
|
||||||
if (decodingAvc || !isSeqPadding(&specialSeq)) {
|
if (decodingVideo || !isSeqPadding(&specialSeq)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -324,7 +355,7 @@ static void processRtpPayloadSlow(PNV_VIDEO_PACKET videoPacket, PBUFFER_DESC cur
|
|||||||
currentPos->length--;
|
currentPos->length--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decodingAvc) {
|
if (decodingVideo) {
|
||||||
queueFragment(currentPos->data, start, currentPos->offset - start);
|
queueFragment(currentPos->data, start, currentPos->offset - start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,7 +368,7 @@ void requestDecoderRefresh(void) {
|
|||||||
waitingForIdrFrame = 1;
|
waitingForIdrFrame = 1;
|
||||||
|
|
||||||
// Flush the decode unit queue and pending state
|
// Flush the decode unit queue and pending state
|
||||||
dropAvcFrameState();
|
dropFrameState();
|
||||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||||
freeDecodeUnitList(LbqFlushQueueItems(&decodeUnitQueue));
|
freeDecodeUnitList(LbqFlushQueueItems(&decodeUnitQueue));
|
||||||
}
|
}
|
||||||
@@ -363,7 +394,7 @@ static void processRtpPayloadFast(BUFFER_DESC location) {
|
|||||||
|
|
||||||
// Process an RTP Payload
|
// Process an RTP Payload
|
||||||
void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
|
void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
|
||||||
BUFFER_DESC currentPos, specialSeq;
|
BUFFER_DESC currentPos;
|
||||||
int frameIndex;
|
int frameIndex;
|
||||||
char flags;
|
char flags;
|
||||||
int firstPacket;
|
int firstPacket;
|
||||||
@@ -403,7 +434,7 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
|
|||||||
|
|
||||||
// Unexpected start of next frame before terminating the last
|
// Unexpected start of next frame before terminating the last
|
||||||
waitingForNextSuccessfulFrame = 1;
|
waitingForNextSuccessfulFrame = 1;
|
||||||
dropAvcFrameState();
|
dropFrameState();
|
||||||
}
|
}
|
||||||
// Look for a non-frame start before a frame start
|
// Look for a non-frame start before a frame start
|
||||||
else if (!firstPacket && !decodingFrame) {
|
else if (!firstPacket && !decodingFrame) {
|
||||||
@@ -417,7 +448,7 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
|
|||||||
|
|
||||||
waitingForNextSuccessfulFrame = 1;
|
waitingForNextSuccessfulFrame = 1;
|
||||||
|
|
||||||
dropAvcFrameState();
|
dropFrameState();
|
||||||
decodingFrame = 0;
|
decodingFrame = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -436,7 +467,7 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
|
|||||||
|
|
||||||
// Wait until next complete frame
|
// Wait until next complete frame
|
||||||
waitingForNextSuccessfulFrame = 1;
|
waitingForNextSuccessfulFrame = 1;
|
||||||
dropAvcFrameState();
|
dropFrameState();
|
||||||
}
|
}
|
||||||
else if (nextFrameNumber != frameIndex) {
|
else if (nextFrameNumber != frameIndex) {
|
||||||
// Duplicate packet or FEC dup
|
// Duplicate packet or FEC dup
|
||||||
@@ -458,7 +489,7 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
|
|||||||
|
|
||||||
waitingForNextSuccessfulFrame = 1;
|
waitingForNextSuccessfulFrame = 1;
|
||||||
|
|
||||||
dropAvcFrameState();
|
dropFrameState();
|
||||||
decodingFrame = 0;
|
decodingFrame = 0;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -478,10 +509,7 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
|
|||||||
currentPos.length -= 8;
|
currentPos.length -= 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstPacket &&
|
if (firstPacket && isIdrFrameStart(¤tPos))
|
||||||
getSpecialSeq(¤tPos, &specialSeq) &&
|
|
||||||
isSeqFrameStart(&specialSeq) &&
|
|
||||||
specialSeq.data[specialSeq.offset + specialSeq.length] == 0x67)
|
|
||||||
{
|
{
|
||||||
// SPS and PPS prefix is padded between NALs, so we must decode it with the slow path
|
// SPS and PPS prefix is padded between NALs, so we must decode it with the slow path
|
||||||
processRtpPayloadSlow(videoPacket, ¤tPos);
|
processRtpPayloadSlow(videoPacket, ¤tPos);
|
||||||
@@ -508,11 +536,11 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
|
|||||||
if (waitingForIdrFrame) {
|
if (waitingForIdrFrame) {
|
||||||
Limelog("Waiting for IDR frame\n");
|
Limelog("Waiting for IDR frame\n");
|
||||||
|
|
||||||
dropAvcFrameState();
|
dropFrameState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
reassembleAvcFrame(frameIndex);
|
reassembleFrame(frameIndex);
|
||||||
|
|
||||||
startFrameNumber = nextFrameNumber;
|
startFrameNumber = nextFrameNumber;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,7 +197,8 @@ int startVideoStream(void* rendererContext, int drFlags) {
|
|||||||
|
|
||||||
// This must be called before the decoder thread starts submitting
|
// This must be called before the decoder thread starts submitting
|
||||||
// decode units
|
// decode units
|
||||||
VideoCallbacks.setup(StreamConfig.width,
|
LC_ASSERT(NegotiatedVideoFormat != 0);
|
||||||
|
VideoCallbacks.setup(NegotiatedVideoFormat, StreamConfig.width,
|
||||||
StreamConfig.height, StreamConfig.fps, rendererContext, drFlags);
|
StreamConfig.height, StreamConfig.fps, rendererContext, drFlags);
|
||||||
|
|
||||||
rtpSocket = bindUdpSocket(RemoteAddr.ss_family, RTP_RECV_BUFFER);
|
rtpSocket = bindUdpSocket(RemoteAddr.ss_family, RTP_RECV_BUFFER);
|
||||||
|
|||||||
Reference in New Issue
Block a user