我正在尝试将mp4视频文件编码为flv视频。这是我正在做的事情:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#define STREAM_FRAME_RATE 30 /* 30 images/s */
#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */
/**************************************************************/
/* Add an output stream. */
static AVStream *add_stream(AVFormatContext *oc, AVCodec **codec,
enum AVCodecID codec_id) {
AVCodecContext *c;
AVStream *st;
/* find the encoder */
*codec = avcodec_find_encoder(codec_id);
if (!(*codec)) {
fprintf(stderr, "Could not find encoder for '%s'\n",
avcodec_get_name(codec_id));
exit(1);
}
st = avformat_new_stream(oc, *codec);
if (!st) {
fprintf(stderr, "Could not allocate stream\n");
exit(1);
}
st->id = oc->nb_streams - 1;
c = st->codec;
switch ((*codec)->type) {
case AVMEDIA_TYPE_VIDEO:
//avcodec_get_context_defaults3(c, *codec);
c->codec_id = codec_id;
c->bit_rate = 150 * 1000; //2314000;;
//c->rc_max_rate = 150*1000;
//c->rc_buffer_size = 150*1000;
/* Resolution must be a multiple of two. */
c->width = 1280;
c->height = 720;
/* timebase: This is the fundamental unit of time (in seconds) in terms
* of which frame timestamps are represented. For fixed-fps content,
* timebase should be 1/framerate and timestamp increments should be
* identical to 1. */
c->time_base.den = STREAM_FRAME_RATE;
c->time_base.num = 1;
c->gop_size = 12; /* emit one intra frame every twelve frames at most */
c->pix_fmt = STREAM_PIX_FMT;
if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
/* just for testing, we also add B frames */
c->max_b_frames = 2;
}
if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
/* Needed to avoid using macroblocks in which some coeffs overflow.
* This does not happen with normal video, it just happens here as
* the motion of the chroma plane does not match the luma plane. */
c->mb_decision = 2;
}
break;
default:
break;
}
/* Some formats want stream headers to be separate. */
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
return st;
}
/**************************************************************/
/* video output */
static AVFrame *frame;
static AVPicture src_picture, dst_picture;
static int frame_count;
static void open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st) {
int ret;
AVCodecContext *c = st->codec;
/* open the codec */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret));
exit(1);
}
/* allocate and init a re-usable frame */
frame = avcodec_alloc_frame();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
/* Allocate the encoded raw picture. */
ret = avpicture_alloc(&dst_picture, c->pix_fmt, c->width, c->height);
if (ret < 0) {
fprintf(stderr, "Could not allocate picture: %s\n", av_err2str(ret));
exit(1);
}
/* If the output format is not YUV420P, then a temporary YUV420P
* picture is needed too. It is then converted to the required
* output format. */
if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
ret = avpicture_alloc(&src_picture, AV_PIX_FMT_YUV420P, c->width,
c->height);
if (ret < 0) {
fprintf(stderr, "Could not allocate temporary picture: %s\n",
av_err2str(ret));
exit(1);
}
}
/* copy data and linesize picture pointers to frame */
*((AVPicture *) frame) = dst_picture;
}
static void close_video(AVFormatContext *oc, AVStream *st) {
avcodec_close(st->codec);
av_free(src_picture.data[0]);
av_free(dst_picture.data[0]);
av_free(frame);
}
int main(int argc, char *argv[]) {
AVFormatContext *pFormatCtx = NULL;
int i, videoStream;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec;
AVFrame *pFrame;
AVPacket packet;
int frameFinished;
const char *filename;
AVOutputFormat *fmt;
AVFormatContext *oc;
AVStream *video_st;
AVCodec *video_codec;
int ret;
// Register all formats, codecs and network
av_register_all();
avcodec_register_all();
avformat_network_init();
// Open video file
if (avformat_open_input(&pFormatCtx, "input_file.mp4", NULL, NULL) != 0)
return -1; // Couldn't open file
// Retrieve stream information
if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
return -1; // Couldn't find stream information
// Dump information about file onto standard error
av_dump_format(pFormatCtx, 0, "input_file.mp4", 0);
// Find the first video stream
videoStream = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++)
if (pFormatCtx->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
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
// Find the decoder for the video stream
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL) {
fprintf(stderr, "Unsupported codec!\n");
return -1; // Codec not found
}
// Open codec
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
return -1; // Could not open codec
// Allocate video frame
pFrame = avcodec_alloc_frame();
// setup mux
filename = "output_file.flv";
fmt = av_guess_format("flv", filename, NULL);
if (fmt == NULL) {
printf("Could not guess format.\n");
return -1;
}
// allocate the output media context
oc = avformat_alloc_context();
if (oc == NULL) {
printf("could not allocate context.\n");
return -1;
}
oc->oformat = fmt;
// Add the video streams using the default format codecs
// and initialize the codecs.
video_st = NULL;
if (fmt->video_codec != AV_CODEC_ID_NONE) {
video_st = add_stream(oc, &video_codec, fmt->video_codec);
}
// Now that all the parameters are set, we can open the
// video codec and allocate the necessary encode buffers.
if (video_st)
open_video(oc, video_codec, video_st);
/* open the output file, if needed */
if (!(fmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "Could not open '%s': %s\n", filename,
av_err2str(ret));
return 1;
}
}
// dump output format
av_dump_format(oc, 0, filename, 1);
// Write the stream header, if any.
ret = avformat_write_header(oc, NULL);
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file: %s\n",
av_err2str(ret));
return 1;
}
// Read frames, decode, and re-encode
while (av_read_frame(pFormatCtx, &packet) >= 0) {
// Is this a packet from the video stream?
if (packet.stream_index == videoStream) {
// Decode video frame
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
// Did we get a video frame?
if (frameFinished) {
AVFrame* newFrame = avcodec_alloc_frame(); // Initialize a new frame
int size = avpicture_get_size(video_st->codec->pix_fmt,
video_st->codec->width, video_st->codec->height);
uint8_t* picture_buf = av_malloc(size);
avpicture_fill((AVPicture *) newFrame, picture_buf,
video_st->codec->pix_fmt, video_st->codec->width,
video_st->codec->height);
// Copy only the frame content without any other disturbing
av_picture_copy((AVPicture*) newFrame, (AVPicture*) pFrame,
video_st->codec->pix_fmt, video_st->codec->width,
video_st->codec->height);
// encode the image
AVPacket pkt;
int got_output;
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
/* Setting newFrame->pts this way produces the error 'non-strictly-monotonic PTS' */
//if(newFrame->pts != AV_NOPTS_VALUE)
//newFrame->pts = av_rescale_q(newFrame->pts, video_st->time_base, video_st->codec->time_base);
// Setting newFrame->pts this does not produce 'non-strictly-monotonic PTS'
newFrame->pts = frame_count;
ret = avcodec_encode_video2(video_st->codec, &pkt, newFrame, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret));
exit(1);
}
if (got_output) {
if (video_st->codec->coded_frame->key_frame)
pkt.flags |= AV_PKT_FLAG_KEY;
pkt.stream_index = video_st->index;
if (pkt.pts != AV_NOPTS_VALUE)
pkt.pts = av_rescale_q(pkt.pts,
video_st->codec->time_base,
video_st->time_base);
if (pkt.dts != AV_NOPTS_VALUE)
pkt.dts = av_rescale_q(pkt.dts,
video_st->codec->time_base,
video_st->time_base);
// Write the compressed frame to the media file.
ret = av_interleaved_write_frame(oc, &pkt);
} else {
ret = 0;
}
if (ret != 0) {
fprintf(stderr, "Error while writing video frame: %s\n",
av_err2str(ret));
exit(1);
}
fprintf(stderr, "encoded frame #%d\n", frame_count);
frame_count++;
av_free(picture_buf);
av_free_packet(&pkt);
}
}
// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
}
/* Write the trailer, if any. The trailer must be written before you
* close the CodecContexts open when you wrote the header; otherwise
* av_write_trailer() may try to use memory that was freed on
* av_codec_close(). */
av_write_trailer(oc);
/* Close video encoder (codec). */
if (video_st)
close_video(oc, video_st);
// Free the streams.
for (i = 0; i < oc->nb_streams; i++) {
av_freep(&oc->streams[i]->codec);
av_freep(&oc->streams[i]);
}
if (!(fmt->flags & AVFMT_NOFILE))
/* Close the output file. */
avio_close(oc->pb);
/* free the stream */
av_free(oc);
// Free the YUV frame holding decoded frame
av_free(pFrame);
// Close the decoder (codec)
avcodec_close(pCodecCtx);
// Close the input video file
avformat_close_input(&pFormatCtx);
return 0;
}
如果我通过更改将输出格式更改为mp4、mov 或 f4v:
// setup mux (FLV OUTPUT)
filename = "output_file.flv";
fmt = av_guess_format("flv", filename, NULL);
if (fmt == NULL) {
printf("Could not guess format.\n");
return -1;
}
到
// setup mux (MP4 OUTPUT)
filename = "output_file.mp4";
fmt = av_guess_format("mp4", filename, NULL);
if (fmt == NULL) {
printf("Could not guess format.\n");
return -1;
}
然后尊重比特率。这是使用mp4输出运行的输出
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'input_file.mp4':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: isomavc1mp42
creation_time : 2010-02-09 19:11:10
Duration: 00:04:31.80, start: 0.000000, bitrate: 2314 kb/s
Stream #0:0(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s
Metadata:
creation_time : 2010-02-09 19:11:10
handler_name : (C) 2007 Google Inc. v08.13.2007.
Stream #0:1(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 2186 kb/s, 30 fps, 30 tbr, 30k tbn, 60 tbc
Metadata:
creation_time : 2010-02-09 19:11:10
handler_name : (C) 2007 Google Inc. v08.13.2007.
[libx264 @ 02268b80] using cpu capabilities: MMX2 SSE2Fast SSSE3 FastShuffle SSE4.2 AVX
[libx264 @ 02268b80] profile High, level 3.1
[libx264 @ 02268b80] 264 - core 129 r2230 1cffe9f - H.264/MPEG-4 AVC codec - Copyleft 2003-2012 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deb
lock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chro
ma_qp_offset=-2 threads=12 lookahead_threads=2 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1
b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=12 keyint_min=1 scenecut=40 intra_refresh=0 rc_lookahead=12 rc=abr mbtree=1 bitrate=150 ratetol=1.0 qco
mp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to 'output_file.mp4':
Stream #0:0: Video: h264, yuv420p, 1280x720, q=-1--1, 150 kb/s, 90k tbn, 30 tbc
encoded frame #0
encoded frame #1
encoded frame #2
encoded frame #3
.
.
.
encoded frame #8142
encoded frame #8143
encoded frame #8144
encoded frame #8145
[libx264 @ 02174240] frame I:771 Avg QP:47.71 size: 2739
[libx264 @ 02174240] frame P:5083 Avg QP:50.05 size: 833
[libx264 @ 02174240] frame B:2264 Avg QP:48.16 size: 91
[libx264 @ 02174240] consecutive B-frames: 59.6% 6.4% 9.0% 24.9%
[libx264 @ 02174240] mb I I16..4: 34.0% 65.8% 0.2%
[libx264 @ 02174240] mb P I16..4: 6.8% 3.0% 0.0% P16..4: 8.5% 0.4% 1.7% 0.0% 0.0% skip:79.6%
[libx264 @ 02174240] mb B I16..4: 0.0% 0.0% 0.0% B16..8: 1.8% 0.0% 0.0% direct: 0.0% skip:98.2% L0:23.6% L1:76.4% BI: 0.0%
[libx264 @ 02174240] final ratefactor: 57.62
[libx264 @ 02174240] 8x8 transform intra:52.1% inter:98.5%
[libx264 @ 02174240] coded y,uvDC,uvAC intra: 3.1% 14.0% 0.2% inter: 0.1% 0.4% 0.0%
[libx264 @ 02174240] i16 v,h,dc,p: 69% 23% 4% 4%
[libx264 @ 02174240] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 23% 6% 68% 1% 1% 1% 1% 0% 0%
[libx264 @ 02174240] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 41% 18% 18% 4% 4% 5% 3% 3% 3%
[libx264 @ 02174240] i8c dc,h,v,p: 98% 1% 1% 0%
[libx264 @ 02174240] Weighted P-Frames: Y:2.1% UV:1.2%
[libx264 @ 02174240] ref P L0: 72.4% 0.8% 19.1% 7.6% 0.1%
[libx264 @ 02174240] ref B L0: 89.8% 9.1% 1.2%
[libx264 @ 02174240] ref B L1: 96.2% 3.8%
[libx264 @ 02174240] kb/s:193.62
output_file.mp4的最终文件大小约为6.5MB,使用 ffplay,我注意到比特率得到了尊重。当然,质量本身就显示出来了。
这是使用flv输出运行的输出:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'input_file.mp4':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: isomavc1mp42
creation_time : 2010-02-09 19:11:10
Duration: 00:04:31.80, start: 0.000000, bitrate: 2314 kb/s
Stream #0:0(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s
Metadata:
creation_time : 2010-02-09 19:11:10
handler_name : (C) 2007 Google Inc. v08.13.2007.
Stream #0:1(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 2186 kb/s, 30 fps, 30 tbr, 30k tbn, 60 tbc
Metadata:
creation_time : 2010-02-09 19:11:10
handler_name : (C) 2007 Google Inc. v08.13.2007.
Output #0, flv, to 'output_file.flv':
Stream #0:0: Video: flv1, yuv420p, 1280x720, q=2-31, 150 kb/s, 90k tbn, 30 tbc
encoded frame #0
encoded frame #1
encoded frame #2
encoded frame #3
.
.
.
encoded frame #8142
encoded frame #8143
encoded frame #8144
encoded frame #8145
我注意到只要容器是 mp4、mov、f4v 或类似的东西,比特率就会受到尊重。如果我尝试使用 flv、wmv 或其他不同的容器,则比特率不会受到尊重。此外,为 wmv 自动设置的比特率与 flv 不同。我不确定我做错了什么。我怀疑它与pts有关,但是,我找不到解决方案:(。