ChinaFFmpeg

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 7652|回复: 2

[Linux] 一个最简单的使用ffmpeg实现视频播放demo[代码实现]

[复制链接]
发表于 2018-5-23 19:37:58 | 显示全部楼层 |阅读模式
主要是介绍原理,然后为了介绍一下ffmpeg解码显示的操作其实并不复杂
1. 首先打开一个framebuffer
2. 打开文件
3. 正常解封装文件
4. 解码文件
5. 将解码的yuv转成rgb
6. 如果需要缩放则缩放操作
7. 将rgb铺到framebuffer

改的ffmpeg的demo:
[AppleScript] 纯文本查看 复制代码
gcc -I. -Isrc/ -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -I/root/multimedia/ffmpeg/compat/atomics/gcc -std=c11 -fomit-frame-pointer -pthread -g -Wdeclaration-after-statement -Wall -Wdisabled-optimization -Wpointer-arith -Wredundant-decls -Wwrite-strings -Wtype-limits -Wundef -Wmissing-prototypes -Wno-pointer-to-int-cast -Wstrict-prototypes -Wempty-body -Wno-parentheses -Wno-switch -Wno-format-zero-length -Wno-pointer-sign -O3 -fno-math-errno -fno-signed-zeros -fno-tree-vectorize -Werror=format-security -Werror=implicit-function-declaration -Werror=missing-prototypes -Werror=return-type -Werror=vla -Wformat -fdiagnostics-color=auto -Wno-maybe-uninitialized  -MMD -MF doc/examples/demuxing_decoding.d -MT doc/examples/demuxing_decoding.o -c -o doc/examples/demuxing_decoding.o src/doc/examples/demuxing_decoding.c



代码如下:
[AppleScript] 纯文本查看 复制代码
/*
 * Copyright (c) 2012 Stefano Sabatini
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * @file
 * Demuxing and decoding example.
 *
 * Show how to use the libavformat and libavcodec API to demux and
 * decode audio and video data.
 * @example demuxing_decoding.c
 */

#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <errno.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>

uint8_t  *fb_addr;
unsigned int fb_size;
unsigned int fb_width = 0;
unsigned int fb_line_size = 0;
unsigned int fb_height = 0;
static AVFormatContext *fmt_ctx = NULL;
static AVCodecContext *video_dec_ctx = NULL;
static int width, height;
static enum AVPixelFormat pix_fmt;
static AVStream *video_stream = NULL;
static const char *src_filename = NULL;
static int video_stream_idx = -1;
static AVFrame *frame = NULL;
static AVFrame *out_frame = NULL;
static AVPacket pkt;
static int video_frame_count = 0;

static int test_init_fb(void)
{
        int screen_fbd=0;
        struct fb_fix_screeninfo fb_fix;
        struct fb_var_screeninfo fb_var;
        char *env=NULL;
        unsigned int *color_white;

        if(!(env = getenv("FRAMEBUFFER"))) {
                env = "/dev/fb0";
        }

        screen_fbd = open(env, O_RDWR);

        if(screen_fbd < 0) {
                printf("liuqi Error Opening FrameBuffer Device:%s\r\n", env);
                return 0;
        } else {
                printf("Success Opening FrameBuffer Device:%s\r\n",env);
        }

        if(ioctl(screen_fbd, FBIOGET_FSCREENINFO, &fb_fix) == -1) {
                printf("Error Reading Screen Info FSCREENINFO.\n");
                close(screen_fbd);
                return 0;
        }

        if(ioctl(screen_fbd, FBIOGET_VSCREENINFO, &fb_var) == -1) {
                printf("Error Reading Screen Info VSCREENINFO.\n");
                close(screen_fbd);
                return 0;
        }

        fb_size = fb_var.yres * fb_var.xres * 4;
        fb_addr = (uint8_t *)mmap(NULL, fb_size, PROT_READ|PROT_WRITE,MAP_SHARED, screen_fbd, 0);
        fb_width = fb_var.xres;
        fb_line_size = fb_fix.line_length;
        fb_height = fb_var.yres;
        if (!fb_addr)
                exit(1);
        free(color_white);
        return 0;
}

