我正在尝试使用libav复制以下ffmpeg命令执行的操作:
ffmpeg -f v4l2 -framerate 25 -video_size 640x480 -i / dev / video 0 -f mpegts-编解码器:v mpeg1video -s 640x480 -b:v 1000k -bf 0 -muxdelay 0.001 http://localhost:8081/supersecret
我设法复制了大部分内容。问题是,当分配“ mpegts”流(第23行)时,所选的编解码器是“ mpeg2video”,但我需要它是“ mpeg1video”。我尝试将codec_id变量强制为“ mpeg1video”(第25行),虽然在图像中出现了很多瑕疵,但它确实起作用了,所以我猜测这不是您的操作方式。在这种情况下,如何正确更改编解码器(即ffmpeg命令的“-codec:v mpeg1video” 部分)?
正在使用的C ++代码:
#include <exception>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/error.h>
#include <libavutil/frame.h>
#include <libavutil/imgutils.h>
}
struct AVStreamer {
AVFormatContext* format_context;
AVStream* stream;
AVCodecContext* codec_context;
AVCodec* codec;
AVFrame* frame;
int64_t next_pts;
void init_format_context(const char* url) {
avformat_alloc_output_context2(&format_context, nullptr, "mpegts", url);
if (format_context == nullptr) throw std::runtime_error("Could not create output context");
format_context->oformat->video_codec = AV_CODEC_ID_MPEG1VIDEO;
}
void init_codec() {
auto codec_id = format_context->oformat->video_codec;
codec = avcodec_find_encoder(codec_id);
if (codec == nullptr) throw std::runtime_error("Could not find encoder");
}
void init_stream() {
stream = avformat_new_stream(format_context, nullptr);
if (stream == nullptr) throw std::runtime_error("Failed to alloc stream");
stream->id = format_context->nb_streams - 1;
}
void init_codec_context() {
codec_context = avcodec_alloc_context3(codec);
if (codec_context == nullptr) throw std::runtime_error("Failed to alloc encoding context");
auto codec_id = format_context->oformat->video_codec;
codec_context->codec_id = codec_id;
codec_context->bit_rate = 400000;
codec_context->width = 640;
codec_context->height = 480;
stream->time_base = AVRational{1, 30};
codec_context->time_base = stream->time_base;
codec_context->gop_size = 30; // one intra frame every gop_size
// codec_context->max_b_frames = 0; // output delayed by max_b_frames
codec_context->pix_fmt = AV_PIX_FMT_YUV420P;
if (codec_context->codec_id == AV_CODEC_ID_MPEG1VIDEO) { codec_context->mb_decision = 2; }
if (format_context->oformat->flags & AVFMT_GLOBALHEADER) {
codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
}
void init_frame() {
frame = av_frame_alloc();
if (frame == nullptr) throw std::runtime_error("Failed to alloc frame");
frame->format = codec_context->pix_fmt;
frame->width = codec_context->width;
frame->height = codec_context->height;
auto status = av_frame_get_buffer(frame, 32);
if (status < 0) { throw std::runtime_error("Could not allocate frame data.\n"); }
}
void open_stream(const char* url) {
int status = avcodec_open2(codec_context, codec, nullptr);
if (status != 0) throw std::runtime_error("Failed to open codec");
// copy the stream parameters to the muxer
status = avcodec_parameters_from_context(stream->codecpar, codec_context);
if (status < 0) throw std::runtime_error("Could not copy the stream parameters");
av_dump_format(format_context, 0, url, 1);
if (!(format_context->oformat->flags & AVFMT_NOFILE)) {
status = avio_open(&format_context->pb, url, AVIO_FLAG_WRITE);
if (status < 0) throw std::runtime_error("Could not open output file");
}
// Write the stream header, if any.
status = avformat_write_header(format_context, nullptr);
if (status < 0) throw std::runtime_error("Error occurred when opening output file");
}
AVStreamer(const char* url) : next_pts(0) {
init_format_context(url);
init_codec();
init_stream();
init_codec_context();
init_frame();
open_stream(url);
}
virtual ~AVStreamer() {
avformat_free_context(format_context);
avcodec_free_context(&codec_context);
av_frame_free(&frame);
}
void send(cv::Mat const& image) {
cv::cvtColor(image, image, CV_BGR2YUV);
cv::Mat planes[3];
cv::split(image, planes);
cv::pyrDown(planes[1], planes[1]);
cv::pyrDown(planes[2], planes[2]);
if (av_frame_make_writable(frame) < 0) {
throw std::runtime_error("Failed to make frame writable");
}
frame->data[0] = planes[0].data;
frame->linesize[0] = planes[0].step;
frame->data[1] = planes[1].data;
frame->linesize[1] = planes[1].step;
frame->data[2] = planes[2].data;
frame->linesize[2] = planes[2].step;
frame->pts = next_pts++;
AVPacket packet;
av_init_packet(&packet);
int status = avcodec_send_frame(codec_context, frame);
if (status < 0) throw std::runtime_error("Send frame failed");
status = avcodec_receive_packet(codec_context, &packet);
if (status == AVERROR(EAGAIN)) { return; }
if (status < 0) throw std::runtime_error("Receive packet failed");
av_packet_rescale_ts(&packet, codec_context->time_base, stream->time_base);
packet.stream_index = stream->index;
av_interleaved_write_frame(format_context, &packet);
}
};
int main(int argc, char** argv) {
av_register_all();
avformat_network_init();
auto url = argc == 2 ? argv[1] : "http://localhost:8081/supersecret";
AVStreamer streamer(url);
cv::VideoCapture video(0);
assert(video.isOpened() && "Failed to open video");
for (;;) {
cv::Mat image;
video >> image;
streamer.send(image);
}
}