/* * Copyright (c) 2023 Fancy Code. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FfmpegThumbnail.h" #include "ILog.h" #include "LinuxApi.h" #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus } #endif #include FfmpegThumbnail::FfmpegThumbnail(const AVCodecID &encodecId, const AVCodecID &dncodecId) : mOutputFormat(nullptr), mStream(nullptr), sws_ctx(nullptr) { mDecoder = std::make_shared(dncodecId); mEncoder = std::make_shared(encodecId); } void FfmpegThumbnail::Init(void) { LogInfo("FfmpegThumbnail Init\n"); mDecodeCallback = std::bind(&FfmpegThumbnail::GetDecodeDataCallback, this, std::placeholders::_1); mEncodeCallback = std::bind(&FfmpegThumbnail::GetEncodeDataCallback, this, std::placeholders::_1); } void FfmpegThumbnail::UnInit(void) { if (mOutputFormat && mOutputFormat->pb) { av_write_trailer(mOutputFormat); } if (mEncoder) { mEncoder->UnInit(); mEncoder.reset(); } if (mDecoder) { mDecoder->UnInit(); mDecoder.reset(); } if (nullptr == mOutputFormat) { return; } if (!(mOutputFormat->oformat->flags & AVFMT_NOFILE)) { /* Close the output file. */ avio_closep(&mOutputFormat->pb); } avformat_free_context(mOutputFormat); fx_system_v2("sync"); if (sws_ctx) { sws_freeContext(sws_ctx); sws_ctx = nullptr; } } bool FfmpegThumbnail::CreateThumbnail(const std::string &outputFile, const void *data, const size_t &size) { if (!mDecoder || !mDecodeCallback) { LogError("CreateThumbnail mDecoder && mDecodeCallback\n"); return true; } AVDictionary *opt = nullptr; int ret = 0; /* allocate the output media context */ avformat_alloc_output_context2(&mOutputFormat, nullptr, "image2", outputFile.c_str()); if (!mOutputFormat) { LogError("Could not deduce output format from file.\n"); return false; } /* Add the audio and video streams using the default format codecs * and initialize the codecs. */ if (mOutputFormat->oformat->video_codec != AV_CODEC_ID_NONE) { mStream = avformat_new_stream(mOutputFormat, nullptr); if (!mStream) { LogError("Could not allocate stream\n"); return false; } mStream->id = mOutputFormat->nb_streams - 1; LogInfo("Create video stream\n"); // char *pData = (char *)data; // for (size_t i = 0; i < size; i++) { // if ((0x00 == pData[i]) && (0x00 == pData[i + 1]) && (0x00 == pData[i + 2]) && (0x01 == pData[i + 3]) && // (0x5 == (pData[i + 4] & 0x1F))) { // uint8_t *extradata = (uint8_t *)av_mallocz(i + 1); // if (!extradata) { // LogError("Could not allocate extradata\n"); // return false; // } // LogInfo("Found extradata\n"); // memcpy(extradata, pData, i); // mStream->codecpar->extradata = extradata; // mStream->codecpar->extradata_size = i; // } // } } av_dump_format(mOutputFormat, 0, outputFile.c_str(), 1); /* open the output file, if needed */ // if (!(mOutputFormat->oformat->flags & AVFMT_NOFILE)) { // ret = avio_open(&mOutputFormat->pb, outputFile.c_str(), AVIO_FLAG_WRITE); // if (ret < 0) { // char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; // LogError("Could not open '%s': %s\n", // outputFile.c_str(), // av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); // } LogInfo("Open output file\n"); // } av_dict_set_int(&opt, "use_editlist", 0, 0); /* Write the stream header, if any. */ ret = avformat_write_header(mOutputFormat, &opt); if (ret < 0) { char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; LogError("Error occurred when opening output file: %s\n", av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); return false; } av_dict_free(&opt); // return true; mDecoder->Init(); mEncoder->Init(mOutputFormat->flags); mStream->time_base = mEncoder->GetTimeBase(); mEncoder->OpenEncoder(nullptr, mStream); LogInfo("Start to decode data\n"); mDecoder->DecodeData(data, size, AV_NOPTS_VALUE, mDecodeCallback); LogInfo("Decode data end\n"); return false; } void FfmpegThumbnail::GetDecodeDataCallback(AVFrame *frame) { LogInfo("GetDecodeDataCallback frame->width = %d, frame->height=%d\n", frame->width, frame->height); AVFrame *output_frame = av_frame_alloc(); output_frame->format = AV_PIX_FMT_YUV420P; output_frame->width = 640; output_frame->height = 480; // output_frame->linesize[0] = 640; // Y plane 的跨度 // output_frame->linesize[1] = 320; // U/V planes 的跨度 // output_frame->linesize[2] = 320; // U/V planes 的跨度 int jpeg_buf_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 1920, 2160, 1); LogInfo("jpeg_buf_size: %d\n", jpeg_buf_size); uint8_t *jpeg_buf = (uint8_t *)av_malloc(jpeg_buf_size); av_image_fill_arrays( output_frame->data, output_frame->linesize, jpeg_buf, AV_PIX_FMT_YUV420P, frame->width, frame->height, 1); sws_ctx = sws_getContext(1920, 2160, static_cast(frame->format), output_frame->width, output_frame->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); // 进行像素格式转换 sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, output_frame->data, output_frame->linesize); if (mEncoder) { mEncoder->EncodeData(output_frame, mStream, mEncodeCallback); // return; } av_frame_free(&output_frame); av_free(jpeg_buf); return; } void FfmpegThumbnail::GetEncodeDataCallback(AVPacket *pkt) { return; LogInfo("ggggggggggggggggggggggggggggggggggggggg GetEncodeDataCallback %d\n", pkt->size); int ret = 0; ret = av_interleaved_write_frame(mOutputFormat, pkt); /* pkt is now blank (av_interleaved_write_frame() takes ownership of * its contents and resets pkt), so that no unreferencing is necessary. * This would be different if one used av_write_frame(). */ if (ret < 0) { char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; LogInfo("Error while writing output packet: %s\n", av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); } }