/* Enable or disable frame reference counting. You are not supposed to support
 * both paths in your application but pick the one most appropriate to your
 * needs. Look for the use of refcount in this example to see what are the
 * differences of API usage between them. */
static int refcount = 0;
static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height)
{
    AVFrame *picture;
    int ret;

    picture = av_frame_alloc();
    if (!picture)
        return NULL;

    picture->format = pix_fmt;
    picture->width  = width;
    picture->height = height;

    /* allocate the buffers for the frame data */
    ret = av_frame_get_buffer(picture, 4);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate frame data.\n");
        exit(1);
    }

    return picture;
}

static int decode_packet(int *got_frame, int cached)
{
    int ret = 0;
    int decoded = pkt.size;

    *got_frame = 0;

    if (pkt.stream_index == video_stream_idx) {
            /* decode video frame */
            ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt);
            if (ret < 0) {
                    fprintf(stderr, "Error decoding video frame (%s)\n", av_err2str(ret));
                    return ret;
            }

            if (*got_frame) {

                    if (frame->width != width || frame->height != height ||
                                    frame->format != pix_fmt) {
                            /* To handle this change, one could call av_image_alloc again and
                             * decode the following frames into another rawvideo file. */
                            fprintf(stderr, "Error: Width, height and pixel format have to be "
                                            "constant in a rawvideo file, but the width, height or "
                                            "pixel format of the input video changed:\n"
                                            "old: width = %d, height = %d, format = %s\n"
                                            "new: width = %d, height = %d, format = %s\n",
                                            width, height, av_get_pix_fmt_name(pix_fmt),
                                            frame->width, frame->height,
                                            av_get_pix_fmt_name(frame->format));
                            return -1;
                    }


                    struct SwsContext *sws_ctx;
                    sws_ctx = sws_getContext(width, height, pix_fmt,
                                    tmp_frame->width, tmp_frame->height, AV_PIX_FMT_BGRA,
                                    SWS_BICUBIC, NULL, NULL, NULL);
                    sws_scale(sws_ctx, (const uint8_t * const *) frame->data,
                                    frame->linesize,
                                    0,
                                    height,
                                    tmp_frame->data,
                                    tmp_frame->linesize);
                    sws_freeContext(sws_ctx);

                    usleep(30000);
                    unsigned char *p = tmp_frame->data[0];
                    unsigned char *a = fb_addr;
                    int i = 0;
                    for (i = 0; i < tmp_frame->height; i++) {
                            memcpy(a, p, tmp_frame->linesize[0]);
                            a += fb_line_size;
                            p += tmp_frame->linesize[0];
                    }

            }
    }

    /* If we use frame reference counting, we own the data and need
     * to de-reference it when we don't use it anymore */
    if (*got_frame && refcount)
        av_frame_unref(frame);

    return decoded;
}

static int open_codec_context(int *stream_idx, AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type)
{
    int ret, stream_index;
    AVStream *st;
    AVCodec *dec = NULL;
    AVDictionary *opts = NULL;

    ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
    if (ret < 0) {
        fprintf(stderr, "Could not find %s stream in input file '%s'\n",
                av_get_media_type_string(type), src_filename);
        return ret;
    } else {
        stream_index = ret;
        st = fmt_ctx->streams[stream_index];

        /* find decoder for the stream */
        dec = avcodec_find_decoder(st->codecpar->codec_id);
        if (!dec) {
            fprintf(stderr, "Failed to find %s codec\n",
                    av_get_media_type_string(type));
            return AVERROR(EINVAL);
        }

        /* Allocate a codec context for the decoder */
        *dec_ctx = avcodec_alloc_context3(dec);
        if (!*dec_ctx) {
            fprintf(stderr, "Failed to allocate the %s codec context\n",
                    av_get_media_type_string(type));
            return AVERROR(ENOMEM);
        }

        /* Copy codec parameters from input stream to output codec context */
        if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {
            fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",
                    av_get_media_type_string(type));
            return ret;
        }

        /* Init the decoders, with or without reference counting */
        av_dict_set(&opts, "refcounted_frames", refcount ? "1" : "0", 0);
        if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {
            fprintf(stderr, "Failed to open %s codec\n",
                    av_get_media_type_string(type));
            return ret;
        }
        *stream_idx = stream_index;
    }

    return 0;
}

