279 lines
8.4 KiB
C++
279 lines
8.4 KiB
C++
|
||
#include "VideoCoder.h"
|
||
#include "Debuger.h"
|
||
FILE *p = nullptr;
|
||
int VideoCoder::OnBuffer(double dblSampleTime, BYTE * pBuffer, long lBufferSize)
|
||
{
|
||
this->Encode(pBuffer, lBufferSize, AV_PIX_FMT_YUV420P);
|
||
return 0;
|
||
}
|
||
|
||
int VideoCoder::OnCameraData(uint8_t * dat, uint32_t size)
|
||
{
|
||
this->Encode(dat, size, AV_PIX_FMT_YUV420P);
|
||
return 0;
|
||
}
|
||
|
||
int VideoCoder::SetDestPix(uint8_t width, uint8_t height)
|
||
{
|
||
this->mDestHeight = height;
|
||
this->mDestWidth = width;
|
||
return 0;
|
||
}
|
||
|
||
VideoCoder::VideoCoder(int width, int height, AVPixelFormat formt)
|
||
:mObserver(nullptr),mFrame(nullptr), mPitureBuffer(nullptr), mFormatCtx(nullptr), mOutputFmt(nullptr),
|
||
mVideoStream(nullptr), mCodecCtx(nullptr), mCodec(nullptr)
|
||
{
|
||
AVCodecID codec_id = AV_CODEC_ID_H264;
|
||
mCodec = avcodec_find_encoder(codec_id);
|
||
|
||
av_register_all();
|
||
if (nullptr == p) {
|
||
p = fopen("shit.h264", "wb");
|
||
}
|
||
this->mWidth = width;
|
||
this->mHeight = height;
|
||
this->mInformat = formt;
|
||
if (!mCodec) {
|
||
printf("Codec not found\n");
|
||
}
|
||
this->mFormatCtx = avformat_alloc_context();
|
||
|
||
//原文链接:https ://blog.csdn.net/leixiaohua1020/article/details/25430425 引用来自雷神的文章,雷神保佑
|
||
this->mOutputFmt = av_guess_format(NULL, "shit.h264", NULL);
|
||
this->mFormatCtx->oformat = mOutputFmt;
|
||
mCodecCtx = avcodec_alloc_context3(mCodec);
|
||
if (!mCodecCtx) {
|
||
printf("Could not allocate video codec context\n");
|
||
}
|
||
mCodecCtx->bit_rate = 1000;
|
||
this->mDestHeight = 480;
|
||
this->mDestWidth = 640;
|
||
mCodecCtx->width = this->mDestWidth;
|
||
mCodecCtx->height = this->mDestHeight;
|
||
mCodecCtx->time_base.num = 1;
|
||
mCodecCtx->time_base.den = 10;
|
||
mCodecCtx->max_b_frames = 0;
|
||
mCodecCtx->qmin = 10;
|
||
mCodecCtx->qmax = 25;
|
||
//mCodecCtx->flags |= AV_CODEC_FLAG_LOW_DELAY;
|
||
mCodecCtx->gop_size = 10;
|
||
mCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
|
||
av_opt_set(mCodecCtx->priv_data, "preset", "superfast", 0);
|
||
av_opt_set(mCodecCtx->priv_data, "tune", "zerolatency", 0);
|
||
if (avcodec_open2(mCodecCtx, mCodec, NULL) < 0) {
|
||
printf("Could not open codec\n");
|
||
}
|
||
mFrame = av_frame_alloc();
|
||
if (!mFrame) {
|
||
printf("Could not allocate video frame\n");
|
||
}
|
||
mFrame->format = mCodecCtx->pix_fmt;
|
||
mFrame->width = mCodecCtx->width/2;
|
||
mFrame->height = mCodecCtx->height/2;
|
||
mFrame->pts = 0;
|
||
int ret = av_image_alloc(mFrame->data, mFrame->linesize, mCodecCtx->width, mCodecCtx->height,
|
||
mCodecCtx->pix_fmt, 8);
|
||
if (ret < 0) {
|
||
printf("Could not allocate raw picture buffer\n");
|
||
}
|
||
|
||
// 让我们假设分辨率都是不可改变的,AvPack可以复用
|
||
avformat_write_header(mFormatCtx, NULL);
|
||
int picture_size = avpicture_get_size(AV_PIX_FMT_YUV420P, mCodecCtx->width, mCodecCtx->height);
|
||
}
|
||
|
||
VideoCoder::~VideoCoder()
|
||
{
|
||
fclose(p);
|
||
}
|
||
|
||
void VideoCoder::Encode(uint8_t * src, int size, enum AVPixelFormat format)
|
||
{
|
||
uint8_t *pFrame[4];
|
||
int lineSize[4];
|
||
static int debugs = 1;
|
||
//如果不是yuv420p就转成yuv420p
|
||
int iFramesize;
|
||
|
||
av_init_packet(&mAVPack);
|
||
mAVPack.data = NULL; // packet data will be allocated by the encoder
|
||
|
||
int ret = av_image_alloc(pFrame, lineSize, mWidth, mHeight, AV_PIX_FMT_YUV420P, 1);
|
||
if (ret< 0) {
|
||
Debuger::Debug(L"Could not allocate destination image\n");
|
||
}
|
||
|
||
if (this->mInformat != AV_PIX_FMT_YUV420P || (this->mDestHeight != mHeight)) {
|
||
int size = avpicture_get_size(this->mInformat,mWidth,mHeight);
|
||
this->forceYUV420P(src, size, mInformat, (uint8_t ***)&pFrame,&iFramesize);
|
||
//仅仅支持yuv420p
|
||
mFrame->data[0] = pFrame[0]; //Y
|
||
mFrame->data[1] = pFrame[1]; //U
|
||
mFrame->data[2] = pFrame[2]; //V
|
||
}
|
||
else {
|
||
mFrame->data[0] = src; //Y
|
||
mFrame->data[1] = src + mWidth*mHeight; //U
|
||
mFrame->data[2] = src + mWidth*mHeight + mWidth*mHeight/4; //V
|
||
}
|
||
//PTS
|
||
mFrame->pts++;
|
||
int got_picture = 0;
|
||
//Encode
|
||
avcodec_encode_video2(mCodecCtx, &mAVPack, mFrame, &got_picture);
|
||
if (got_picture > 0) {
|
||
if(nullptr != this->mObserver)
|
||
this->mObserver->OnGetCodeFrame(mAVPack.data, mAVPack.size);
|
||
}
|
||
//Debuger::Debug(L"Succeed to encode frame: %5d\tsize:%5d\n", 1, mAVPack.size);
|
||
//fwrite(mAVPack.data, 1, mAVPack.size, p);
|
||
//fflush(p);
|
||
// 刷新coder,防止包挤压
|
||
av_packet_unref(&mAVPack);
|
||
av_freep(&pFrame[0]);
|
||
free(pFrame[0]);
|
||
//av_freep(&mFrame->data[0]);
|
||
//av_freep(&mFrame->data[0]);
|
||
}
|
||
|
||
void VideoCoder::SetOutPutPixel(unsigned int width, unsigned int height)
|
||
{
|
||
this->mHeight = height;
|
||
this->mWidth = width;
|
||
}
|
||
|
||
int VideoCoder::flushCoder(AVFormatContext *fmt_ctx, unsigned int stream_index)
|
||
{
|
||
int ret;
|
||
int got_frame;
|
||
AVPacket enc_pkt;
|
||
if (!(this->mFormatCtx->streams[stream_index]->codec->codec->capabilities ))
|
||
return 0;
|
||
while (1) {
|
||
enc_pkt.data = NULL;
|
||
enc_pkt.size = 0;
|
||
av_init_packet(&enc_pkt);
|
||
ret = avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec, &enc_pkt,
|
||
NULL, &got_frame);
|
||
av_frame_free(NULL);
|
||
if (ret < 0)
|
||
break;
|
||
if (!got_frame) {
|
||
ret = 0;
|
||
break;
|
||
}
|
||
Debuger::Debug(L"Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n", enc_pkt.size);
|
||
/* mux encoded frame */
|
||
ret = av_write_frame(fmt_ctx, &enc_pkt);
|
||
if (ret < 0)
|
||
break;
|
||
}
|
||
return ret;
|
||
}
|
||
// 强制把其他个数的数据转换成libav可以认得到的数据
|
||
int VideoCoder::forceYUV420P(uint8_t * src, int size,
|
||
AVPixelFormat format,uint8_t **dst[4],int *len)
|
||
{
|
||
uint8_t *src_data[4];
|
||
int src_linesize[4];
|
||
uint8_t *dst_data[4];
|
||
int dst_linesize[4];
|
||
struct SwsContext *img_convert_ctx;
|
||
int ret = 0;
|
||
|
||
if (nullptr == dst || nullptr == len) {
|
||
return -2;
|
||
}
|
||
|
||
int src_bpp = av_get_bits_per_pixel(av_pix_fmt_desc_get(format));
|
||
AVPixelFormat dst_pixfmt = AV_PIX_FMT_YUV420P;
|
||
int dst_bpp = av_get_bits_per_pixel(av_pix_fmt_desc_get(dst_pixfmt));
|
||
|
||
ret = av_image_alloc(src_data, src_linesize, mWidth, mHeight, format, 1);
|
||
if (ret< 0) {
|
||
Debuger::Debug(L"Could not allocate source image\n");
|
||
return -1;
|
||
}
|
||
ret = av_image_alloc(dst_data, dst_linesize, mDestWidth, mDestHeight, AV_PIX_FMT_YUV420P, 1);
|
||
if (ret< 0) {
|
||
Debuger::Debug(L"Could not allocate destination image\n");
|
||
return -1;
|
||
}
|
||
|
||
img_convert_ctx = sws_alloc_context();
|
||
//Show AVOption
|
||
//av_opt_show2(img_convert_ctx, stdout, AV_OPT_FLAG_VIDEO_PARAM, 0);
|
||
//Set Value
|
||
av_opt_set_int(img_convert_ctx, "sws_flags", SWS_BICUBIC | SWS_PRINT_INFO, 0);
|
||
av_opt_set_int(img_convert_ctx, "srcw", mWidth, 0);
|
||
av_opt_set_int(img_convert_ctx, "srch", mHeight, 0);
|
||
av_opt_set_int(img_convert_ctx, "src_format", format, 0);
|
||
av_opt_set_int(img_convert_ctx, "src_range", 1, 0);
|
||
|
||
av_opt_set_int(img_convert_ctx, "dstw", mDestWidth, 0);
|
||
av_opt_set_int(img_convert_ctx, "dsth", mDestHeight, 0);
|
||
av_opt_set_int(img_convert_ctx, "dst_format", dst_pixfmt, 0);
|
||
av_opt_set_int(img_convert_ctx, "dst_range", 1, 0);
|
||
sws_init_context(img_convert_ctx, NULL, NULL);
|
||
|
||
// 设置输入
|
||
switch (format) {
|
||
case AV_PIX_FMT_GRAY8: {
|
||
memcpy(src_data[0], src, mWidth*mHeight);
|
||
break;
|
||
}
|
||
case AV_PIX_FMT_YUV420P: {
|
||
memcpy(src_data[0], src, mWidth*mHeight); //Y
|
||
memcpy(src_data[1], src + mWidth*mHeight, mWidth*mHeight / 4); //U
|
||
memcpy(src_data[2], src + mWidth*mHeight * 5 / 4, mWidth*mHeight / 4); //V
|
||
break;
|
||
}
|
||
case AV_PIX_FMT_YUV422P: {
|
||
memcpy(src_data[0], src, mWidth*mHeight); //Y
|
||
memcpy(src_data[1], src + mWidth*mHeight, mWidth*mHeight / 2); //U
|
||
memcpy(src_data[2], src + mWidth*mHeight * 3 / 2, mWidth*mHeight / 2); //V
|
||
break;
|
||
}
|
||
case AV_PIX_FMT_YUV444P: {
|
||
memcpy(src_data[0], src, mWidth*mHeight); //Y
|
||
memcpy(src_data[1], src + mWidth*mHeight, mWidth*mHeight); //U
|
||
memcpy(src_data[2], src + mWidth*mHeight * 2, mWidth*mHeight); //V
|
||
break;
|
||
}
|
||
case AV_PIX_FMT_YUYV422: {
|
||
memcpy(src_data[0], src, mWidth*mHeight * 2); //Packed
|
||
break;
|
||
}
|
||
case AV_PIX_FMT_RGB24: {
|
||
memcpy(src_data[0], src, mWidth*mHeight * 3); //Packed
|
||
break;
|
||
}
|
||
case AV_PIX_FMT_RGB32: {
|
||
memcpy(src_data[0], src, mWidth*mHeight *4); //Packed
|
||
break;
|
||
}
|
||
default: {
|
||
Debuger::Debug(L"Not Support Input Pixel Format.\n");
|
||
break;
|
||
}
|
||
}
|
||
// 转换数据
|
||
ret = sws_scale(img_convert_ctx, src_data, src_linesize, 0, mHeight, dst_data, dst_linesize);
|
||
if (ret < 0) {
|
||
return ret;
|
||
}
|
||
memcpy(dst[0], dst_data[0], mDestWidth*mDestHeight);
|
||
memcpy(dst[1], dst_data[1], mDestWidth*mDestHeight /4);
|
||
memcpy(dst[2], dst_data[2], mDestWidth*mDestHeight /4);
|
||
|
||
*len = mDestWidth*mDestHeight + mDestWidth*mDestHeight / 2;
|
||
// source此时就不需要了,但是dst要在外面free
|
||
av_freep(&src_data[0]);
|
||
av_freep(&dst_data[0]);
|
||
|
||
sws_freeContext(img_convert_ctx);
|
||
return 0;
|
||
}
|