美文网首页
录制音频数据

录制音频数据

作者: 想成为大牛的程旭元 | 来源:发表于2020-07-27 15:58 被阅读0次

创建文件


 //1-1.创建文件

 /*

 fopen 创建文件函数

 const char *restrict __filename:路径

 const char *restrict __mode:模式  w写数据 r是读数据  b是byte二进制数据,+号是这个文件不存在,你就去创建它

 FILE 返回值

 */

 char * out = "/Users/xxxx/Desktop/audio/av_base/audio.pcm";

 FILE * outFile = fopen(out, "wb+");

将音频是写入到文件中


 while ((av_read_frame(fmt_ctx, &pkt) == 0)&& (count++ <= 500)) {

 //1-2.写入文件

 /*

 const void *restrict __ptr: 要写入的数据

 size_t __size:大小

 size_t __nitems:数据的个数

 FILE *restrict __stream:文件存入地址

 */

 fwrite(pkt.data, pkt.size, 1, outFile);

fflush(outFile);

 //1-3 关闭文件

 fclose(outFile);

 }

关闭文件


//1-3 关闭文件

fclose(outFile);

强调

我们通过fwrite将数据写到文件中,这个时候并不是一下子就写到文件中了,而是先写到系统的缓冲区,这是操作系统出于效率的考虑,并没有直接将数据写到磁盘IO中,而是先写到系统的缓冲区,当系统缓冲区的内存到了一定的时候,这时候再从缓冲区拷贝到文件IO中,通过文件IO,写成文件,这时候会产生一个丢数据的问题,就是当数据不能实时的写到文件中去,一旦断电或者系统出现问题的时候,就会造成丢数据的问题,所以在写文件的时候,我们会掉fflush函数,

fflush函数


//fflush()会强迫将缓冲区内的数bai据写回参数stream 指定的文件中. 如果参数stream 为NULL,fflush()会将所有打开的文件数据更新. 

fflush(FILE*)

代码
void record_audio()
{
    av_log_set_level(AV_LOG_DEBUG);
    //1. 注册所有设备
    avdevice_register_all();
    //2. 获取格式  设置采集方式
    AVInputFormat * iformat = av_find_input_format("avfoundation");
    
    AVFormatContext *fmt_ctx = NULL;
    AVDictionary *options = NULL;
    //每次重新分配地址
    AVPacket pkt;
    //默认为读取500次后退出
    int count = 0;
    
    //音频设备  mac上 :0 从第一个音频设备获取声音   可以到ffmpeg官网查看devicename说明
    char * devicename = ":0";
     
    
    //3.打开设备
    /*
     AVFormatContext **ps  上下文,作用:前面调用的API和后面调用的API都依赖于上下文,对一个多媒体文件进行解码、编码都依赖于上下文,操作那个文件,只需要把这个文件的上下文传过去,就知道是对那个文件操作了 。这个上下文是avformat_open_input这个API返回的指针,我们只要定义这个指针就行 AVFormatContext *fmt_ctx;
     const char *url: 可以是一个网络地址,也可以是本地的文件,
     AVInputFormat *fmt
     AVDictionary **options   打开设备的一些参数,一般解码的时候用到
     */

    // 成功==0,不成功!= 0
    int ret = avformat_open_input(&fmt_ctx, devicename, iformat, &options);
    if (ret < 0) {
        /*
        int    enum         错误码
        char   errbuf       错误信息输出到哪
        size_t errbuf_size
         */
        char errors[1024];
        
        av_strerror(ret, errors, 1024);
        printf(stderr,"Failed to open audio device,[%d]%s\n",ret,errors);
        return;
    }
    
    //1-1.创建文件
    /*
     fopen 创建文件函数
     const char *restrict __filename:路径
     const char *restrict __mode:模式     w写数据 r是读数据     b是byte二进制数据,+号是这个文件不存在,你就去创建它
     FILE 返回值
     */
    char * out = "/Users/xxxxx/Desktop/audio/av_base/audio.pcm";
    FILE * outFile = fopen(out, "wb+");
    
    //初始化
    av_init_packet(&pkt);
    while ((av_read_frame(fmt_ctx, &pkt) == 0)&& (count++ <= 500)) {
        //1-2.写入文件
        /*
         const void *restrict __ptr: 要写入的数据
         size_t __size:大小
         size_t __nitems:数据的个数
         FILE *restrict __stream:文件存入地址
         */
        fwrite(pkt.data, pkt.size, 1, outFile);
        //
        fflush(outFile);
        //1-3 关闭文件
        fclose(outFile);
        
        //释放掉pkt
        av_packet_unref(&pkt);
    }
    //释放上下文
    avformat_close_input(&fmt_ctx);
    av_log(NULL, AV_LOG_DEBUG, "Finished\n");
    return;
};

