ffmpeg键盘命令响应程序详解

本文详细介绍了Linux系统中终端交互的基本原理,包括标准输入输出、termios结构的应用、以及如何通过编程实现对终端特性的定制化配置。同时,通过一个ffmpeg项目的示例,展示了如何在实际应用中响应键盘输入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、对终端进行读写
当一个程序在命令提示符中被调用时,
shell负责将标准输入和标准输出流连接到你的程序,
实现程序与用户间的交互。

1. 标准模式和非标准模式
在默认情况下,
只有用户按下回车键后,程序才能读到终端的输入。
在大多数情况下,这样做是有益的,
因为它允许用户使用退格键和删除键来纠正输入中的错误,
用户只在对自己在屏幕上看到的内容满意时,
才会按下回车键把键入的数据传递给程序。
这种方式称为规范模式或标准模式。

在这种方式下,
Linux会暂存用户输入的内容,
直到用户按下回车键,
然后将用户选择的字符及紧随其后的回车符一起传递给程序。


2. 与终端进行对话
Linux提供了一个特殊设备/dev/tty,
该设备始终是指向当前终端或当前登录会话。

二、termios结构
termios是在POSIX规范中定义的标准接口,
通过设置termios类型的数据结构中的值和使用一小组函数调用,
就可以对终端接口进行控制。
termios数据结构和相关函数调用都定义在头文件termios.h中。

可以被调整来影响终端的值按照不同的模式被分成如下几组:
输入模式
输出模式
控制模式
本地模式
特殊控制字符

最小的termios结构的典型定义如下:

#include <termios.h>
struct termios {
  tcflag_t c_iflag;
  tcflag_t c_oflag;
  tcflag_t c_cflag;
  tcflag_t c_lflag;
  cc_t c_cc[NCCS];
};

结构成员名称与上面列出的5种参数类型相对应。

可以调用函数tcgetattr来初始化与一个终端对应的termios结构,
该函数的原型如下:
#include <termios.h>

int tcgetattr(int fd, struct termios *termios_p);

这个函数调用把当前终端接口变量的值写入termios_p参数指向的结构。

如果这些值其后被修改了,
可以通过调用函数tcsetattr来重新配置终端接口,
该函数的原型如下:
#include <termios.h>

int tcsetattr(int fd, int actions, const struct termios *termios_p);
参数actions控制修改方式,
共有3种修改方式:
  TCSANOW:   立刻对值进行修改
  TCSADRAIN: 等当前的输出完成后再对值进行修改
  FCSAFLUSH: 等当前的输出完成后再对值进行修改,
             但丢弃还未从read调用返回的当前可用的任何输入。

具体的模式的宏定义可以参见man帮助手册或相关文档。

1. 输入模式
termios结构中的c_iflag成员的标志对它们进行控制。
可用于c_iflag成员的宏如下所示:
❑ BRKINT: Generate an interrupt when a break condition (loss of connection) 
          is detected on the line
❑ IGNBRK: Ignore break conditions on the line
❑ ICRNL:  Convert a received carriage return to a newline
❑ IGNCR:  Ignore received carriage returns
❑ INLCR:  Convert received newlines to carriage returns
❑ IGNPAR: Ignore characters with parity errors
❑ INPCK:  Perform parity checking on received characters
❑ PARMRK: Mark parity errors
❑ ISTRIP: Strip (set to seven bits) all incoming characters
❑ IXOFF:  Enable software flow control on input
❑ IXON:   Enable software flow control on output

2. 输出模式
c_oflag成员的宏如下所示:
❑ OPOST:  Turn on output processing
❑ ONLCR:  Convert any output newline to a carriage return/line feed pair
❑ OCRNL:  Convert any output carriage return to a newline
❑ ONOCR:  No carriage return output in column 0
❑ ONLRET: A newline also does a carriage return
❑ OFILL:  Send fill characters to provide delays
❑ OFDEL:  Use DEL as a fill character, rather than NULL
❑ NLDLY:  Newline delay selection
❑ CRDLY:  Carriage return delay selection
❑ TABDLY: Tab delay selection
❑ BSDLY:  Backspace delay selection
❑ VTDLY:  Vertical tab delay selection
❑ FFDLY:  Form feed delay selection

3. 控制模式
c_cflag成员的宏如下所示:
❑ CLOCAL: Ignore any modem status lines.
❑ CREAD:  Enable the receipt of characters.
❑ CS5:    Use five bits in sent or received characters.
❑ CS6:    Use six bits in sent or received characters.
❑ CS7:    Use seven bits in sent or received characters.
❑ CS8:    Use eight bits in sent or received characters.
❑ CSTOPB: Use two stop bits per character, rather than one.
❑ HUPCL:  Hang up modem on close.
❑ PARENB: Enable parity generation and detection.
❑ PARODD: Use odd parity rather than even parity.

