mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-27 04:35:34 +08:00
Support ffmpeg soft decode for Winodws
This commit is contained in:
@@ -4,6 +4,13 @@
|
|||||||
|
|
||||||
VideoFrame::VideoFrame() {}
|
VideoFrame::VideoFrame() {}
|
||||||
|
|
||||||
|
VideoFrame::VideoFrame(size_t size) {
|
||||||
|
buffer_ = new uint8_t[size];
|
||||||
|
size_ = size;
|
||||||
|
width_ = 0;
|
||||||
|
height_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
VideoFrame::VideoFrame(const uint8_t *buffer, size_t size) {
|
VideoFrame::VideoFrame(const uint8_t *buffer, size_t size) {
|
||||||
buffer_ = new uint8_t[size];
|
buffer_ = new uint8_t[size];
|
||||||
memcpy(buffer_, buffer, size);
|
memcpy(buffer_, buffer, size);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
class VideoFrame {
|
class VideoFrame {
|
||||||
public:
|
public:
|
||||||
VideoFrame();
|
VideoFrame();
|
||||||
|
VideoFrame(size_t size);
|
||||||
VideoFrame(const uint8_t *buffer, size_t size);
|
VideoFrame(const uint8_t *buffer, size_t size);
|
||||||
VideoFrame(const uint8_t *buffer, size_t size, size_t width, size_t height);
|
VideoFrame(const uint8_t *buffer, size_t size, size_t width, size_t height);
|
||||||
VideoFrame(const VideoFrame &video_frame);
|
VideoFrame(const VideoFrame &video_frame);
|
||||||
@@ -19,6 +20,8 @@ class VideoFrame {
|
|||||||
const uint8_t *Buffer() { return buffer_; }
|
const uint8_t *Buffer() { return buffer_; }
|
||||||
const size_t Size() { return size_; }
|
const size_t Size() { return size_; }
|
||||||
|
|
||||||
|
uint8_t *GetBuffer() { return buffer_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t width_ = 0;
|
size_t width_ = 0;
|
||||||
size_t height_ = 0;
|
size_t height_ = 0;
|
||||||
|
|||||||
@@ -2,357 +2,124 @@
|
|||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
const char H264_NAL_START[] = {0x00, 0x00, 0x00, 0x01};
|
#define SAVE_ENCODER_STREAM 1
|
||||||
|
|
||||||
VideoDecoder::VideoDecoder(PacketQueue *packetQueue) {
|
extern "C" {
|
||||||
pPacketQueue = packetQueue;
|
#include <libavformat/avformat.h>
|
||||||
pFrameDataCallbackMutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
|
#include <libavutil/imgutils.h>
|
||||||
|
#include <libswscale/swscale.h>
|
||||||
|
};
|
||||||
|
|
||||||
int ret = pthread_mutex_init(pFrameDataCallbackMutex, nullptr);
|
VideoDecoder::VideoDecoder() {
|
||||||
if (ret != 0) {
|
if (SAVE_ENCODER_STREAM) {
|
||||||
LOG_ERROR("video FrameDataCallbackMutex init failed.\n");
|
file_ = fopen("decode_stream.yuv", "w+b");
|
||||||
|
if (!file_) {
|
||||||
|
LOG_WARN("Fail to open stream.yuv");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gSPSLen = 0;
|
|
||||||
pSPS = nullptr;
|
|
||||||
|
|
||||||
gPPSLen = 0;
|
|
||||||
pPPS = nullptr;
|
|
||||||
|
|
||||||
isFirstIDR = false;
|
|
||||||
|
|
||||||
gFrameRate = 25;
|
|
||||||
|
|
||||||
pFrameDataCallback = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoDecoder::~VideoDecoder() {
|
VideoDecoder::~VideoDecoder() {
|
||||||
pthread_mutex_destroy(pFrameDataCallbackMutex);
|
if (SAVE_ENCODER_STREAM && file_) {
|
||||||
|
fflush(file_);
|
||||||
if (nullptr != pFrameDataCallbackMutex) {
|
fclose(file_);
|
||||||
free(pFrameDataCallbackMutex);
|
file_ = nullptr;
|
||||||
pFrameDataCallbackMutex = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (decoded_frame_) {
|
||||||
|
delete decoded_frame_;
|
||||||
|
decoded_frame_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
av_frame_free(&frame_);
|
||||||
|
av_frame_free(&frame_nv12_);
|
||||||
|
sws_freeContext(img_convert_ctx);
|
||||||
|
avcodec_close(codec_ctx_);
|
||||||
|
av_free(codec_ctx_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoDecoder::setFrameDataCallback(FrameDataCallback *frameDataCallback) {
|
int VideoDecoder::Init() {
|
||||||
pthread_mutex_lock(pFrameDataCallbackMutex);
|
// av_log_set_level(AV_LOG_DEBUG);
|
||||||
pFrameDataCallback = frameDataCallback;
|
|
||||||
pthread_mutex_unlock(pFrameDataCallbackMutex);
|
codec_id_ = AV_CODEC_ID_H264;
|
||||||
|
codec_ = avcodec_find_decoder(codec_id_);
|
||||||
|
if (!codec_) {
|
||||||
|
printf("Codec not found\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
codec_ctx_ = avcodec_alloc_context3(codec_);
|
||||||
|
if (!codec_ctx_) {
|
||||||
|
printf("Could not allocate video codec context\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
codec_ctx_->time_base.num = 1;
|
||||||
|
codec_ctx_->frame_number = 1;
|
||||||
|
codec_ctx_->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||||
|
codec_ctx_->bit_rate = 0;
|
||||||
|
codec_ctx_->time_base.den = 29;
|
||||||
|
codec_ctx_->width = 1280;
|
||||||
|
codec_ctx_->height = 720;
|
||||||
|
codec_ctx_->pix_fmt = AV_PIX_FMT_NV12;
|
||||||
|
codec_ctx_->color_range = AVCOL_RANGE_MPEG;
|
||||||
|
|
||||||
|
if (avcodec_open2(codec_ctx_, codec_, NULL) < 0) {
|
||||||
|
printf("Could not open codec\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_ = av_frame_alloc();
|
||||||
|
frame_nv12_ = av_frame_alloc();
|
||||||
|
|
||||||
|
img_convert_ctx =
|
||||||
|
sws_getContext(1280, 720, AV_PIX_FMT_YUV420P, 1280, 720, AV_PIX_FMT_NV12,
|
||||||
|
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
decoded_frame_ = new VideoFrame(1280 * 720 * 3 / 2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoDecoder::close() {
|
int VideoDecoder::Decode(
|
||||||
isDecoding = false;
|
const uint8_t *data, int size,
|
||||||
pthread_join(decodeThread, nullptr);
|
std::function<void(VideoFrame)> on_receive_decoded_frame) {
|
||||||
|
if (!first_) {
|
||||||
if (pSPS != nullptr) {
|
if ((*(data + 4) & 0x1f) != 0x07) {
|
||||||
free(pSPS);
|
return -1;
|
||||||
pSPS = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pPPS != nullptr) {
|
|
||||||
free(pPPS);
|
|
||||||
pPPS = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pFrame != nullptr) {
|
|
||||||
av_frame_free(&pFrame);
|
|
||||||
LOG_INFO("%s video Frame free", __FUNCTION__);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pVideoAVCodecCtx != nullptr) {
|
|
||||||
avcodec_free_context(&pVideoAVCodecCtx);
|
|
||||||
LOG_INFO("%s video avcodec_free_context", __FUNCTION__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VideoDecoder::open(unsigned int frameRate, unsigned int profile,
|
|
||||||
unsigned int level, char *sps, unsigned int spsLen,
|
|
||||||
char *pps, unsigned int ppsLen) {
|
|
||||||
gSPSLen = 0;
|
|
||||||
pSPS = nullptr;
|
|
||||||
|
|
||||||
gPPSLen = 0;
|
|
||||||
pPPS = nullptr;
|
|
||||||
|
|
||||||
LOG_INFO("%s spsLen=%d ppsLen=%d", __FUNCTION__, spsLen, ppsLen);
|
|
||||||
|
|
||||||
if (spsLen > 0) {
|
|
||||||
pSPS = (char *)malloc(spsLen);
|
|
||||||
if (nullptr == pSPS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(pSPS, sps, spsLen);
|
|
||||||
gSPSLen = spsLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ppsLen > 0) {
|
|
||||||
pPPS = (char *)malloc(ppsLen);
|
|
||||||
if (nullptr == pPPS) {
|
|
||||||
free(pSPS);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(pPPS, pps, ppsLen);
|
|
||||||
gPPSLen = ppsLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
isFirstIDR = false;
|
|
||||||
|
|
||||||
if (frameRate > 0) {
|
|
||||||
gFrameRate = frameRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret;
|
|
||||||
AVCodec *dec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
|
||||||
LOG_INFO("%s video decoder name: %s", __FUNCTION__, dec->name);
|
|
||||||
pVideoAVCodecCtx = avcodec_alloc_context3(dec);
|
|
||||||
|
|
||||||
if (pVideoAVCodecCtx == nullptr) {
|
|
||||||
LOG_ERROR("%s VideoAVCodecCtx alloc failed", __FUNCTION__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVCodecParameters *par = avcodec_parameters_alloc();
|
|
||||||
if (par == nullptr) {
|
|
||||||
LOG_ERROR("%s video AVCodecParameters alloc failed", __FUNCTION__);
|
|
||||||
free(pSPS);
|
|
||||||
free(pPPS);
|
|
||||||
avcodec_free_context(&pVideoAVCodecCtx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
par->codec_type = AVMEDIA_TYPE_VIDEO;
|
|
||||||
par->codec_id = AV_CODEC_ID_H264;
|
|
||||||
par->format = AV_PIX_FMT_YUV420P; // AV_PIX_FMT_NV12
|
|
||||||
par->color_range = AVCOL_RANGE_JPEG;
|
|
||||||
|
|
||||||
if (profile != 0) {
|
|
||||||
par->profile = (int)profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level != 0) {
|
|
||||||
par->level = (int)level;
|
|
||||||
}
|
|
||||||
|
|
||||||
avcodec_parameters_to_context(pVideoAVCodecCtx, par);
|
|
||||||
avcodec_parameters_free(&par);
|
|
||||||
|
|
||||||
LOG_INFO("%s profile=%d level=%d", __FUNCTION__, profile, level);
|
|
||||||
ret = avcodec_open2(pVideoAVCodecCtx, dec, nullptr);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERROR("%s Can not open video encoder", __FUNCTION__);
|
|
||||||
free(pSPS);
|
|
||||||
free(pPPS);
|
|
||||||
avcodec_free_context(&pVideoAVCodecCtx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOG_INFO("%s avcodec_open2 video SUCC", __FUNCTION__);
|
|
||||||
pFrame = av_frame_alloc();
|
|
||||||
if (pFrame == nullptr) {
|
|
||||||
LOG_ERROR("%s video av_frame_alloc failed", __FUNCTION__);
|
|
||||||
free(pSPS);
|
|
||||||
free(pPPS);
|
|
||||||
avcodec_free_context(&pVideoAVCodecCtx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
isDecoding = true;
|
|
||||||
ret = pthread_create(&decodeThread, nullptr, &VideoDecoder::_decode,
|
|
||||||
(void *)this);
|
|
||||||
if (ret != 0) {
|
|
||||||
LOG_ERROR("video decode-thread create failed.\n");
|
|
||||||
isDecoding = false;
|
|
||||||
free(pSPS);
|
|
||||||
free(pPPS);
|
|
||||||
avcodec_free_context(&pVideoAVCodecCtx);
|
|
||||||
av_frame_free(&pFrame);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoDecoder::decode() {
|
|
||||||
int ret;
|
|
||||||
unsigned sleepDelta = 1000000 / gFrameRate / 4; // 一帧视频的 1/4
|
|
||||||
int NAL_START_LEN = 4;
|
|
||||||
|
|
||||||
while (isDecoding) {
|
|
||||||
AVPacket *pkt = av_packet_alloc();
|
|
||||||
|
|
||||||
if (pkt == nullptr) {
|
|
||||||
usleep(sleepDelta);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pPacketQueue == nullptr) {
|
|
||||||
av_packet_free(&pkt);
|
|
||||||
usleep(sleepDelta);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
PACKET_STRUCT *packetStruct;
|
|
||||||
bool isDone = pPacketQueue->Take(packetStruct);
|
|
||||||
if (isDone && packetStruct != nullptr && packetStruct->data != nullptr &&
|
|
||||||
packetStruct->data_size > 0) {
|
|
||||||
// 0x67:sps
|
|
||||||
if (packetStruct->data[0] == 0x67) {
|
|
||||||
if (gSPSLen <= 0) {
|
|
||||||
gSPSLen = packetStruct->data_size;
|
|
||||||
pSPS = (char *)malloc(gSPSLen);
|
|
||||||
if (nullptr == pSPS) {
|
|
||||||
av_packet_free(&pkt);
|
|
||||||
free(packetStruct->data);
|
|
||||||
free(packetStruct);
|
|
||||||
|
|
||||||
usleep(sleepDelta);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
memcpy(pSPS, packetStruct->data, gSPSLen);
|
|
||||||
LOG_INFO("%s get sps spsLen=%d", __FUNCTION__, gSPSLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
av_packet_free(&pkt);
|
|
||||||
free(packetStruct->data);
|
|
||||||
free(packetStruct);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 0x68:pps
|
|
||||||
if (packetStruct->data[0] == 0x68) {
|
|
||||||
if (gPPSLen <= 0) {
|
|
||||||
gPPSLen = packetStruct->data_size;
|
|
||||||
pPPS = (char *)malloc(gPPSLen);
|
|
||||||
if (nullptr == pPPS) {
|
|
||||||
av_packet_free(&pkt);
|
|
||||||
free(packetStruct->data);
|
|
||||||
free(packetStruct);
|
|
||||||
|
|
||||||
usleep(sleepDelta);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
memcpy(pPPS, packetStruct->data, gPPSLen);
|
|
||||||
LOG_INFO("%s get pps ppsLen=%d", __FUNCTION__, gPPSLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
av_packet_free(&pkt);
|
|
||||||
free(packetStruct->data);
|
|
||||||
free(packetStruct);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isFirstIDR) {
|
|
||||||
// 0x65:IDR
|
|
||||||
if (packetStruct->data[0] == 0x65) {
|
|
||||||
isFirstIDR = true;
|
|
||||||
LOG_INFO("%s get first idr.", __FUNCTION__);
|
|
||||||
} else {
|
|
||||||
av_packet_free(&pkt);
|
|
||||||
free(packetStruct->data);
|
|
||||||
free(packetStruct);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packetStruct->data[0] == 0x65 && gSPSLen > 0 && gPPSLen > 0) {
|
|
||||||
ret = av_new_packet(
|
|
||||||
pkt, (int)(NAL_START_LEN + gSPSLen + NAL_START_LEN + gPPSLen +
|
|
||||||
packetStruct->data_size + NAL_START_LEN));
|
|
||||||
} else {
|
|
||||||
ret = av_new_packet(pkt, packetStruct->data_size + NAL_START_LEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
av_packet_free(&pkt);
|
|
||||||
free(packetStruct->data);
|
|
||||||
free(packetStruct);
|
|
||||||
|
|
||||||
usleep(sleepDelta);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
av_packet_free(&pkt);
|
first_ = true;
|
||||||
usleep(sleepDelta);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packet_.data = (uint8_t *)data;
|
||||||
|
packet_.size = size;
|
||||||
|
|
||||||
|
int ret = avcodec_send_packet(codec_ctx_, &packet_);
|
||||||
|
|
||||||
|
while (ret >= 0) {
|
||||||
|
ret = avcodec_receive_frame(codec_ctx_, frame_);
|
||||||
|
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
||||||
|
continue;
|
||||||
|
} else if (ret < 0) {
|
||||||
|
LOG_ERROR("Error receive decoding video frame ret=%d", ret);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packetStruct->data[0] == 0x65 && gSPSLen > 0 && gPPSLen > 0) {
|
if (on_receive_decoded_frame) {
|
||||||
int pos = 0;
|
av_image_fill_arrays(frame_nv12_->data, frame_nv12_->linesize,
|
||||||
// 复制 0x 00 00 00 01
|
decoded_frame_->GetBuffer(), AV_PIX_FMT_NV12,
|
||||||
memcpy(pkt->data + pos, H264_NAL_START, NAL_START_LEN);
|
frame_->width, frame_->height, 1);
|
||||||
pos += NAL_START_LEN;
|
|
||||||
memcpy(pkt->data + pos, pSPS, gSPSLen);
|
|
||||||
pos += (int)gSPSLen;
|
|
||||||
|
|
||||||
memcpy(pkt->data + pos, H264_NAL_START, NAL_START_LEN);
|
sws_scale(img_convert_ctx, frame_->data, frame_->linesize, 0,
|
||||||
pos += NAL_START_LEN;
|
frame_->height, frame_nv12_->data, frame_nv12_->linesize);
|
||||||
memcpy(pkt->data + pos, pPPS, gPPSLen);
|
|
||||||
pos += (int)gPPSLen;
|
|
||||||
|
|
||||||
memcpy(pkt->data + pos, H264_NAL_START, NAL_START_LEN);
|
on_receive_decoded_frame(*decoded_frame_);
|
||||||
pos += NAL_START_LEN;
|
if (SAVE_ENCODER_STREAM) {
|
||||||
memcpy(pkt->data + pos, packetStruct->data, packetStruct->data_size);
|
fwrite((unsigned char *)frame_->data, 1,
|
||||||
} else {
|
frame_->width * frame_->height * 3 / 2, file_);
|
||||||
memcpy(pkt->data, H264_NAL_START, NAL_START_LEN);
|
|
||||||
memcpy(pkt->data + NAL_START_LEN, packetStruct->data,
|
|
||||||
packetStruct->data_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
pkt->pts = packetStruct->timestamp;
|
|
||||||
pkt->dts = packetStruct->timestamp;
|
|
||||||
|
|
||||||
free(packetStruct->data);
|
|
||||||
free(packetStruct);
|
|
||||||
/* send the packet for decoding */
|
|
||||||
ret = avcodec_send_packet(pVideoAVCodecCtx, pkt);
|
|
||||||
// LOGD("%s send the video packet for decoding pkt size=%d", __FUNCTION__,
|
|
||||||
// pkt->size);
|
|
||||||
|
|
||||||
av_packet_unref(pkt);
|
|
||||||
av_packet_free(&pkt);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERROR("%s Error sending the video pkt to the decoder ret=%d",
|
|
||||||
__FUNCTION__, ret);
|
|
||||||
usleep(sleepDelta);
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
// 编码和解码都是一样的,都是send 1次,然后receive多次,
|
|
||||||
// 直到AVERROR(EAGAIN)或者AVERROR_EOF
|
|
||||||
while (ret >= 0) {
|
|
||||||
ret = avcodec_receive_frame(pVideoAVCodecCtx, pFrame);
|
|
||||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
|
||||||
usleep(sleepDelta);
|
|
||||||
continue;
|
|
||||||
} else if (ret < 0) {
|
|
||||||
LOG_ERROR("%s Error receive decoding video frame ret=%d",
|
|
||||||
__FUNCTION__, ret);
|
|
||||||
usleep(sleepDelta);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(pFrameDataCallbackMutex);
|
|
||||||
if (pFrameDataCallback != nullptr) {
|
|
||||||
// 解码固定为 AV_PIX_FMT_YUV420P
|
|
||||||
int planeNum = 3;
|
|
||||||
int yuvLens[planeNum];
|
|
||||||
yuvLens[0] = pFrame->linesize[0] * pFrame->height;
|
|
||||||
yuvLens[1] = pFrame->linesize[1] * pFrame->height / 2;
|
|
||||||
yuvLens[2] = pFrame->linesize[2] * pFrame->height / 2;
|
|
||||||
// LOG_INFO("%s video onDataArrived", __FUNCTION__);
|
|
||||||
pFrameDataCallback->onDataArrived(
|
|
||||||
StreamType::VIDEO, (long long)pFrame->pts, (char **)pFrame->data,
|
|
||||||
yuvLens, planeNum, -1, -1, pFrame->width, pFrame->height);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(pFrameDataCallbackMutex);
|
|
||||||
|
|
||||||
av_frame_unref(pFrame);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1,57 +1,49 @@
|
|||||||
#ifndef _FFMPEG_DECODER_H_
|
#ifndef _FFMPEG_DECODER_H_
|
||||||
#define _FFMPEG_DECODER_H_
|
#define _FFMPEG_DECODER_H_
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Windows
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// 编解码
|
|
||||||
#include "libavcodec/avcodec.h"
|
#include "libavcodec/avcodec.h"
|
||||||
}
|
};
|
||||||
|
#else
|
||||||
|
// Linux...
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#ifdef __cplusplus
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "PacketQueue.h"
|
#include <functional>
|
||||||
#include "cb/FrameDataCallback.h"
|
|
||||||
|
#include "frame.h"
|
||||||
|
|
||||||
class VideoDecoder {
|
class VideoDecoder {
|
||||||
public:
|
public:
|
||||||
VideoDecoder(PacketQueue *packetQueue);
|
VideoDecoder();
|
||||||
~VideoDecoder();
|
~VideoDecoder();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int Init();
|
int Init();
|
||||||
int Decode(const uint8_t *pData, int nSize);
|
int Decode(const uint8_t *data, int size,
|
||||||
int GetFrame(uint8_t *yuv_data, uint32_t &width, uint32_t &height,
|
std::function<void(VideoFrame)> on_receive_decoded_frame);
|
||||||
uint32_t &size);
|
|
||||||
|
|
||||||
bool open(unsigned int frameRate, unsigned int profile, unsigned int level,
|
|
||||||
char *sps, unsigned int spsLen, char *pps, unsigned int ppsLen);
|
|
||||||
|
|
||||||
void close();
|
|
||||||
|
|
||||||
void decode();
|
|
||||||
|
|
||||||
static void *_decode(void *self) {
|
|
||||||
static_cast<VideoDecoder *>(self)->decode();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFrameDataCallback(FrameDataCallback *frameDataCallback);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PacketQueue *pPacketQueue;
|
AVCodecID codec_id_;
|
||||||
AVCodecContext *pVideoAVCodecCtx;
|
const AVCodec *codec_;
|
||||||
AVFrame *pFrame;
|
AVCodecContext *codec_ctx_ = nullptr;
|
||||||
|
AVPacket packet_;
|
||||||
|
AVFrame *frame_ = nullptr;
|
||||||
|
AVFrame *frame_nv12_ = nullptr;
|
||||||
|
struct SwsContext *img_convert_ctx = nullptr;
|
||||||
|
|
||||||
bool volatile isDecoding;
|
VideoFrame *decoded_frame_ = nullptr;
|
||||||
pthread_t decodeThread;
|
|
||||||
pthread_mutex_t *pFrameDataCallbackMutex;
|
|
||||||
FrameDataCallback *pFrameDataCallback;
|
|
||||||
|
|
||||||
char *pSPS;
|
FILE *file_ = nullptr;
|
||||||
unsigned int volatile gSPSLen;
|
bool first_ = false;
|
||||||
char *pPPS;
|
|
||||||
unsigned int volatile gPPSLen;
|
|
||||||
|
|
||||||
bool volatile isFirstIDR;
|
|
||||||
|
|
||||||
unsigned int gFrameRate;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -44,21 +44,21 @@ int VideoDecoder::Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int VideoDecoder::Decode(
|
int VideoDecoder::Decode(
|
||||||
const uint8_t *pData, int nSize,
|
const uint8_t *data, int size,
|
||||||
std::function<void(VideoFrame)> on_receive_decoded_frame) {
|
std::function<void(VideoFrame)> on_receive_decoded_frame) {
|
||||||
if (!decoder) {
|
if (!decoder) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((*(pData + 4) & 0x1f) == 0x07) {
|
if ((*(data + 4) & 0x1f) == 0x07) {
|
||||||
// LOG_WARN("Receive key frame");
|
// LOG_WARN("Receive key frame");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SAVE_ENCODER_STREAM) {
|
if (SAVE_ENCODER_STREAM) {
|
||||||
fwrite((unsigned char *)pData, 1, nSize, file_);
|
fwrite((unsigned char *)data, 1, size, file_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_frame_returned = decoder->Decode(pData, nSize);
|
int num_frame_returned = decoder->Decode(data, size);
|
||||||
|
|
||||||
for (size_t i = 0; i < num_frame_returned; ++i) {
|
for (size_t i = 0; i < num_frame_returned; ++i) {
|
||||||
cudaVideoSurfaceFormat format = decoder->GetOutputFormat();
|
cudaVideoSurfaceFormat format = decoder->GetOutputFormat();
|
||||||
@@ -66,11 +66,10 @@ int VideoDecoder::Decode(
|
|||||||
uint8_t *data = nullptr;
|
uint8_t *data = nullptr;
|
||||||
data = decoder->GetFrame();
|
data = decoder->GetFrame();
|
||||||
if (data) {
|
if (data) {
|
||||||
VideoFrame decoded_frame(
|
|
||||||
data, decoder->GetWidth() * decoder->GetHeight() * 3 / 2,
|
|
||||||
decoder->GetWidth(), decoder->GetHeight());
|
|
||||||
|
|
||||||
if (on_receive_decoded_frame) {
|
if (on_receive_decoded_frame) {
|
||||||
|
VideoFrame decoded_frame(
|
||||||
|
data, decoder->GetWidth() * decoder->GetHeight() * 3 / 2,
|
||||||
|
decoder->GetWidth(), decoder->GetHeight());
|
||||||
on_receive_decoded_frame(decoded_frame);
|
on_receive_decoded_frame(decoded_frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,28 +78,3 @@ int VideoDecoder::Decode(
|
|||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VideoDecoder::GetFrame(uint8_t *yuv_data, uint32_t &width, uint32_t &height,
|
|
||||||
uint32_t &size) {
|
|
||||||
if (nullptr == decoder) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
cudaVideoSurfaceFormat format = decoder->GetOutputFormat();
|
|
||||||
if (format == cudaVideoSurfaceFormat_NV12) {
|
|
||||||
uint8_t *data = nullptr;
|
|
||||||
data = decoder->GetFrame();
|
|
||||||
if (data) {
|
|
||||||
// yuv_data = data;
|
|
||||||
|
|
||||||
width = decoder->GetWidth();
|
|
||||||
height = decoder->GetHeight();
|
|
||||||
size = width * height * 3 / 2;
|
|
||||||
memcpy(yuv_data, data, size);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,10 +13,8 @@ class VideoDecoder {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
int Init();
|
int Init();
|
||||||
int Decode(const uint8_t* pData, int nSize,
|
int Decode(const uint8_t* data, int size,
|
||||||
std::function<void(VideoFrame)> on_receive_decoded_frame);
|
std::function<void(VideoFrame)> on_receive_decoded_frame);
|
||||||
int GetFrame(uint8_t* yuv_data, uint32_t& width, uint32_t& height,
|
|
||||||
uint32_t& size);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NvDecoder* decoder = nullptr;
|
NvDecoder* decoder = nullptr;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include "ffmpeg_decoder.h"
|
||||||
#include "ice_transmission.h"
|
#include "ice_transmission.h"
|
||||||
#include "nv_decoder.h"
|
|
||||||
#include "nv_encoder.h"
|
#include "nv_encoder.h"
|
||||||
#include "ws_transmission.h"
|
#include "ws_transmission.h"
|
||||||
|
|
||||||
|
|||||||
2
thirdparty/ffmpeg/xmake.lua
vendored
2
thirdparty/ffmpeg/xmake.lua
vendored
@@ -1,4 +1,4 @@
|
|||||||
package("ffmpeg")
|
package("ffmpegxx")
|
||||||
set_homepage("https://www.ffmpeg.org")
|
set_homepage("https://www.ffmpeg.org")
|
||||||
set_description("A collection of libraries to process multimedia content such as audio, video, subtitles and related metadata.")
|
set_description("A collection of libraries to process multimedia content such as audio, video, subtitles and related metadata.")
|
||||||
set_license("GPL-3.0")
|
set_license("GPL-3.0")
|
||||||
|
|||||||
16
xmake.lua
16
xmake.lua
@@ -9,6 +9,14 @@ add_rules("mode.release", "mode.debug")
|
|||||||
add_requires("asio 1.24.0", "nlohmann_json", "spdlog 1.11.0")
|
add_requires("asio 1.24.0", "nlohmann_json", "spdlog 1.11.0")
|
||||||
add_requires("libjuice", {system = false})
|
add_requires("libjuice", {system = false})
|
||||||
|
|
||||||
|
if is_os("windows") then
|
||||||
|
add_requires("vcpkg::ffmpeg 5.1.2", {configs = {shared = false}})
|
||||||
|
add_packages("vcpkg::ffmpeg")
|
||||||
|
elseif is_os("linux") then
|
||||||
|
add_requires("ffmpeg 5.1.2", {system = false})
|
||||||
|
add_packages("ffmpeg")
|
||||||
|
end
|
||||||
|
|
||||||
add_defines("JUICE_STATIC")
|
add_defines("JUICE_STATIC")
|
||||||
add_defines("ASIO_STANDALONE", "ASIO_HAS_STD_TYPE_TRAITS", "ASIO_HAS_STD_SHARED_PTR",
|
add_defines("ASIO_STANDALONE", "ASIO_HAS_STD_TYPE_TRAITS", "ASIO_HAS_STD_SHARED_PTR",
|
||||||
"ASIO_HAS_STD_ADDRESSOF", "ASIO_HAS_STD_ATOMIC", "ASIO_HAS_STD_CHRONO", "ASIO_HAS_CSTDINT", "ASIO_HAS_STD_ARRAY",
|
"ASIO_HAS_STD_ADDRESSOF", "ASIO_HAS_STD_ATOMIC", "ASIO_HAS_STD_CHRONO", "ASIO_HAS_CSTDINT", "ASIO_HAS_STD_ARRAY",
|
||||||
@@ -17,6 +25,7 @@ add_defines("ASIO_STANDALONE", "ASIO_HAS_STD_TYPE_TRAITS", "ASIO_HAS_STD_SHARED_
|
|||||||
if is_os("windows") then
|
if is_os("windows") then
|
||||||
add_defines("_WEBSOCKETPP_CPP11_INTERNAL_")
|
add_defines("_WEBSOCKETPP_CPP11_INTERNAL_")
|
||||||
add_links("ws2_32", "Bcrypt")
|
add_links("ws2_32", "Bcrypt")
|
||||||
|
add_links("windowsapp", "User32", "Strmiids", "Mfuuid")
|
||||||
add_requires("cuda")
|
add_requires("cuda")
|
||||||
elseif is_os("linux") then
|
elseif is_os("linux") then
|
||||||
add_links("pthread")
|
add_links("pthread")
|
||||||
@@ -90,14 +99,14 @@ target("media")
|
|||||||
set_kind("static")
|
set_kind("static")
|
||||||
add_deps("log", "frame")
|
add_deps("log", "frame")
|
||||||
add_packages("cuda")
|
add_packages("cuda")
|
||||||
add_links("cuda", "nvencodeapi", "nvcuvid")
|
|
||||||
add_files("src/media/video/encode/nvcodec/*.cpp",
|
add_files("src/media/video/encode/nvcodec/*.cpp",
|
||||||
"src/media/video/decode/nvcodec/*.cpp")
|
"src/media/video/decode/nvcodec/*.cpp", "src/media/video/decode/ffmpeg/*.cpp")
|
||||||
add_includedirs("src/media/video/encode/nvcodec",
|
add_includedirs("src/media/video/encode/nvcodec",
|
||||||
"src/media/video/decode/nvcodec",
|
"src/media/video/decode/nvcodec", "src/media/video/decode/ffmpeg",
|
||||||
"thirdparty/nvcodec/Interface",
|
"thirdparty/nvcodec/Interface",
|
||||||
"thirdparty/nvcodec/Samples", {public = true})
|
"thirdparty/nvcodec/Samples", {public = true})
|
||||||
add_linkdirs("thirdparty/nvcodec/Lib/x64")
|
add_linkdirs("thirdparty/nvcodec/Lib/x64")
|
||||||
|
add_links("cuda", "nvencodeapi", "nvcuvid")
|
||||||
|
|
||||||
target("qos")
|
target("qos")
|
||||||
set_kind("static")
|
set_kind("static")
|
||||||
@@ -120,6 +129,7 @@ target("pc")
|
|||||||
add_packages("asio", "nlohmann_json", "cuda")
|
add_packages("asio", "nlohmann_json", "cuda")
|
||||||
add_includedirs("src/transmission", {public = true})
|
add_includedirs("src/transmission", {public = true})
|
||||||
|
|
||||||
|
|
||||||
target("projectx")
|
target("projectx")
|
||||||
set_kind("shared")
|
set_kind("shared")
|
||||||
add_deps("log")
|
add_deps("log")
|
||||||
|
|||||||
Reference in New Issue
Block a user