代码修改

代码逻辑

在ViewController里面定义了

    //是开始录制 还是暂停录制
    var recStatus:Bool = false;
    var btn:NSButton = NSButton(title: "", target: self, action: #selector(myfunc));
    var thread = Thread();

在testc.c里面定义

//状态值  标志是1是开始录制,0是结束录制
static int rec_status = 0;

//在Swift代码里赋值
void set_status(int status){
    rec_status = status;
}

void record_audio()
{
    //开始录制了
    rec_status = 1;
    
    av_log_set_level(AV_LOG_DEBUG);
    //1. 注册所有设备
    avdevice_register_all();
    //2. 获取格式  设置采集方式
    AVInputFormat * iformat = av_find_input_format("avfoundation");
    
    AVFormatContext *fmt_ctx = NULL;
    AVDictionary *options = NULL;
    //每次重新分配地址
    AVPacket pkt;
    //音频设备  mac上 :0 从第一个音频设备获取声音   可以到ffmpeg官网查看devicename说明
    char * devicename = ":0";
     
    
    //3.打开设备
    /*
     AVFormatContext **ps  上下文,作用:前面调用的API和后面调用的API都依赖于上下文,对一个多媒体文件进行解码、编码都依赖于上下文,操作那个文件,只需要把这个文件的上下文传过去,就知道是对那个文件操作了 。这个上下文是avformat_open_input这个API返回的指针,我们只要定义这个指针就行 AVFormatContext *fmt_ctx;
     const char *url: 可以是一个网络地址,也可以是本地的文件,
     AVInputFormat *fmt
     AVDictionary **options   打开设备的一些参数,一般解码的时候用到
     */

    // 成功==0,不成功!= 0
    int ret = avformat_open_input(&fmt_ctx, devicename, iformat, &options);
    if (ret < 0) {
        /*
        int    enum         错误码
        char   errbuf       错误信息输出到哪
        size_t errbuf_size
         */
        char errors[1024];
        
        av_strerror(ret, errors, 1024);
        printf(stderr,"Failed to open audio device,[%d]%s\n",ret,errors);
        return;
    }
    
    //1-1.创建文件
    /*
     fopen 创建文件函数
     const char *restrict __filename:路径
     const char *restrict __mode:模式     w写数据 r是读数据     b是byte二进制数据,+号是这个文件不存在,你就去创建它
     FILE 返回值
     */
    char * out = "/Users/yangyazhuo/Desktop/audio/av_base/audio.pcm";
    FILE * outFile = fopen(out, "wb+");
    
    //初始化
    av_init_packet(&pkt);
    while ((av_read_frame(fmt_ctx, &pkt) == 0)&& rec_status) {
        //1-2.写入文件
        /*
         const void *restrict __ptr: 要写入的数据
         size_t __size:大小
         size_t __nitems:数据的个数
         FILE *restrict __stream:文件存入地址
         */
        fwrite(pkt.data, pkt.size, 1, outFile);
        //
        fflush(outFile);
        //1-3 关闭文件
        fclose(outFile);
        
        //释放掉pkt
        av_packet_unref(&pkt);
    }
    //释放上下文
    avformat_close_input(&fmt_ctx);
    av_log(NULL, AV_LOG_DEBUG, "Finished\n");
    return;
};

while ((av_read_frame(fmt_ctx, &pkt) == 0)&& rec_status)  这,当rec_status为0,结束录制

在ViewController里面的代码

//
//  ViewController.swift
//  myapp
//
//  Created by z on 2020/7/23.
//  Copyright © 2020 1. All rights reserved.
//

import Cocoa

class ViewController: NSViewController {
    //是开始录制 还是暂停录制
    var recStatus:Bool = false;
    //录制按钮
    var btn:NSButton = NSButton(title: "", target: self, action: #selector(myfunc));
    //线程 开始录制的时候,在一个线程里面 结束录制的时候 结束录制
    var thread = Thread();
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        btn.title = "开始录制";
        btn.bezelStyle = .rounded;
        btn.setButtonType(.pushOnPushOff)
        btn.frame = CGRect(x: 320/2-60, y: 240/2-15, width: 130, height: 30);
        self.view.addSubview(btn)
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }
    @objc func myfunc(){
        recStatus = !recStatus;
        self.btn.title = recStatus ? "停止录制":"开始录制";
        if recStatus {
            thread = Thread(target: self, selector: #selector(recAudio), object: nil)
            thread.start()
        }else{
            set_status(0);
        }
    }
    @objc func recAudio(){
        print("start thread");
        record_audio()
    }

}


testc.h里面的代码

//
//  testc.h
//  myapp
//
//  Created by z on 2020/7/24.
//  Copyright © 2020 1. All rights reserved.
//

#ifndef testc_h
#define testc_h

#include <stdio.h>
#include "libavutil/avutil.h"
//注册设备的
#include "libavdevice/avdevice.h"
//打开设备
#include "libavformat/avformat.h"

#include "libavcodec/avcodec.h"

void record_audio(void);
void set_status(int status);

#endif /* testc_h */

testc.c文件里面的代码

//
//  testc.c
//  myapp
//
//  Created by z on 2020/7/24.
//  Copyright © 2020 1. All rights reserved.
//

#include "testc.h"

//状态值  标志是1是开始录制,0是结束录制
static int rec_status = 0;

void set_status(int status){
    rec_status = status;
}

void record_audio()
{
    //开始录制了
    rec_status = 1;
    
    av_log_set_level(AV_LOG_DEBUG);
    //1. 注册所有设备
    avdevice_register_all();
    //2. 获取格式  设置采集方式
    AVInputFormat * iformat = av_find_input_format("avfoundation");
    
    AVFormatContext *fmt_ctx = NULL;
    AVDictionary *options = NULL;
    //每次重新分配地址
    AVPacket pkt;
    //音频设备  mac上 :0 从第一个音频设备获取声音   可以到ffmpeg官网查看devicename说明
    char * devicename = ":0";
     
    
    //3.打开设备
    /*
     AVFormatContext **ps  上下文,作用:前面调用的API和后面调用的API都依赖于上下文,对一个多媒体文件进行解码、编码都依赖于上下文,操作那个文件,只需要把这个文件的上下文传过去,就知道是对那个文件操作了 。这个上下文是avformat_open_input这个API返回的指针,我们只要定义这个指针就行 AVFormatContext *fmt_ctx;
     const char *url: 可以是一个网络地址,也可以是本地的文件,
     AVInputFormat *fmt
     AVDictionary **options   打开设备的一些参数,一般解码的时候用到
     */

    // 成功==0,不成功!= 0
    int ret = avformat_open_input(&fmt_ctx, devicename, iformat, &options);
    if (ret < 0) {
        /*
        int    enum         错误码
        char   errbuf       错误信息输出到哪
        size_t errbuf_size
         */
        char errors[1024];
        
        av_strerror(ret, errors, 1024);
        printf(stderr,"Failed to open audio device,[%d]%s\n",ret,errors);
        return;
    }
    
    //1-1.创建文件
    /*
     fopen 创建文件函数
     const char *restrict __filename:路径
     const char *restrict __mode:模式     w写数据 r是读数据     b是byte二进制数据,+号是这个文件不存在,你就去创建它
     FILE 返回值
     */
    char * out = "/Users/yangyazhuo/Desktop/audio/av_base/audio.pcm";
    FILE * outFile = fopen(out, "wb+");
    
    //初始化
    av_init_packet(&pkt);
    while ((av_read_frame(fmt_ctx, &pkt) == 0)&& rec_status) {
        //1-2.写入文件
        /*
         const void *restrict __ptr: 要写入的数据
         size_t __size:大小
         size_t __nitems:数据的个数
         FILE *restrict __stream:文件存入地址
         */
        fwrite(pkt.data, pkt.size, 1, outFile);
        //
        fflush(outFile);
        //1-3 关闭文件
        fclose(outFile);
        
        //释放掉pkt
        av_packet_unref(&pkt);
    }
    //释放上下文
    avformat_close_input(&fmt_ctx);
    av_log(NULL, AV_LOG_DEBUG, "Finished\n");
    return;
};



相关文章

网友评论

      本文标题:录制音频数据

      本文链接:https://www.haomeiwen.com/subject/bhpzlktx.html