Привет, ЛОР, есть задача сделать несколько функций, которые будут нарезать и склеивать входные видеофайлы с использованием libavcodec и libavformat. Почитал примеры в исходниках ffmpeg, все смотрелось довольно таки просто, написал программку по образу и подобию, вроде даже открывает, декодирует, кодирует и сбрасывает пакеты в выходной файл, но напоролся на такие подводные камни:
1) при попытке поперематывать выходное видео получаю битую картинку вплоть до следующего ключевого кадра, mplayer сыплет такими ворнингами [mpeg4 @ 0x7fcd7bf12b80]warning: first frame is no keyframe.
2) если пытаться кодировать с помощью libx264, то ловлю для каждого кадра такие ворнинги: [libx264 @ 0x167f1c0] non-strictly-monotonic PTS а для первых 50 кадров до кучи еще и [mp4 @ 0x16821c0] pts < dts in stream 0 Выходная статистика битая, например [libx264 @ 0x167f1c0] kb/s:inf
3) получаю на выходе какой то совсем странный битрейт Выставил: 10Mbps CODEC_ID_H264: 723kbps CODEC_ID_MPEG4: 1142kbps
Есть серьезное подозрение, что у меня плохо с матчастью по части форматов и этих самых волшебных PTS и DTS. Буду признателен, если кто нибудь сможет помочь советом.
Исходник программки:
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
AVFormatContext *in_ctx;
AVFormatContext *out_ctx;
AVCodecContext *in_codec_ctx;
AVCodecContext *out_codec_ctx;
AVCodec *in_codec;
AVCodec *out_codec;
AVStream *video_st;
int videoStream = -1;
int open_input_file(char *filename) {
// Open video file
if(av_open_input_file(&in_ctx, filename, NULL, 0, NULL) != 0)
return -1; // Couldn't open file
// Retrieve stream information
if(av_find_stream_info(in_ctx) < 0)
return -1; // Couldn't find stream information
// Find the first video stream
int i;
for(i = 0; i < in_ctx->nb_streams; i++)
if(in_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
break;
}
if(videoStream == -1)
return -1; // Didn't find a video stream
// Get a pointer to the codec context for the video stream
in_codec_ctx = in_ctx->streams[videoStream]->codec;
// Find the decoder for the video stream
in_codec = avcodec_find_decoder(in_codec_ctx->codec_id);
if(in_codec == NULL) {
fprintf(stderr, "Unsupported input codec!\n");
return -1; // Codec not found
}
// Open codec
if(avcodec_open(in_codec_ctx, in_codec) < 0)
return -1; // Could not open codec
return 0;
}
int create_out_file(char *filename) {
// Allocate output format context
out_ctx = avformat_alloc_context();
out_ctx->oformat = av_guess_format(NULL, filename, NULL);
if (out_ctx->oformat == NULL)
{
fprintf(stderr, "Could not guess output format\n");
exit(1);
}
snprintf(out_ctx->filename, sizeof(out_ctx->filename), "%s", filename);
video_st = av_new_stream(out_ctx, 0);
if (!video_st) {
fprintf(stderr, "Could not alloc stream\n");
exit(1);
}
// Set encoding options
out_codec_ctx = video_st->codec;
out_codec_ctx->codec_id = CODEC_ID_H264;
out_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
out_codec_ctx->bit_rate = 10000000;
//out_codec_ctx->crf = 10;
out_codec_ctx->width = in_codec_ctx->width;
out_codec_ctx->height = in_codec_ctx->height;
out_codec_ctx->time_base.den = in_codec_ctx->time_base.den;
out_codec_ctx->time_base.num = in_codec_ctx->time_base.num;
out_codec_ctx->gop_size = 30;
out_codec_ctx->pix_fmt = PIX_FMT_YUV420P;
out_codec_ctx->thread_count = 4;
if(out_ctx->oformat->flags & AVFMT_GLOBALHEADER)
out_codec_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
// Find the encoder for the video stream
out_codec = avcodec_find_encoder(out_codec_ctx->codec_id);
if(out_codec == NULL) {
fprintf(stderr, "Unsupported output codec!\n");
return -1; // Codec not found
}
// Open codec
if(avcodec_open(out_codec_ctx, out_codec) < 0)
return -1; // Could not open codec
avio_open(&out_ctx->pb, filename, AVIO_FLAG_WRITE);
av_write_header(out_ctx);
return 0;
}
int main(int argc, char **argv) {
AVFrame *picture;
int picture_size;
AVFrame *tmp_picture;
int out_size;
int frame_count = 0;
int frame_finished;
AVPacket packet;
uint8_t *buffer;
struct SwsContext *img_convert_ctx;
int retval;
av_register_all();
if (open_input_file(argv[1]))
exit(1);
if (create_out_file(argv[2]))
exit(1);
av_dump_format(in_ctx, 0, argv[1], 0);
av_dump_format(out_ctx, 0, argv[2], 1);
//Allocate frame for decoding
tmp_picture = avcodec_alloc_frame();
//Allocate frame fro encoding
picture = avcodec_alloc_frame();
picture_size = avpicture_get_size(out_codec_ctx->pix_fmt, out_codec_ctx->width, out_codec_ctx->height);
buffer=(uint8_t *)av_malloc(picture_size * sizeof(uint8_t));
avpicture_fill((AVPicture *)picture, buffer, out_codec_ctx->pix_fmt, out_codec_ctx->width, out_codec_ctx->height);
//Crate scaler context
img_convert_ctx = sws_getContext(in_codec_ctx->width, in_codec_ctx->height, in_codec_ctx->pix_fmt,
out_codec_ctx->width, out_codec_ctx->height, out_codec_ctx->pix_fmt,
SWS_BICUBIC, NULL, NULL, NULL);
if (img_convert_ctx == NULL) {
fprintf(stderr, "Cannot initialize the conversion context\n");
exit(1);
}
av_init_packet(&packet);
while (av_read_frame(in_ctx, &packet) >= 0) {
if(packet.stream_index == videoStream) {
frame_count++;
printf("Encoding frame %d\n", frame_count);
//Decode
retval = avcodec_decode_video2(in_codec_ctx, tmp_picture, &frame_finished, &packet);
if (retval < 0)
fprintf(stderr, "Error decoding frame %d\n", frame_count);
//Scale
retval = sws_scale(img_convert_ctx, (const uint8_t * const*)tmp_picture->data, tmp_picture->linesize,
0, out_codec_ctx->height, picture->data, picture->linesize);
if (retval < 1)
fprintf(stderr, "Error scaling frame %d\n", frame_count);
//Encode
out_size = avcodec_encode_video(out_codec_ctx, buffer, picture_size, picture);
if (out_size < 0) {
fprintf(stderr, "Error encoding frame %d\n", frame_count);
} else {
//Write
if (out_codec_ctx->coded_frame->pts != AV_NOPTS_VALUE)
packet.pts = av_rescale_q(out_codec_ctx->coded_frame->pts, out_codec_ctx->time_base, video_st->time_base);
if (out_codec_ctx->coded_frame->key_frame) {
packet.flags |= AV_PKT_FLAG_KEY;
printf("frame %d is key\n", frame_count);
}
packet.stream_index = video_st->index;
packet.data = buffer;
packet.size = out_size;
av_write_frame(out_ctx, &packet);
}
}
}
av_write_trailer(out_ctx);
av_free_packet(&packet);
av_free(buffer);
av_free(picture);
av_free(tmp_picture);
avcodec_close(in_codec_ctx);
avcodec_close(out_codec_ctx);
av_close_input_file(in_ctx);
avio_close(out_ctx->pb);
return 0;
}