使用 libavcodec 将视频解码为 YUV422
using libavcodec to decode video to YUV422
我想编写一个 C++ 应用程序来打开 mp4 文件并将其解码为 yuv422 文件。
我根据 libavcodec 教程编写了一些代码,但找不到将位深度和格式设置为 YUV422 的地方。
这是我写的一些代码
void read_video_stream(AVFormatContext *pFormatContext, AVCodec *pCodec, AVCodecParameters *pCodecParameters,
int video_stream_index)
{
AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec);
std::unique_ptr<AVCodecContext, av_deleter> ctx_guard(pCodecContext);
if (!pCodecContext) {
return;
}
if (avcodec_parameters_to_context(pCodecContext, pCodecParameters) < 0) {
return;
}
// i tried setting it here
if (avcodec_open2(pCodecContext, pCodec, NULL) < 0) {
return;
}
while (true) {
std::unique_ptr<AVPacket, std::function<void(AVPacket*)>> packet{
new AVPacket,
[](AVPacket* p){ av_packet_unref(p); delete p; }};
av_init_packet(packet.get());
int response = av_read_frame(pFormatContext, packet.get());
if (AVERROR_EOF == response) {
std::cout << "EOF\n";
}
else if (response < 0) {
std::cout << "Error " << response;
return;
}
if (packet->stream_index != video_stream_index) {
continue;
}
response = avcodec_send_packet(pCodecContext, packet.get());
if (response < 0) {
std::cout << "Error while sending a packet to the decoder: " << response;
return;
}
while (response >= 0) {
std::shared_ptr<AVFrame> pFrame{ av_frame_alloc(), AVFrameDeleter};
response = avcodec_receive_frame(pCodecContext, pFrame.get());
if (response == AVERROR(EAGAIN)) {
continue;
}
if (response == AVERROR_EOF) {
std::cerr << "got to last frame\n";
return;
}
else if (response < 0) {
std::cerr << "Error while receiving a frame from the decoder: " << response;
return;
}
if (response >= 0) {
// copy line to cyclic buffer
cb.push_back(std::move(pFrame));
}
}
}
}
我的最终目标是将未压缩的数据(需要在 pFrame->data[0-2] 中)发送到网络中的设备。
你能帮我解决这个问题吗
谢谢
评论说明了一切"You don’t decode to a spacific format. You decode to what ever format the stream was encoded to. If you want a different format, you convert (with swacale) after it’s decided."
我会填补空白。大多数电影文件 (mp4) 最初是 YUV420P 格式,所以这就是解码后的格式。但这可能与我称之为解码像素格式不同。获得具有解码像素格式的 AVFrame 后,您可以使用 swscale()
函数将其转换为任何其他像素格式。
像素格式转换(swscale()
)需要两件事:
1) 设置缩放器上下文。在 FFmpeg 中,像素格式转换和缩放是通过相同的函数完成的。保持两种尺寸相同,您将不会缩放,只会进行转换。
您不需要多次执行此操作,除非参数已更改且不再有效:
SwsContext *swsContext = sws_getContext(src_width, src_height, src_pixfmt,
dst_width, dst_height, dst_pixfmt,
SWS_BILINEAR, NULL, NULL, NULL);
示例 src_pixfmt
是 AV_PIX_FMT_YUV420P
例如 dst_pixfmt
是 AV_PIX_FMT_YUV422P
或 AV_PIX_FMT_UYVY422
SWS_BILINEAR
是缩放算法。当还需要缩放时,您可能需要这一点。他们说双线性有利于放大,双三次有利于缩小。我不是这个领域的专家。但据我所知,双线性比许多其他算法运行良好且速度更快。
2) 要进行转换,您需要这样的东西:
AVFrame *dstframe = av_frame_alloc();
if (dstframe == NULL)
{
fprintf(stderr, "Error: av_frame_alloc() failed.\n");
exit(EXIT_FAILURE);
}
dstframe->format = AV_PIX_FMT_UYVY422; /* choose same format set on sws_getContext() */
dstframe->width = srcframe->width; /* must match sizes as on sws_getContext() */
dstframe->height = srcframe->height; /* must match sizes as on sws_getContext() */
int ret = av_frame_get_buffer(dstframe, 32);
if (ret < 0)
{
fprintf(stderr, "Error: could not allocate the video frame data\n");
exit(EXIT_FAILURE);
}
/* do the conversion */
ret = sws_scale(swsContext, /* SwsContext* on step (1) */
srcframe->data, /* srcSlice[] from decoded AVFrame */
srcframe->linesize, /* srcStride[] from decoded AVFrame */
0, /* srcSliceY */
src_height, /* srcSliceH from decoded AVFrame */
dstframe->data, /* dst[] */
dstframe->linesize); /* dstStride[] */
if (ret < 0)
{
/* error handling */
}
转换成功后,您将在 dstframe
上获得所需内容。
在此处查看更多格式、函数和参数的详细信息:https://www.ffmpeg.org/doxygen/trunk/index.html
希望对您有所帮助。
我想编写一个 C++ 应用程序来打开 mp4 文件并将其解码为 yuv422 文件。 我根据 libavcodec 教程编写了一些代码,但找不到将位深度和格式设置为 YUV422 的地方。
这是我写的一些代码
void read_video_stream(AVFormatContext *pFormatContext, AVCodec *pCodec, AVCodecParameters *pCodecParameters,
int video_stream_index)
{
AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec);
std::unique_ptr<AVCodecContext, av_deleter> ctx_guard(pCodecContext);
if (!pCodecContext) {
return;
}
if (avcodec_parameters_to_context(pCodecContext, pCodecParameters) < 0) {
return;
}
// i tried setting it here
if (avcodec_open2(pCodecContext, pCodec, NULL) < 0) {
return;
}
while (true) {
std::unique_ptr<AVPacket, std::function<void(AVPacket*)>> packet{
new AVPacket,
[](AVPacket* p){ av_packet_unref(p); delete p; }};
av_init_packet(packet.get());
int response = av_read_frame(pFormatContext, packet.get());
if (AVERROR_EOF == response) {
std::cout << "EOF\n";
}
else if (response < 0) {
std::cout << "Error " << response;
return;
}
if (packet->stream_index != video_stream_index) {
continue;
}
response = avcodec_send_packet(pCodecContext, packet.get());
if (response < 0) {
std::cout << "Error while sending a packet to the decoder: " << response;
return;
}
while (response >= 0) {
std::shared_ptr<AVFrame> pFrame{ av_frame_alloc(), AVFrameDeleter};
response = avcodec_receive_frame(pCodecContext, pFrame.get());
if (response == AVERROR(EAGAIN)) {
continue;
}
if (response == AVERROR_EOF) {
std::cerr << "got to last frame\n";
return;
}
else if (response < 0) {
std::cerr << "Error while receiving a frame from the decoder: " << response;
return;
}
if (response >= 0) {
// copy line to cyclic buffer
cb.push_back(std::move(pFrame));
}
}
}
}
我的最终目标是将未压缩的数据(需要在 pFrame->data[0-2] 中)发送到网络中的设备。 你能帮我解决这个问题吗 谢谢
评论说明了一切"You don’t decode to a spacific format. You decode to what ever format the stream was encoded to. If you want a different format, you convert (with swacale) after it’s decided."
我会填补空白。大多数电影文件 (mp4) 最初是 YUV420P 格式,所以这就是解码后的格式。但这可能与我称之为解码像素格式不同。获得具有解码像素格式的 AVFrame 后,您可以使用 swscale()
函数将其转换为任何其他像素格式。
像素格式转换(swscale()
)需要两件事:
1) 设置缩放器上下文。在 FFmpeg 中,像素格式转换和缩放是通过相同的函数完成的。保持两种尺寸相同,您将不会缩放,只会进行转换。
您不需要多次执行此操作,除非参数已更改且不再有效:
SwsContext *swsContext = sws_getContext(src_width, src_height, src_pixfmt,
dst_width, dst_height, dst_pixfmt,
SWS_BILINEAR, NULL, NULL, NULL);
示例 src_pixfmt
是 AV_PIX_FMT_YUV420P
例如 dst_pixfmt
是 AV_PIX_FMT_YUV422P
或 AV_PIX_FMT_UYVY422
SWS_BILINEAR
是缩放算法。当还需要缩放时,您可能需要这一点。他们说双线性有利于放大,双三次有利于缩小。我不是这个领域的专家。但据我所知,双线性比许多其他算法运行良好且速度更快。
2) 要进行转换,您需要这样的东西:
AVFrame *dstframe = av_frame_alloc();
if (dstframe == NULL)
{
fprintf(stderr, "Error: av_frame_alloc() failed.\n");
exit(EXIT_FAILURE);
}
dstframe->format = AV_PIX_FMT_UYVY422; /* choose same format set on sws_getContext() */
dstframe->width = srcframe->width; /* must match sizes as on sws_getContext() */
dstframe->height = srcframe->height; /* must match sizes as on sws_getContext() */
int ret = av_frame_get_buffer(dstframe, 32);
if (ret < 0)
{
fprintf(stderr, "Error: could not allocate the video frame data\n");
exit(EXIT_FAILURE);
}
/* do the conversion */
ret = sws_scale(swsContext, /* SwsContext* on step (1) */
srcframe->data, /* srcSlice[] from decoded AVFrame */
srcframe->linesize, /* srcStride[] from decoded AVFrame */
0, /* srcSliceY */
src_height, /* srcSliceH from decoded AVFrame */
dstframe->data, /* dst[] */
dstframe->linesize); /* dstStride[] */
if (ret < 0)
{
/* error handling */
}
转换成功后,您将在 dstframe
上获得所需内容。
在此处查看更多格式、函数和参数的详细信息:https://www.ffmpeg.org/doxygen/trunk/index.html
希望对您有所帮助。