#include "PL_H264Decoder.h" #include // for SPropRecord #include extern "C" { #include #include #include } struct H264Decoder_Internal { uint8_t buffer[1920*1080*3]; size_t buffSize; size_t buffSizeMax; bool fmtp_set_to_context; bool payError; AVCodecContext* pAVCodecContext; AVFrame* pAVFrame; H264Decoder_Internal() : buffSize(0), buffSizeMax(sizeof(buffer)), fmtp_set_to_context(false), payError(false), pAVCodecContext(nullptr), pAVFrame(nullptr) { } ~H264Decoder_Internal() { } void reset() { buffSize = 0; fmtp_set_to_context = false; payError = false; } }; PipeLineElem* create_PL_H264Decoder() { return new PL_H264Decoder; } PL_H264Decoder::PL_H264Decoder() : internal(new H264Decoder_Internal) { } PL_H264Decoder::~PL_H264Decoder() { delete (H264Decoder_Internal*)internal; internal= nullptr; } bool PL_H264Decoder::init(void* args) { H264Decoder_Internal* in = (H264Decoder_Internal*)internal; in->reset(); return true; } void PL_H264Decoder::finit() { H264Decoder_Internal* in = (H264Decoder_Internal*)internal; } SPropRecord* parseSPropParameterSets(char const* sPropParameterSetsStr, size_t& numSPropRecords) { // Make a copy of the input string, so we can replace the commas with '\0's: char* inStr = strDup(sPropParameterSetsStr); if (inStr == NULL) { numSPropRecords = 0; return NULL; } // Count the number of commas (and thus the number of parameter sets): numSPropRecords = 1; char* s; for (s = inStr; *s != '\0'; ++s) { if (*s == ',') { ++numSPropRecords; *s = '\0'; } } // Allocate and fill in the result array: SPropRecord* resultArray = new SPropRecord[numSPropRecords]; s = inStr; for (unsigned i = 0; i < numSPropRecords; ++i) { resultArray[i].sPropBytes = new uint8_t[256]; size_t sPropLength = 0; base64_decode(s, strlen(s), (char*)resultArray[i].sPropBytes, &sPropLength, 0); resultArray[i].sPropLength = sPropLength; s += strlen(s) + 1; } delete[] inStr; return resultArray; } bool initH264DecoderEnv(H264Decoder_Internal* in, uint8_t* sps, size_t spsSize, uint8_t* pps, size_t ppsSize) { av_register_all(); // find the video encoder AVCodec* avCodec = avcodec_find_decoder(AV_CODEC_ID_H264); if (!avCodec) { printf("codec not found!\n"); return false; } in->pAVCodecContext = avcodec_alloc_context3(avCodec); in->pAVCodecContext->time_base.num = 1; in->pAVCodecContext->frame_number = 1; in->pAVCodecContext->codec_type = AVMEDIA_TYPE_VIDEO; in->pAVCodecContext->bit_rate = 0; in->pAVCodecContext->time_base.den = 25; in->pAVCodecContext->width = 1920; in->pAVCodecContext->height = 1080; if (in->pAVCodecContext->extradata == NULL) { int totalsize = 0; unsigned char* tmp = NULL; unsigned char nalu_header[4] = { 0, 0, 0, 1 }; totalsize = 8 + spsSize + ppsSize; tmp = new unsigned char[totalsize]; memcpy(tmp, nalu_header, 4); memcpy(tmp + 4, sps, spsSize); memcpy(tmp + 4 + spsSize, nalu_header, 4); memcpy(tmp + 4 + spsSize + 4, pps, ppsSize); in->pAVCodecContext->extradata_size = totalsize; in->pAVCodecContext->extradata = tmp; } if(avcodec_open2(in->pAVCodecContext, avCodec, NULL) >= 0) in->pAVFrame = av_frame_alloc();// Allocate video frame else return false; return true; } bool decodeH264(H264Decoder_Internal* in, uint8_t* buffer, size_t buffSize) { AVPacket packet = {0}; int frameFinished = buffSize; if (av_packet_from_data(&packet, buffer, buffSize) != 0) { printf("av_packet_from_data error\n"); return false; } // decode avcodec_decode_video2(in->pAVCodecContext, in->pAVFrame, &frameFinished, &packet); if(frameFinished) { // decode ok int picSize = in->pAVCodecContext->height * in->pAVCodecContext->width; in->buffSize = picSize * 1.5; int height = in->pAVFrame->height; int width = in->pAVFrame->width; // write yuv420 int a=0; for (int i = 0; i < height; i++) { memcpy(in->buffer + a, in->pAVFrame->data[0] + i * in->pAVFrame->linesize[0], width); a += width; } for (int i=0; ibuffer + a, in->pAVFrame->data[1] + i * in->pAVFrame->linesize[1], width / 2); a += width / 2; } for (int i=0; ibuffer + a, in->pAVFrame->data[2] + i * in->pAVFrame->linesize[2], width / 2); a += width / 2; } //in->buffer readly //static size_t f=0; //char fname[50]; //sprintf(fname, "%u.yuv420", ++f); //FILE * pFile = fopen (fname,"wb"); //fwrite (in->buffer , sizeof(char), in->buffSize, pFile); //fclose(pFile); } else printf("incomplete frame\n"); } bool PL_H264Decoder::pay(const PipeMaterial& pm) { H264Decoder_Internal* in = (H264Decoder_Internal*)internal; if (!in->fmtp_set_to_context) { if (manager == NULL) return false; std::string fmtp(manager->get_global_param(PLGP_RTSP_FMTP)); if (fmtp.empty()) return false; size_t numSPropRecords = 0; SPropRecord *p_record = parseSPropParameterSets(fmtp.c_str(), numSPropRecords); if (numSPropRecords < 2) return false;//#todo log SPropRecord &sps = p_record[0]; SPropRecord &pps = p_record[1]; bool ret = initH264DecoderEnv(in, sps.sPropBytes, sps.sPropLength, pps.sPropBytes, pps.sPropLength); if (!ret) return false; // #todo log else in->fmtp_set_to_context = true; } in->payError = decodeH264(in, pm.buffer, pm.buffSize); return in->payError; } bool PL_H264Decoder::gain(PipeMaterial& pm) { H264Decoder_Internal* in = (H264Decoder_Internal*)internal; if (!in->payError) { pm.buffer = in->buffer; pm.buffSize = in->buffSize; } pm.former = this; return in->payError; }