c - H264编解码器编码、解码和写入文件
问题描述
我尝试使用 ffmpeg 和 h264 编解码器实时翻译视频。但是在解码编码帧的状态下,我得到了一些“坏”的图像。初始化编码器和解码器:
VCSession *vc_new_x264(Logger *log, ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data,
VCSession *vc)
{
// ------ ffmpeg encoder ------
AVCodec *codec2 = NULL;
vc->h264_encoder_ctx = NULL;//AVCodecContext type
codec2 = NULL;
avcodec_register_all();
codec2 = avcodec_find_encoder(AV_CODEC_ID_H264);
if (codec2 == NULL)
{
LOGGER_WARNING(log, "h264: not find encoder");
}
vc->h264_encoder_ctx = avcodec_alloc_context3(codec2);
vc->h264_out_pic2 = av_packet_alloc();
vc->h264_encoder_ctx->bit_rate = 10 *1000 * 1000;
vc->h264_encoder_ctx->width = 800;
vc->h264_encoder_ctx->height = 600;
vc->h264_enc_width = vc->h264_encoder_ctx->width;
vc->h264_enc_height = vc->h264_encoder_ctx->height;
vc->h264_encoder_ctx->time_base = (AVRational) {
1, 30
};
vc->h264_encoder_ctx->gop_size = 30;
vc->h264_encoder_ctx->max_b_frames = 1;
vc->h264_encoder_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
av_opt_set(vc->h264_encoder_ctx->priv_data, "preset", "veryfast", 0);
av_opt_set(vc->h264_encoder_ctx->priv_data, "annex_b", "1", 0);
av_opt_set(vc->h264_encoder_ctx->priv_data, "repeat_headers", "1", 0);
av_opt_set(vc->h264_encoder_ctx->priv_data, "tune", "zerolatency", 0);
av_opt_set_int(vc->h264_encoder_ctx->priv_data, "zerolatency", 1, 0);
vc->h264_encoder_ctx->time_base.num = 1;
vc->h264_encoder_ctx->time_base.den = 1000;
vc->h264_encoder_ctx->framerate = (AVRational) {
1000, 40
};
AVDictionary *opts = NULL;
if (avcodec_open2(vc->h264_encoder_ctx, codec2, &opts) < 0) {
LOGGER_ERROR(log, "could not open codec H264 on encoder");
}
av_dict_free(&opts);
AVCodec *codec = NULL;
vc->h264_decoder_ctx = NULL;// AVCodecContext - type
codec = NULL;
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec) {
LOGGER_WARNING(log, "codec not found H264 on decoder");
}
vc->h264_decoder_ctx = avcodec_alloc_context3(codec);
if (codec->capabilities & AV_CODEC_CAP_TRUNCATED) {
vc->h264_decoder_ctx->flags |= AV_CODEC_FLAG_TRUNCATED; /* we do not send complete frames */
}
if (codec->capabilities & AV_CODEC_FLAG_LOW_DELAY) {
vc->h264_decoder_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
}
vc->h264_decoder_ctx->flags |= AV_CODEC_FLAG2_SHOW_ALL;
vc->h264_decoder_ctx->refcounted_frames = 0;
vc->h264_decoder_ctx->delay = 0;
vc->h264_decoder_ctx->sw_pix_fmt = AV_PIX_FMT_YUV420P;
av_opt_set_int(vc->h264_decoder_ctx->priv_data, "delay", 0, AV_OPT_SEARCH_CHILDREN);
vc->h264_decoder_ctx->time_base = (AVRational) {
40, 1000
};
vc->h264_decoder_ctx->framerate = (AVRational) {
1000, 40
};
if (avcodec_open2(vc->h264_decoder_ctx, codec, NULL) < 0) {
LOGGER_WARNING(log, "could not open codec H264 on decoder");
}
vc->h264_decoder_ctx->refcounted_frames = 0;
return vc;
}
编码(在这个函数中,我对帧进行编码并用于调试解码并将他保存在文件中):
uint32_t encode_frame_h264_p(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height,
const uint8_t *y,
const uint8_t *u, const uint8_t *v, ToxAVCall *call,
uint64_t *video_frame_record_timestamp,
int vpx_encode_flags,
x264_nal_t **nal,
int *i_frame_size)
{
AVFrame *frame;
int ret;
uint32_t result = 1;
frame = av_frame_alloc();
frame->format = call->video->h264_encoder_ctx->pix_fmt;
frame->width = width;
frame->height = height;
ret = av_frame_get_buffer(frame, 32);
if (ret < 0) {
LOGGER_ERROR(av->m->log, "av_frame_get_buffer:Could not allocate the video frame data");
}
/* make sure the frame data is writable */
ret = av_frame_make_writable(frame);
if (ret < 0) {
LOGGER_ERROR(av->m->log, "av_frame_make_writable:ERROR");
}
frame->pts = (int64_t)(*video_frame_record_timestamp);
// copy YUV frame data into buffers
memcpy(frame->data[0], y, width * height);
memcpy(frame->data[1], u, (width / 2) * (height / 2));
memcpy(frame->data[2], v, (width / 2) * (height / 2));
// encode the frame
ret = avcodec_send_frame(call->video->h264_encoder_ctx, frame);
if (ret < 0) {
LOGGER_ERROR(av->m->log, "Error sending a frame for encoding:ERROR");
}
ret = avcodec_receive_packet(call->video->h264_encoder_ctx, call->video->h264_out_pic2);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
*i_frame_size = 0;
} else if (ret < 0) {
*i_frame_size = 0;
// fprintf(stderr, "Error during encoding\n");
} else {
// Decoded encoded frame and save him to file
saveInFile(call->video->h264_decoder_ctx, frame, call->video->h264_out_pic2, "/home/user/testSave");
// printf("Write packet %3"PRId64" (size=%5d)\n", call->video->h264_out_pic2->pts, call->video->h264_out_pic2->size);
// fwrite(call->video->h264_out_pic2->data, 1, call->video->h264_out_pic2->size, outfile);
global_encoder_delay_counter++;
if (global_encoder_delay_counter > 60) {
global_encoder_delay_counter = 0;
LOGGER_DEBUG(av->m->log, "enc:delay=%ld",
(long int)(frame->pts - (int64_t)call->video->h264_out_pic2->pts)
);
}
*i_frame_size = call->video->h264_out_pic2->size;
*video_frame_record_timestamp = (uint64_t)call->video->h264_out_pic2->pts;
result = 0;
}
av_frame_free(&frame);
return result;
}
解码并保存帧代码:
void saveInFile(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt, const char *filename)
{
if (!pkt)
return;
char buf[1024];
int ret;
static int curNumber = 0;
ret = avcodec_send_packet(dec_ctx, pkt);
if (ret < 0 && ret != AVERROR_EOF)
{
fprintf(stderr, "Error sending a packet for decoding'\n");
if ( ret == AVERROR(EAGAIN))
return;
if (ret == AVERROR(EINVAL))
return;
if (ret == AVERROR(ENOMEM))
return;
}
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) )
return;
if (ret == AVERROR_EOF)
{
return;
}
else if (ret < 0)
{
fprintf(stderr, "Error during decoding\n");
}
printf("saving frame %3d\n", dec_ctx->frame_number);
sprintf(buf, "%s%d", filename, curNumber);
curNumber++;
pgm_save(frame->data[0], frame->linesize[0], frame->width, frame->height, buf);
}
void pgm_save(unsigned char* buf, int wrap, int xsize, int ysize, char *filename)
{
FILE *f;
int i;
f = fopen(filename, "w");
fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);
for (i =0; i < ysize; i++)
fwrite(buf + i* wrap, 1, xsize, f);
fclose(f);
}
解决方案
推荐阅读
- c# - 补高阶函数
- ios - simdJson 如何在iOS程序中使用simdJson?(objective-C)
- python - 如何修改找不到元素的默认等待30秒?
- javascript - 将 SVG 作为 img 元素的 src 加载时会触发 load 事件吗?
- python - 访问 JSON 对象中的嵌套键
- java - “HTTP 状态 500 - 内部服务器错误”通过服务器上传文件
- javascript - Vuex 操作无法正常工作。显示错误“signInWithEmailAndPassword 失败:第一个参数“email”必须是有效字符串”
- php - 如何替换html文件中的内容
- javascript - 如何将锚 href 属性添加到选项对象?
- emacs - 改进从 org 的 babel 源代码生成的格式