201 lines
7.5 KiB
C++
201 lines
7.5 KiB
C++
/*
|
|
* 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 <libavcodec/avcodec.h>
|
|
#include <libavcodec/packet.h>
|
|
#include <libavformat/avformat.h>
|
|
#include <libavutil/avassert.h>
|
|
#include <libavutil/avutil.h>
|
|
#include <libavutil/channel_layout.h>
|
|
#include <libavutil/imgutils.h>
|
|
#include <libavutil/mathematics.h>
|
|
#include <libavutil/opt.h>
|
|
#include <libavutil/timestamp.h>
|
|
#include <libswresample/swresample.h>
|
|
#include <libswscale/swscale.h>
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#include <functional>
|
|
FfmpegThumbnail::FfmpegThumbnail(const AVCodecID &encodecId, const AVCodecID &dncodecId)
|
|
: mOutputFormat(nullptr), mStream(nullptr), sws_ctx(nullptr)
|
|
{
|
|
mDecoder = std::make_shared<FfmpegDecoder>(dncodecId);
|
|
mEncoder = std::make_shared<FfmpegEncoder>(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<AVPixelFormat>(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));
|
|
}
|
|
} |