Я пытаюсь использовать API FFMPEG для декодирования в буфер, определенный клиентской программой, следуя советам в этом вопросе , но используя новый шаблон для декодирования вместо устаревшего avcodec_decode_video2
function.
Если мой входной файл имеет формат I-frame, все работает отлично. Я протестировал файл .mov с кодировкой v210 (без сжатия).
Однако, если вход имеет формат long-GoP (я пытаюсь использовать высокий профиль H.264 4: 2: 2 в файл mp4) я получаю следующий приятно психоделический / импрессионистский c результат:
Ясно что-то движение-вектор что происходит здесь!
И если я позволю FFMPEG управлять своими собственными буферами с вводом H.264 с помощью , а не , переопределяя AVCodecContext::get_buffer2
, я могу сделать копию из полученного кадра в желаемый целевой буфер и получите хорошие результаты.
Вот мой метод декодера, _frame
и _codecCtx
являются объектными элементами типа AVFrame*
и AVCodecContext*
соответственно. Они получают allo c d и init'd в конструкторе.
virtual const DecodeResult decode(const rv::sz_t toggle) override {
_toggle = toggle & 1;
using Flags_e = DecodeResultFlags_e;
DecodeResult ans(Flags_e::kNoResult);
AVPacket pkt; // holds compressed data
::av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
int ret;
// read the compressed frame to decode
_err = av_read_frame(_fmtCtx, &pkt);
if (_err < 0) {
if (_err == AVERROR_EOF) {
ans.set(Flags_e::kEndOfFile);
_err = 0; // we can safely ignore EOF errors
return ans;
} else {
baleOnFail(__PRETTY_FUNCTION__);
}
}
// send (compressed) packets to the decoder until it produces an uncompressed frame
do {
// sender
_err = ::avcodec_send_packet(_codecCtx, &pkt);
if (_err < 0) {
if (_err == AVERROR_EOF) {
_err = 0; // EOFs are ok
ans.set(Flags_e::kEndOfFile);
break;
} else {
baleOnFail(__PRETTY_FUNCTION__);
}
}
// receiver
ret = ::avcodec_receive_frame (_codecCtx, _frame);
if (ret == AVERROR(EAGAIN)) {
continue;
} else if (ret == AVERROR_EOF) {
ans.set(Flags_e::kEndOfFile);
break;
} else if (ret < 0) {
_err = ret;
baleOnFail(__PRETTY_FUNCTION__);
} else {
ans.set(Flags_e::kGotFrame);
}
av_packet_unref (&pkt);
} while (!ans.test(Flags_e::kGotFrame));
//packFrame(); <-- used to copy to client image
return ans;
}
А вот мое переопределение для get_buffer2
int getVideoBuffer(struct AVCodecContext* ctx, AVFrame* frm) {
// ensure frame pointers are all null
if (frm->data[0] || frm->data[1] || frm->data[2] || frm->data[3]){
::strncpy (_errMsg, "non-null frame data pointer detected.", AV_ERROR_MAX_STRING_SIZE);
return -1;
}
// get format descriptor, ensure it's valid.
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(static_cast<AVPixelFormat>(frm->format));
if (!desc) {
::strncpy (_errMsg, "Pixel format descriptor not available.", AV_ERROR_MAX_STRING_SIZE);
return AVERROR(EINVAL);
}
// for Video, extended data must point to the same place as data.
frm->extended_data = frm->data;
// set the data pointers to point at the Image data.
int chan = 0;
IMG* img = _imgs[_toggle];
// initialize active channels
for (; chan < 3; ++chan) {
frm->buf[chan] = av_buffer_create (
static_cast<uint8_t*>(img->begin(chan)),
rv::unsigned_cast<int>(img->size(chan)),
Player::freeBufferCallback, // callback does nothing
reinterpret_cast<void*>(this),
0 // i.e. AV_BUFFER_FLAG_READONLY is not set
);
frm->linesize[chan] = rv::unsigned_cast<int>(img->stride(chan));
frm->data[chan] = frm->buf[chan]->data;
}
// zero out inactive channels
for (; chan < AV_NUM_DATA_POINTERS; ++chan) {
frm->data[chan] = NULL;
frm->linesize[chan] = 0;
}
return 0;
}
Я могу предположить, что код c Мне нужно хранить опорные кадры в памяти, и поэтому я не очень удивлен, что это не работает, но я не смог выяснить, как заставить его доставлять чистые декодированные кадры в память клиента. Я думал, что AVFrame::key_frame
было бы подсказкой, но, наблюдая за его поведением в gdb, он не предоставляет полезного триггера для того, когда выделять AVFrame::buf
s из пула буферов и когда их можно инициализировать так, чтобы они указывали на память клиента.
Благодарен за любую помощь!