diff --git a/test/support_test/video.h264 b/test/support_test/video.h264 index 5834918..db50cf1 100755 Binary files a/test/support_test/video.h264 and b/test/support_test/video.h264 differ diff --git a/utils/MediaBase/src/FfmpegDecoder.cpp b/utils/MediaBase/src/FfmpegDecoder.cpp index 2d64073..8be9226 100644 --- a/utils/MediaBase/src/FfmpegDecoder.cpp +++ b/utils/MediaBase/src/FfmpegDecoder.cpp @@ -146,20 +146,13 @@ void inline FfmpegDecoder::AVParseData(const void *data, const size_t &size, } uint8_t *frameData = (uint8_t *)data; size_t data_size = size; - size_t parse_size = 0; while (data_size > 0) { - if (data_size > 4096) { - parse_size = 4096; - } - else { - parse_size = data_size; - } int ret = av_parser_parse2(mParser, mCodecCtx, &mPacket->data, &mPacket->size, frameData, - parse_size, + data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); diff --git a/utils/MediaBase/src/FfmpegMuxStreamV2.cpp b/utils/MediaBase/src/FfmpegMuxStreamV2.cpp index ea69a05..235979b 100644 --- a/utils/MediaBase/src/FfmpegMuxStreamV2.cpp +++ b/utils/MediaBase/src/FfmpegMuxStreamV2.cpp @@ -34,7 +34,7 @@ extern "C" { #include // #include #include -FfmpegMuxStreamV2::FfmpegMuxStreamV2() : mOutputFormat(nullptr), mOptions(nullptr) +FfmpegMuxStreamV2::FfmpegMuxStreamV2() : mOutputFormat(nullptr), mOptions(nullptr), mFilesMuxing(false) { } StatusCode FfmpegMuxStreamV2::OpenOutputFile(const std::string &fileName) @@ -43,7 +43,7 @@ StatusCode FfmpegMuxStreamV2::OpenOutputFile(const std::string &fileName) } StatusCode FfmpegMuxStreamV2::CloseOutputFile(void) { - if (mOutputFormat && mOutputFormat->pb) { + if (mOutputFormat && mOutputFormat->pb && mFilesMuxing) { av_write_trailer(mOutputFormat); } if (mVideoStream) { @@ -64,6 +64,38 @@ StatusCode FfmpegMuxStreamV2::CloseOutputFile(void) } void FfmpegMuxStreamV2::GetStreamData(const void *data, const size_t &size, const StreamInfo &streamInfo) { + int ret = 0; + if (!mFilesMuxing) { + bool fileMuxing = false; + fileMuxing = mVideoStream->CheckStreamHeader(data, size); + if (fileMuxing) { + av_dump_format(mOutputFormat, 0, "./test.mp4", 1); + /* open the output file, if needed */ + if (!(mOutputFormat->oformat->flags & AVFMT_NOFILE)) { + ret = avio_open(&mOutputFormat->pb, "./test.mp4", AVIO_FLAG_WRITE); + if (ret < 0) { + char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; + LogError("Could not open '%s': %s\n", + "./test.mp4", + av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + } + } + /* Write the stream header, if any. */ + ret = avformat_write_header(mOutputFormat, nullptr); + 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; + } + mFilesMuxing = true; + } + else { + LogWarning("Stream header not found, skip this frame.\n"); + return; + } + } + LogInfo("Write frame size: %zu.\n", size); if (streamInfo.mType == STREAM_TYPE_VIDEO_H264 && mVideoStream) { mVideoStream->WriteSourceData(data, size); } @@ -71,7 +103,7 @@ void FfmpegMuxStreamV2::GetStreamData(const void *data, const size_t &size, cons mAudioStream->WriteSourceData(data, size); } } -StatusCode FfmpegMuxStreamV2::OpenMuxOutputFile(const std::string &fileName) +StatusCode inline FfmpegMuxStreamV2::OpenMuxOutputFile(const std::string &fileName) { AVDictionary *opt = nullptr; int ret = 0; @@ -84,7 +116,8 @@ StatusCode FfmpegMuxStreamV2::OpenMuxOutputFile(const std::string &fileName) /* Add the audio and video streams using the default format codecs * and initialize the codecs. */ if (mOutputFormat->oformat->video_codec != AV_CODEC_ID_NONE) { - mVideoStream = AddStream(mOutputFormat, mOutputFormat->oformat->video_codec, AV_CODEC_ID_H264); + mVideoStream = AddStream(mOutputFormat, AV_CODEC_ID_NONE, AV_CODEC_ID_NONE); + // mVideoStream = AddStream(mOutputFormat, mOutputFormat->oformat->video_codec, AV_CODEC_ID_H264); mVideoStream->SetWriteSourceDataCallback( std::bind(&FfmpegMuxStreamV2::GetAVPacketDataCallback, this, std::placeholders::_1)); } @@ -93,6 +126,7 @@ StatusCode FfmpegMuxStreamV2::OpenMuxOutputFile(const std::string &fileName) mAudioStream->SetWriteSourceDataCallback( std::bind(&FfmpegMuxStreamV2::GetAVPacketDataCallback, this, std::placeholders::_1)); } + return CreateStatusCode(STATUS_CODE_OK); av_dump_format(mOutputFormat, 0, fileName.c_str(), 1); /* open the output file, if needed */ if (!(mOutputFormat->oformat->flags & AVFMT_NOFILE)) { @@ -112,6 +146,7 @@ StatusCode FfmpegMuxStreamV2::OpenMuxOutputFile(const std::string &fileName) av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); return CreateStatusCode(STATUS_CODE_NOT_OK); } + mFilesMuxing = true; return CreateStatusCode(STATUS_CODE_OK); } void FfmpegMuxStreamV2::GetAVPacketDataCallback(AVPacket *pkt) diff --git a/utils/MediaBase/src/FfmpegMuxStreamV2.cpp_bak b/utils/MediaBase/src/FfmpegMuxStreamV2.cpp_bak new file mode 100644 index 0000000..af1c292 --- /dev/null +++ b/utils/MediaBase/src/FfmpegMuxStreamV2.cpp_bak @@ -0,0 +1,137 @@ +/* + * 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 "FfmpegMuxStreamV2.h" +#include "FfmpegOutputStream.h" +#include "ILog.h" +#include "MediaBase.h" +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +#include +// #include +#include +FfmpegMuxStreamV2::FfmpegMuxStreamV2() : mOutputFormat(nullptr), mOptions(nullptr) +{ +} +StatusCode FfmpegMuxStreamV2::OpenOutputFile(const std::string &fileName) +{ + return OpenMuxOutputFile(fileName); +} +StatusCode FfmpegMuxStreamV2::CloseOutputFile(void) +{ + if (mOutputFormat && mOutputFormat->pb) { + av_write_trailer(mOutputFormat); + } + if (mVideoStream) { + mVideoStream->UnInit(); + } + if (mAudioStream) { + mAudioStream->UnInit(); + } + if (nullptr == mOutputFormat) { + return CreateStatusCode(STATUS_CODE_OK); + } + if (!(mOutputFormat->oformat->flags & AVFMT_NOFILE)) { + /* Close the output file. */ + avio_closep(&mOutputFormat->pb); + } + avformat_free_context(mOutputFormat); + return CreateStatusCode(STATUS_CODE_OK); +} +void FfmpegMuxStreamV2::GetStreamData(const void *data, const size_t &size, const StreamInfo &streamInfo) +{ + if (streamInfo.mType == STREAM_TYPE_VIDEO_H264 && mVideoStream) { + mVideoStream->WriteSourceData(data, size); + } + if (streamInfo.mType == STREAM_TYPE_AUDIO_G711A && mAudioStream) { + mAudioStream->WriteSourceData(data, size); + } +} +StatusCode inline FfmpegMuxStreamV2::OpenMuxOutputFile(const std::string &fileName) +{ + AVDictionary *opt = nullptr; + int ret = 0; + /* allocate the output media context */ + avformat_alloc_output_context2(&mOutputFormat, nullptr, "mp4", fileName.c_str()); + if (!mOutputFormat) { + LogError("Could not deduce output format from file.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + /* Add the audio and video streams using the default format codecs + * and initialize the codecs. */ + if (mOutputFormat->oformat->video_codec != AV_CODEC_ID_NONE) { + mVideoStream = AddStream(mOutputFormat, mOutputFormat->oformat->video_codec, AV_CODEC_ID_H264); + mVideoStream->SetWriteSourceDataCallback( + std::bind(&FfmpegMuxStreamV2::GetAVPacketDataCallback, this, std::placeholders::_1)); + } + if (mOutputFormat->oformat->audio_codec != AV_CODEC_ID_NONE) { + mAudioStream = AddStream(mOutputFormat, mOutputFormat->oformat->audio_codec, AV_CODEC_ID_PCM_ALAW); + mAudioStream->SetWriteSourceDataCallback( + std::bind(&FfmpegMuxStreamV2::GetAVPacketDataCallback, this, std::placeholders::_1)); + } + av_dump_format(mOutputFormat, 0, fileName.c_str(), 1); + /* open the output file, if needed */ + if (!(mOutputFormat->oformat->flags & AVFMT_NOFILE)) { + ret = avio_open(&mOutputFormat->pb, fileName.c_str(), AVIO_FLAG_WRITE); + if (ret < 0) { + char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; + LogError("Could not open '%s': %s\n", + fileName.c_str(), + av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + } + } + /* 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 CreateStatusCode(STATUS_CODE_NOT_OK); + } + return CreateStatusCode(STATUS_CODE_OK); +} +void FfmpegMuxStreamV2::GetAVPacketDataCallback(AVPacket *pkt) +{ + // std::lock_guard locker(mMutex); + 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)); + } +} +std::shared_ptr FfmpegMuxStreamV2::AddStream(AVFormatContext *outputFormat, + enum AVCodecID encodecId, enum AVCodecID decodecId) +{ + auto stream = std::make_shared(encodecId, decodecId); + stream->Init(outputFormat); + return stream; +} \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegMuxStreamV2.h b/utils/MediaBase/src/FfmpegMuxStreamV2.h index cc1ea90..3d429de 100644 --- a/utils/MediaBase/src/FfmpegMuxStreamV2.h +++ b/utils/MediaBase/src/FfmpegMuxStreamV2.h @@ -63,5 +63,6 @@ private: std::shared_ptr mVideoStream; std::shared_ptr mAudioStream; AVDictionary *mOptions; + bool mFilesMuxing; }; #endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegOutputStream.cpp b/utils/MediaBase/src/FfmpegOutputStream.cpp index b4e8004..b92f617 100644 --- a/utils/MediaBase/src/FfmpegOutputStream.cpp +++ b/utils/MediaBase/src/FfmpegOutputStream.cpp @@ -30,7 +30,7 @@ extern "C" { #include #include FfmpegOutputStream::FfmpegOutputStream(const AVCodecID &encodecId, const AVCodecID &dncodecId) - : mEncodecId(encodecId), mDeccodecId(dncodecId), mTmpPkt(nullptr), mStream(nullptr) + : mEncodecId(encodecId), mDeccodecId(dncodecId), mTmpPkt(nullptr), mStream(nullptr), mStreamHeaderWritten(false) { } bool FfmpegOutputStream::Init(AVFormatContext *outputFormat) @@ -46,33 +46,107 @@ bool FfmpegOutputStream::Init(AVFormatContext *outputFormat) LogError("Could not allocate stream\n"); return false; } - mDecoder = std::make_shared(mDeccodecId); - mDecoder->Init(); + if (mDeccodecId != AV_CODEC_ID_NONE) { + mDecoder = std::make_shared(mDeccodecId); + mDecoder->Init(); + } mStream->id = outputFormat->nb_streams - 1; - mEncoder = std::make_shared(mEncodecId); - mEncoder->Init(outputFormat->flags); - mStream->time_base = mEncoder->GetTimeBase(); - mEncoder->OpenEncoder(nullptr, mStream); + if (mEncodecId != AV_CODEC_ID_NONE) { + mEncoder = std::make_shared(mEncodecId); + mEncoder->Init(outputFormat->flags); + mStream->time_base = mEncoder->GetTimeBase(); + mEncoder->OpenEncoder(nullptr, mStream); + } + else { + mStream->time_base = (AVRational){1, 15}; + // int ret = avcodec_parameters_copy(mStream->codecpar, in_codecpar); + // if (ret < 0) { + // LogError("Failed to copy codec parameters\n"); + // return false; + // } + mStream->codecpar->codec_id = AV_CODEC_ID_H264; + mStream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + mStream->codecpar->width = 1920; + mStream->codecpar->height = 2160; + // mStream->codecpar->bit_rate = 2073; + mStream->codecpar->format = AV_PIX_FMT_YUV420P; + mStream->codecpar->codec_tag = 0; + mStream->codecpar->extradata = nullptr; + mStream->codecpar->extradata_size = 0; + // mEncoder = std::make_shared(AV_CODEC_ID_MPEG4); + // mEncoder->Init(outputFormat->flags); + // mStream->time_base = mEncoder->GetTimeBase(); + // mEncoder->OpenEncoder(nullptr, mStream); + // mEncoder->UnInit(); + // mEncoder.reset(); + } return true; } void FfmpegOutputStream::UnInit(void) { - mEncoder->UnInit(); - mDecoder->UnInit(); + if (mEncoder) { + mEncoder->UnInit(); + mEncoder.reset(); + } + if (mDecoder) { + mDecoder->UnInit(); + mDecoder.reset(); + } av_packet_free(&mTmpPkt); } void FfmpegOutputStream::WriteSourceData(const void *data, const size_t &size) { - mDecoder->DecodeData(data, size, mDecodeCallback); + if (mDecoder) { + mDecoder->DecodeData(data, size, mDecodeCallback); + return; + } + static unsigned long long u64Interval = 0; + AVRational in_timebase = (AVRational){1, 15}; + if (mEncodeCallback) { + mTmpPkt->data = (uint8_t *)data; + mTmpPkt->size = size; + mTmpPkt->stream_index = mStream->index; + mTmpPkt->pts = u64Interval * 1000; // ת���� us + mTmpPkt->dts = mTmpPkt->pts; + u64Interval += 70; + /* copy packet */ + av_packet_rescale_ts(mTmpPkt, in_timebase, mStream->time_base); + mTmpPkt->pos = -1; + mEncodeCallback(mTmpPkt); + } } void FfmpegOutputStream::SetWriteSourceDataCallback(std::function callback) { mEncodeCallback = callback; } +bool FfmpegOutputStream::CheckStreamHeader(const void *data, const size_t &size) +{ + if (mStreamHeaderWritten || mEncodecId != AV_CODEC_ID_NONE) { + return true; + } + 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; + mStreamHeaderWritten = true; + return mStreamHeaderWritten; + } + } + return false; +} void FfmpegOutputStream::GetDecodeDataCallback(AVFrame *frame) { - mEncoder->EncodeData(frame, mStream, mEncodeCallback); -} -void FfmpegOutputStream::GetEncodeDataCallback(AVPacket *pkt) -{ + if (mEncoder) { + mEncoder->EncodeData(frame, mStream, mEncodeCallback); + return; + } } \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegOutputStream.h b/utils/MediaBase/src/FfmpegOutputStream.h index 6dd68b4..58fa81e 100644 --- a/utils/MediaBase/src/FfmpegOutputStream.h +++ b/utils/MediaBase/src/FfmpegOutputStream.h @@ -45,10 +45,10 @@ public: void UnInit(void); void WriteSourceData(const void *data, const size_t &size); void SetWriteSourceDataCallback(std::function callback); + bool CheckStreamHeader(const void *data, const size_t &size); private: void GetDecodeDataCallback(AVFrame *frame); - void GetEncodeDataCallback(AVPacket *pkt); private: const AVCodecID mEncodecId; @@ -59,5 +59,6 @@ private: AVStream *mStream; std::function mDecodeCallback; std::function mEncodeCallback; + bool mStreamHeaderWritten; }; #endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegReadFile.cpp b/utils/MediaBase/src/FfmpegReadFile.cpp index 7682df5..f44b22f 100644 --- a/utils/MediaBase/src/FfmpegReadFile.cpp +++ b/utils/MediaBase/src/FfmpegReadFile.cpp @@ -60,6 +60,7 @@ StatusCode FfmpegReadFile::StartReadFile(const std::string &path) for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == mFFmpegMediaType) { mediaStreamIndex = i; + LogInfo("Find stream index=%s.\n", avcodec_get_name(pFormatCtx->streams[i]->codecpar->codec_id)); break; } }