4. 本地模式
c_lflag成员的宏如下所示:
❑ ECHO:   Enable local echoing of input characters
❑ ECHOE:  Perform a Backspace, Space, Backspace combination on receiving ERASE
❑ ECHOK:  Perform erase line on the KILL character
❑ ECHONL: Echo newline characters
❑ ICANON: Enable canonical input processing (see the text following this list)
❑ IEXTEN: Enable implementation-specific functions
❑ ISIG:   Enable signals
❑ NOFLSH: Disable flush on queue
❑ TOSTOP: Send background processes a signal on write attempts

5. 特殊控制字符
c_cc数组:
标准模式中可使用的数组下标:
❑ VEOF:   EOF character
❑ VEOL:   EOL character
❑ VERASE: ERASE character
❑ VINTR:  INTR character
❑ VKILL:  KILL character
❑ VQUIT:  QUIT character
❑ VSUSP:  SUSP character
❑ VSTART: START character
❑ VSTOP:  STOP character
非标准模式中可使用的数组下标:
❑ VINTR:  INTR character
❑ VMIN:   MIN value
❑ VQUIT:  QUIT character
❑ VSUSP:  SUSP character
❑ VTIME:  TIME value
❑ VSTART: START character
❑ VSTOP:  STOP character

6. 终端速度
函数原型如下:
#include <termios.h>

speed_t cfgetispeed(const struct termios *);
speed_t cfgetospeed(const struct termios *);
int cfsetispeed(struct termios *, speed_t speed);
int cfsetospeed(struct termios *, speed_t speed);
7. 其他函数

#include <termios.h>

int tcdrain(int fd);
int tcflow(int fd, int flowtype);
int tcflush(int fd, int in_out_selector);

三、ffmpeg对键盘响应程序的分析
main.c
/*
 * \File
 * main.c
 * \Descript
 * Taken from ffmpeg,
 * implement for signal response function
 * \Author
 * Neo Yan
 */

#include <stdio.h>
#include <stdlib.h>

#include <signal.h>
#include <limits.h>
#include <unistd.h>

#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <termios.h>

static struct termios oldtty;
static int q_pressed = 0;
static int verbose = 1;
static int using_stdin = 0;
static int run_as_daemon = 0;


static volatile int received_sigterm = 0;

static void term_init(void);
static void term_exit(void);
static void sigterm_handler(int sig);
static int read_key(void);


/*
 * \Func
 * main
 * \Descript
 *
 */
int main(int argc, char **argv)
{
  int index = 0;
  int key;

  if (!using_stdin)
  {
    if (verbose >= 0)
      printf("Perss [q] to stop, [?] for help\n");
  }

  term_init();
  for ( ; received_sigterm == 0; )
  {
    /* if 'q' pressed, exits */
    if (!using_stdin)
    {
      if (q_pressed)
        break;

      /* read_key() returns 0 on EOF */
      key = read_key();
      if (key == 'q')
      {
        printf("quit\n");
        break;
      }
      if (key == '+') 
      {
        verbose++;
        printf("verbose = %d\n", verbose);
      }
      if (key == '-')
      { 
        verbose--;
        printf("verbose = %d\n", verbose);
      }
      if (key == '?')
      {
        printf("key function\n" 
               "? show this help\n" 
               "+ increase verbosity\n" 
               "- decrease verbosity\n" 
               "q quit\n"
               );
       }
    }

    printf("index = %d\n", index++);
    sleep(1);
  }
  return 0;
}


static void term_init(void)
{
    if (!run_as_daemon)
    {
        struct termios tty;

        tcgetattr(0, &tty);
        oldtty = tty;
        atexit(term_exit);

        tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
                    |INLCR|IGNCR|ICRNL|IXON);
        tty.c_oflag |= OPOST;
        tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
        tty.c_cflag &= ~(CSIZE|PARENB);
        tty.c_cflag |= CS8;
        tty.c_cc[VMIN] = 1;
        tty.c_cc[VTIME] = 0;

        tcsetattr (0, TCSANOW, &tty);

                /* Quit (POSIX). */
        signal(SIGQUIT, sigterm_handler); 
    }

    signal(SIGINT , sigterm_handler);
    signal(SIGTERM, sigterm_handler); 
}


static void term_exit(void)
{
    printf("%s\n", "TERMINATION");

    if (!run_as_daemon)
        tcsetattr(0, TCSANOW, &oldtty);
}

static void sigterm_handler(int sig)
{
    received_sigterm = sig;
    q_pressed++;
    term_exit();
}

/* 
 * \Func
 * read_key
 * \Descript
 * read a key without blocking
 */
static int read_key(void)
{
    int n = 1;
    unsigned char ch;
    struct timeval tv;
    fd_set rfds;

    if (run_as_daemon)
     return -1;

    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    tv.tv_sec = 0;
    tv.tv_usec = 0;
    n = select(1, &rfds, NULL, NULL, &tv);

    if (n > 0)
    {
        n = read(0, &ch, 1);

        if (n == 1)
         return ch;

        return n;
    }

    return -1;
}
makefile
OBJECTS = main.o
CC = gcc
CFLAG = -g

ffmpeg_signal : $(OBJECTS)
  $(CC) $(CFLAG) -o ffmpeg_signal $(OBJECTS)

main.o:

.PHONY:clean
clean:
  rm ffmpeg_signal $(OBJECTS)





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值