int main (int argc, char **argv)
{
    int ret = 0, got_frame;

        test_init_fb();
    if (argc != 2 && argc != 3) {
        fprintf(stderr, "usage: %s [-refcount] input_file video_output_file audio_output_file\n"
                "API example program to show how to read frames from an input file.\n"
                "This program reads frames from a file, decodes them, and writes decoded\n"
                "video frames to a rawvideo file named video_output_file, and decoded\n"
                "audio frames to a bbs.chinaffmpeg.com-孙悟空 file named audio_output_file.\n\n"
                "If the -refcount option is specified, the program use the\n"
                "reference counting frame system which allows keeping a copy of\n"
                "the data for longer than one decode call.\n"
                "\n", argv[0]);
        exit(1);
    }
    if (argc == 5 && !strcmp(argv[1], "-refcount")) {
        refcount = 1;
        argv++;
    }
    src_filename = argv[1];

    /* open input file, and allocate format context */
    if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {
        fprintf(stderr, "Could not open source file %s\n", src_filename);
        exit(1);
    }

    /* retrieve stream information */
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
        fprintf(stderr, "Could not find stream information\n");
        exit(1);
    }

    if (open_codec_context(&video_stream_idx, &video_dec_ctx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
        video_stream = fmt_ctx->streams[video_stream_idx];
        /* allocate image where the decoded image will be put */
        width = video_dec_ctx->width;
        height = video_dec_ctx->height;
        pix_fmt = video_dec_ctx->pix_fmt;
        out_frame = alloc_picture(AV_PIX_FMT_BGRA, 1280, 720);
    }

    /* dump input information to stderr */
    av_dump_format(fmt_ctx, 0, src_filename, 0);

    if (!video_stream) {
        fprintf(stderr, "Could not find audio or video stream in the input, aborting\n");
        ret = 1;
        goto end;
    }

    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate frame\n");
        ret = AVERROR(ENOMEM);
        goto end;
    }

    /* initialize packet, set data to NULL, let the demuxer fill it */
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;

    /* read frames from the file */
    while (av_read_frame(fmt_ctx, &pkt) >= 0) {
        AVPacket orig_pkt = pkt;
        do {
            ret = decode_packet(&got_frame, 0);
            if (ret < 0)
                break;
            pkt.data += ret;
            pkt.size -= ret;
        } while (pkt.size > 0);
        av_packet_unref(&orig_pkt);
    }

    /* flush cached frames */
    pkt.data = NULL;
    pkt.size = 0;
    do {
        decode_packet(&got_frame, 1);
    } while (got_frame);

    printf("Demuxing succeeded.\n");

end:
    avcodec_free_context(&video_dec_ctx);
    avformat_close_input(&fmt_ctx);
    av_frame_free(&frame);

    return ret < 0;
}



