优化性能,修复mp4无法播放
This commit is contained in:
parent
80e7abcd08
commit
763f1473bc
@ -24,6 +24,118 @@ extern "C" {
|
||||
|
||||
namespace rk3588 {
|
||||
|
||||
namespace {
|
||||
|
||||
static bool HasAnnexBStartCode(const uint8_t* d, size_t n) {
|
||||
if (!d || n < 4) return false;
|
||||
for (size_t i = 0; i + 3 < n; ++i) {
|
||||
if (d[i] == 0 && d[i + 1] == 0 && d[i + 2] == 1) return true;
|
||||
if (i + 4 < n && d[i] == 0 && d[i + 1] == 0 && d[i + 2] == 0 && d[i + 3] == 1) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static size_t FindStartCode(const uint8_t* d, size_t n, size_t from, size_t* sc_len) {
|
||||
for (size_t i = from; i + 3 < n; ++i) {
|
||||
if (d[i] == 0 && d[i + 1] == 0 && d[i + 2] == 1) {
|
||||
if (sc_len) *sc_len = 3;
|
||||
return i;
|
||||
}
|
||||
if (i + 4 < n && d[i] == 0 && d[i + 1] == 0 && d[i + 2] == 0 && d[i + 3] == 1) {
|
||||
if (sc_len) *sc_len = 4;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if (sc_len) *sc_len = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
static std::vector<std::vector<uint8_t>> SplitAnnexBNals(const uint8_t* d, size_t n) {
|
||||
std::vector<std::vector<uint8_t>> out;
|
||||
if (!d || n < 4) return out;
|
||||
size_t pos = 0;
|
||||
while (true) {
|
||||
size_t sc_len = 0;
|
||||
size_t sc = FindStartCode(d, n, pos, &sc_len);
|
||||
if (sc >= n) break;
|
||||
size_t nal_start = sc + sc_len;
|
||||
size_t next_sc = FindStartCode(d, n, nal_start, nullptr);
|
||||
size_t nal_end = (next_sc >= n) ? n : next_sc;
|
||||
|
||||
// Trim trailing zeros before next start code.
|
||||
while (nal_end > nal_start && d[nal_end - 1] == 0) --nal_end;
|
||||
|
||||
if (nal_end > nal_start) {
|
||||
out.emplace_back(d + nal_start, d + nal_end);
|
||||
}
|
||||
pos = nal_end;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool BuildAvccFromAnnexB(const std::vector<uint8_t>& annexb, std::vector<uint8_t>& avcc_out) {
|
||||
avcc_out.clear();
|
||||
if (annexb.empty()) return false;
|
||||
auto nals = SplitAnnexBNals(annexb.data(), annexb.size());
|
||||
|
||||
const uint8_t* sps = nullptr;
|
||||
size_t sps_len = 0;
|
||||
const uint8_t* pps = nullptr;
|
||||
size_t pps_len = 0;
|
||||
|
||||
for (const auto& nal : nals) {
|
||||
if (nal.empty()) continue;
|
||||
const uint8_t t = nal[0] & 0x1F;
|
||||
if (t == 7 && !sps) {
|
||||
sps = nal.data();
|
||||
sps_len = nal.size();
|
||||
} else if (t == 8 && !pps) {
|
||||
pps = nal.data();
|
||||
pps_len = nal.size();
|
||||
}
|
||||
}
|
||||
|
||||
if (!sps || sps_len < 4 || !pps || pps_len < 1) return false;
|
||||
|
||||
// AVCDecoderConfigurationRecord
|
||||
// https://developer.apple.com/documentation/quicktime-file-format/avcdecoderconfigurationrecord
|
||||
avcc_out.reserve(11 + sps_len + pps_len);
|
||||
avcc_out.push_back(1); // configurationVersion
|
||||
avcc_out.push_back(sps[1]); // AVCProfileIndication
|
||||
avcc_out.push_back(sps[2]); // profile_compatibility
|
||||
avcc_out.push_back(sps[3]); // AVCLevelIndication
|
||||
avcc_out.push_back(0xFF); // 6 bits reserved + lengthSizeMinusOne(3 => 4 bytes)
|
||||
avcc_out.push_back(0xE1); // 3 bits reserved + numOfSequenceParameterSets(1)
|
||||
avcc_out.push_back(static_cast<uint8_t>((sps_len >> 8) & 0xFF));
|
||||
avcc_out.push_back(static_cast<uint8_t>((sps_len) & 0xFF));
|
||||
avcc_out.insert(avcc_out.end(), sps, sps + sps_len);
|
||||
avcc_out.push_back(1); // numOfPictureParameterSets
|
||||
avcc_out.push_back(static_cast<uint8_t>((pps_len >> 8) & 0xFF));
|
||||
avcc_out.push_back(static_cast<uint8_t>((pps_len) & 0xFF));
|
||||
avcc_out.insert(avcc_out.end(), pps, pps + pps_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> AnnexBToLengthPrefixed(const uint8_t* d, size_t n) {
|
||||
std::vector<uint8_t> out;
|
||||
auto nals = SplitAnnexBNals(d, n);
|
||||
// Each NAL becomes [len_be32][nal_bytes]
|
||||
size_t total = 0;
|
||||
for (const auto& nal : nals) total += 4 + nal.size();
|
||||
out.reserve(total);
|
||||
for (const auto& nal : nals) {
|
||||
const uint32_t len = static_cast<uint32_t>(nal.size());
|
||||
out.push_back(static_cast<uint8_t>((len >> 24) & 0xFF));
|
||||
out.push_back(static_cast<uint8_t>((len >> 16) & 0xFF));
|
||||
out.push_back(static_cast<uint8_t>((len >> 8) & 0xFF));
|
||||
out.push_back(static_cast<uint8_t>((len) & 0xFF));
|
||||
out.insert(out.end(), nal.begin(), nal.end());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ClipAction::Init(const SimpleJson& config) {
|
||||
pre_sec_ = config.ValueOr<int>("pre_sec", 5);
|
||||
post_sec_ = config.ValueOr<int>("post_sec", 10);
|
||||
@ -140,16 +252,42 @@ std::string ClipAction::ProcessClipFromPackets(const std::vector<std::shared_ptr
|
||||
}
|
||||
|
||||
stream->time_base = AVRational{1, 1000};
|
||||
stream->avg_frame_rate = AVRational{std::max(1, fps_), 1};
|
||||
stream->r_frame_rate = stream->avg_frame_rate;
|
||||
stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
stream->codecpar->codec_id = cid;
|
||||
stream->codecpar->width = width;
|
||||
stream->codecpar->height = height;
|
||||
stream->codecpar->format = AV_PIX_FMT_YUV420P;
|
||||
|
||||
// MP4 requires AVCC/HVCC style extradata and length-prefixed samples.
|
||||
std::vector<uint8_t> mp4_extradata;
|
||||
if (!first->codec->extradata.empty()) {
|
||||
stream->codecpar->extradata_size = static_cast<int>(first->codec->extradata.size());
|
||||
stream->codecpar->extradata = static_cast<uint8_t*>(av_mallocz(first->codec->extradata.size() + AV_INPUT_BUFFER_PADDING_SIZE));
|
||||
std::memcpy(stream->codecpar->extradata, first->codec->extradata.data(), first->codec->extradata.size());
|
||||
const auto& ex = first->codec->extradata;
|
||||
if (cid == AV_CODEC_ID_H264) {
|
||||
if (!ex.empty() && ex[0] == 1) {
|
||||
mp4_extradata = ex; // AVCC
|
||||
} else if (HasAnnexBStartCode(ex.data(), ex.size())) {
|
||||
if (!BuildAvccFromAnnexB(ex, mp4_extradata)) {
|
||||
std::cerr << "[ClipAction] failed to build AVCC from AnnexB extradata\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cid == AV_CODEC_ID_H264 && mp4_extradata.empty()) {
|
||||
// As a fallback, try extracting SPS/PPS from the first keyframe packet.
|
||||
for (const auto& m : metas) {
|
||||
if (!m || m->magic != EncodedVideoFrameMeta::kMagic) continue;
|
||||
if (!m->pkt.key) continue;
|
||||
if (!HasAnnexBStartCode(m->pkt.data.data(), m->pkt.data.size())) continue;
|
||||
if (BuildAvccFromAnnexB(m->pkt.data, mp4_extradata)) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mp4_extradata.empty()) {
|
||||
stream->codecpar->extradata_size = static_cast<int>(mp4_extradata.size());
|
||||
stream->codecpar->extradata = static_cast<uint8_t*>(av_mallocz(mp4_extradata.size() + AV_INPUT_BUFFER_PADDING_SIZE));
|
||||
std::memcpy(stream->codecpar->extradata, mp4_extradata.data(), mp4_extradata.size());
|
||||
}
|
||||
|
||||
if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
|
||||
@ -165,12 +303,22 @@ std::string ClipAction::ProcessClipFromPackets(const std::vector<std::shared_ptr
|
||||
return "";
|
||||
}
|
||||
|
||||
// Start from the first keyframe inside the window to ensure decodability.
|
||||
size_t start_idx = 0;
|
||||
for (size_t i = 0; i < metas.size(); ++i) {
|
||||
if (metas[i] && metas[i]->pkt.key) {
|
||||
start_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t first_pts = 0;
|
||||
bool have_first = false;
|
||||
int64_t last_pts = -1;
|
||||
const int64_t frame_dur = (fps_ > 0) ? (1000LL / std::max(1, fps_)) : 40;
|
||||
|
||||
for (const auto& m : metas) {
|
||||
for (size_t i = start_idx; i < metas.size(); ++i) {
|
||||
const auto& m = metas[i];
|
||||
if (!m || m->magic != EncodedVideoFrameMeta::kMagic) continue;
|
||||
if (!m->codec || m->pkt.data.empty()) continue;
|
||||
if (m->pkt.pts_ms <= 0) continue;
|
||||
@ -184,13 +332,19 @@ std::string ClipAction::ProcessClipFromPackets(const std::vector<std::shared_ptr
|
||||
if (pts <= last_pts) pts = last_pts + 1;
|
||||
last_pts = pts;
|
||||
|
||||
std::vector<uint8_t> sample = m->pkt.data;
|
||||
if (cid == AV_CODEC_ID_H264 && HasAnnexBStartCode(sample.data(), sample.size())) {
|
||||
sample = AnnexBToLengthPrefixed(sample.data(), sample.size());
|
||||
}
|
||||
if (sample.empty()) continue;
|
||||
|
||||
AVPacket* pkt = av_packet_alloc();
|
||||
if (!pkt) continue;
|
||||
if (av_new_packet(pkt, static_cast<int>(m->pkt.data.size())) < 0) {
|
||||
if (av_new_packet(pkt, static_cast<int>(sample.size())) < 0) {
|
||||
av_packet_free(&pkt);
|
||||
continue;
|
||||
}
|
||||
std::memcpy(pkt->data, m->pkt.data.data(), m->pkt.data.size());
|
||||
std::memcpy(pkt->data, sample.data(), sample.size());
|
||||
pkt->stream_index = stream->index;
|
||||
pkt->pts = pts;
|
||||
pkt->dts = pts;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user