代码搞定之后,编译之后,首先要设置Linux的环境,确定是可以使用framebuffer设备:
代码如下:
[C] 纯文本查看 复制代码
#include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <linux/fb.h>
 int main(int argc, char *argv[])
 {
         int screen_fbd=0;
         struct fb_fix_screeninfo fb_fix;
         struct fb_var_screeninfo fb_var;
         char *env=NULL;

         if(!(env = getenv("FRAMEBUFFER")))
         {
                 env = "/dev/fb0";
         }

         screen_fbd = open(env, O_RDWR);

         if(screen_fbd < 0)
         {
                printf("Error Opening FrameBuffer Device:%s\r\n", env);
                return 0;
         }
         else
         {
                printf("Success Opening FrameBuffer Device:%s\r\n",env);
         }

         if(ioctl(screen_fbd, FBIOGET_FSCREENINFO, &fb_fix) == -1)
         {
                 printf("Error Reading Screen Info FSCREENINFO.\n");

                 close(screen_fbd);

                 return 0;
         }

         printf("fb_fix.id=\"%s\"\r\n",fb_fix.id);
         printf("fb_fix.smem_start=%#x\r\n",fb_fix.smem_start);
         printf("fb_fix.mem_len=%d\r\n",fb_fix.smem_len);
         printf("fb_fix.type=%d\r\n",fb_fix.type);
         printf("fb_fix.type_aux=%d\r\n",fb_fix.type_aux);
         printf("fb_fix.visual=%d\r\n",fb_fix.visual);
         printf("fb_fix.line_length=%d\r\n",fb_fix.line_length);
         printf("fb_fix.mmio_start=%#x\r\n",fb_fix.mmio_start);
         printf("fb_fix.mmio_len=%#x\r\n",fb_fix.mmio_len);
         printf("fb_fix.accel=%d\r\n",fb_fix.accel);
printf("fb_fix.accel=%s\r\n", "bbs.chinaffmpeg.com 孙悟空");
printf("fb_fix.reserved[0]=%d\r\n",fb_fix.reserved[0]);
         printf("fb_fix.reserved[1]=%d\r\n",fb_fix.reserved[1]);
         printf("fb_fix.reserved[2]=%d\r\n",fb_fix.reserved[2]);
         if(ioctl(screen_fbd, FBIOGET_VSCREENINFO, &fb_var) == -1)
         {
                 printf("Error Reading Screen Info VSCREENINFO.\n");

                 close(screen_fbd);

                 return 0;
         }
         printf("fb_var.xres=%d\r\n",fb_var.xres);
         printf("fb_var.yres=%d\r\n",fb_var.yres);
         printf("fb_var.xres_virtual=%d\r\n",fb_var.xres_virtual);
         printf("fb_var.yres_virtual=%d\r\n",fb_var.yres_virtual);
         printf("fb_var.xoffset=%d\r\n",fb_var.xoffset);
         printf("fb_var.yoffset=%d\r\n",fb_var.yoffset);
         printf("fb_var.bits_per_pixel=%d\r\n",fb_var.bits_per_pixel);
         printf("fb_var.grayscale=%d\r\n",fb_var.grayscale);
         printf("fb_var.red=%#x\r\n",fb_var.red);
         printf("fb_var.green=%#x\r\n",fb_var.green);
         printf("fb_var.blue=%#x\r\n",fb_var.blue);
         printf("fb_var.transp=%#x\r\n",fb_var.transp);
        printf("fb_var.nonstd=%d\r\n",fb_var.nonstd);
         printf("fb_var.activate=%d\r\n",fb_var.activate);
         printf("fb_var.height=%d\r\n",fb_var.height);
         printf("fb_var.width=%d\r\n",fb_var.width);
         printf("fb_var.accel_flags=%d\r\n",fb_var.accel_flags);
         printf("fb_var.pixclock=%d\r\n",fb_var.pixclock);
         printf("fb_var.left_margin=%d\r\n",fb_var.left_margin);
         printf("fb_var.right_margin=%d\r\n",fb_var.right_margin);
         printf("fb_var.upper_margin=%d\r\n",fb_var.upper_margin);
         printf("fb_var.lower_margin=%d\r\n",fb_var.lower_margin);
         printf("fb_var.hsync_len=%d\r\n",fb_var.hsync_len);
         printf("fb_var.vsync_len=%d\r\n",fb_var.vsync_len);
         printf("fb_var.sync=%d\r\n",fb_var.sync);
         printf("fb_var.vmode=%d\r\n",fb_var.vmode);
         printf("fb_var.rotate=%d\r\n",fb_var.rotate);
         printf("fb_var.reserved[0]=%d\r\n",fb_var.reserved[0]);
         printf("fb_var.reserved[1]=%d\r\n",fb_var.reserved[1]);
         printf("fb_var.reserved[2]=%d\r\n",fb_var.reserved[2]);
         printf("fb_var.reserved[3]=%d\r\n",fb_var.reserved[3]);
         printf("fb_var.reserved[4]=%d\r\n",fb_var.reserved[4]);

         return 0;
 }



跑之后的效果如下图:


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

发表于 2018-5-23 19:42:25 | 显示全部楼层
回复

使用道具 举报

发表于 2021-1-11 16:34:24 | 显示全部楼层
希望大师兄多发表这样的文章
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|Archiver|ChinaFFmpeg

GMT+8, 2024-12-27 04:46 , Processed in 0.067633 second(s), 17 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表