1

我正在尝试编写一个简单的应用程序来使用 C 中的本机应用程序开发在我的银河手表上录制音频。

到目前为止,当我启动应用程序并有一个开始和停止录制的按钮时,我就到了这个阶段。

由于某种原因,我无法让记录器工作,它返回rerror -38,这是内部错误,在日志中我可以看到:

mm_camcorder_audiorec.c: _mmcamcorder_audio_command(530) > file delete(/opt/usr/home/owner/apps_rw/org.example.audiorecorder/data/AUDIO-2019-11-24_01:15:51.3gp)

但我不知道为什么它会在 recorder_commit 上被删除。

我错过了什么?你能帮我吗?

应用程序代码:

#include <app.h>
#include <stddef.h>
#include <privacy_privilege_manager.h>
// Functions for creating and managing user interface elements. Here are all the widgets,
// such as: windows, buttons, etc.
#include <Elementary.h>

//Functions for using callback for hardware Back button and for
//using extended widgets for a circle screen

#include <efl_extension.h>

#include "audiorecorder.h"

#include <recorder.h>
#include <camera.h>

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

typedef struct _UIData
{
    Evas_Object *win;
    Evas_Object *box;
    Evas_Object *icon;
    Evas_Object *button;
} UIData;

#define TEXT_BUF_SIZE 256
#define FILENAME_PREFIX "AUDIO"
#define NUMBER_OF_PRIVILEGES 3
#define START_RECORD_IMAGE_PATH "images/start-recording.jpg"
#define STOP_RECORD_IMAGE_PATH "images/stop-recording.png"

static recorder_h g_recorder;
bool permission_granted = false;
int user_clicked = 0;

static void
_state_changed_cb(recorder_state_e previous,
                  recorder_state_e current,
                  bool by_policy, void *user_data)
{
    dlog_print(DLOG_INFO, LOG_TAG,
               "_recorder_state_changed_cb (prev: %d, curr: %d)\n",
               previous,
               current);
}

static void
_recorder_recording_limit_reached_cb(
                                     recorder_recording_limit_type_e type,
                                     void *user_data)
{
    dlog_print(DLOG_DEBUG, LOG_TAG, "Recording limit reached: %d\n", type);
}

/* Check the audio recorder state */
static bool
_recorder_expect_state(recorder_h recorder,
                       recorder_state_e expected_state)
{
    recorder_state_e state;
    int error_code = recorder_get_state(recorder, &state);

    dlog_print(DLOG_INFO, LOG_TAG,
               "recorder state = %d, expected recorder state = %d, error_code = %d",
               state, expected_state, error_code);
    if (state == expected_state)
        return true;

    return false;
}

static void
_win_back_cb(void *data, Evas_Object *obj, void *event_info)
{
    UIData *ui = data;

    elm_win_lower(ui->win);
}

static void
app_get_resource(const char *edj_file_in, char *edj_path_out, int edj_path_max)
{
    char *res_path = app_get_resource_path();
    if (res_path)
    {
        snprintf(edj_path_out, edj_path_max, "%s%s", res_path, edj_file_in);
        free(res_path);
    }
}

static void
_icon_create(UIData *ui)
{
    dlog_print(DLOG_INFO, LOG_TAG, "icon create");
    char image_path[PATH_MAX] =
    { 0, };

    ui->icon = elm_image_add(ui->button);
    elm_image_resizable_set(ui->icon, EINA_TRUE, EINA_TRUE);
    app_get_resource(START_RECORD_IMAGE_PATH, image_path, (int) PATH_MAX);

    int error_code = elm_image_file_set(ui->icon, image_path, NULL);
    dlog_print(DLOG_INFO, LOG_TAG, "res path: %s error_code: %d", image_path, error_code);

    evas_object_show(ui->icon);
}

static void
_button_create(UIData *ui)
{
    dlog_print(DLOG_INFO, LOG_TAG, "button create");

    ui->button = elm_button_add(ui->box);

    _icon_create(ui);
    elm_object_style_set(ui->button, "circle");
    evas_object_size_hint_weight_set(ui->button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    evas_object_size_hint_align_set(ui->button, 0.5, 0.4);
    evas_object_size_hint_min_set(ui->button, ELM_SCALE_SIZE(90), ELM_SCALE_SIZE(90));
    elm_object_content_set(ui->button, ui->icon);

    evas_object_show(ui->button);
}

static void
_box_create(UIData *ui)
{
    dlog_print(DLOG_INFO, LOG_TAG, "box create");
    ui->box = elm_box_add(ui->win);

    evas_object_size_hint_weight_set(ui->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    _button_create(ui);
    elm_object_content_set(ui->box, ui->button);
    elm_box_pack_end(ui->box, ui->button);
    evas_object_show(ui->box);
}

static void
_window_create(UIData *ui)
{
    dlog_print(DLOG_INFO, LOG_TAG, "window create");
    // Create and configure the main window
    ui->win = elm_win_util_standard_add(NULL, NULL);

    // Create a conformant - in this case, the main container in the application,
    // which is also the interface between the application and
    // system elements, such as a keyboard.
    _box_create(ui);

    //  Set the size of the conformant widget the same as the size of the window
    elm_win_resize_object_add(ui->win, ui->box);

    //Register the function that will be called
    //when you press the hardware Back button
    eext_object_event_callback_add(ui->win, EEXT_CALLBACK_BACK, _win_back_cb, ui);

    // /Always display the window only after the entire base
    // user interface will be displayed.
    evas_object_show(ui->win);
}

static void permission_request_cb(ppm_call_cause_e cause,
                                  ppm_request_result_e result,
                                  const char *privilege,
                                  void *user_data)
{
    dlog_print(DLOG_INFO, LOG_TAG, "callback called for privilege: %s with cause: %d, the result was: %d", privilege,
               cause,
               result);
    user_clicked++;
}

void *func(void *arg)
{
    while (user_clicked < NUMBER_OF_PRIVILEGES)
    {
        dlog_print(DLOG_INFO, LOG_TAG, "waiting for user to accept all privileges, so far accepted: %d", user_clicked);
        sleep(1);
    }
    return 0;
}

static void _button_click_cb(void *data, Evas_Object *button, void *ev)
{
    UIData *ui = data;
    int error_code = 0;
    dlog_print(DLOG_INFO, LOG_TAG, "clicked button!");
    char image_path[PATH_MAX] = { 0, };
    const char *image;
    const char *group;
    char *path = NULL;
    recorder_state_e state;

    elm_image_file_get(ui->icon, &image, &group);
    dlog_print(DLOG_INFO, LOG_TAG, "file get, image: %s, group: %s", image, group);

    if (strstr(image, "start") != NULL) {
        app_get_resource(STOP_RECORD_IMAGE_PATH, image_path, (int) PATH_MAX);
        error_code = elm_image_file_set(ui->icon, image_path, NULL);
        dlog_print(DLOG_INFO, LOG_TAG, "setting icon to stop, error code: %d", error_code);

        dlog_print(DLOG_INFO, LOG_TAG, "starting recorder");
        if (_recorder_expect_state(g_recorder, RECORDER_STATE_READY)
                || _recorder_expect_state(g_recorder, RECORDER_STATE_PAUSED))
        {
            error_code = recorder_start(g_recorder);
            dlog_print(DLOG_INFO, LOG_TAG, "starting recorder result: %d", error_code);
        }
        else
        {
            recorder_get_state(g_recorder, &state);
            dlog_print(DLOG_INFO, LOG_TAG, "Failure, recorder in wrong state = %d", state);
        }
    }
    else if(strstr(image, "stop") != NULL)
    {
        app_get_resource(START_RECORD_IMAGE_PATH, image_path, (int) PATH_MAX);
        error_code = elm_image_file_set(ui->icon, image_path, NULL);
        dlog_print(DLOG_INFO, LOG_TAG, "setting icon to start, error code: %d", error_code);


        /* Stop the recorder and save the recorded data to a file */
        if (_recorder_expect_state(g_recorder, RECORDER_STATE_RECORDING)
                || _recorder_expect_state(g_recorder, RECORDER_STATE_PAUSED))
        {
            error_code = recorder_get_filename(g_recorder, &path);
            dlog_print(DLOG_INFO, LOG_TAG, "going to save audio to file %s error code: %d", path, error_code);
            error_code = recorder_commit(g_recorder);
            if (error_code != RECORDER_ERROR_NONE)
            {
                dlog_print(DLOG_INFO, LOG_TAG, "Failure, error code = %d", error_code);
                recorder_cancel(g_recorder);
            }

        }
        else
        {
            recorder_get_state(g_recorder, &state);
            dlog_print(DLOG_INFO, LOG_TAG, "Failure, recorder in wrong state = %d", state);
        }
    }
}

static bool
_app_create_cb(void *data)
{
    UIData *ui = data;

    _window_create(ui);
    evas_object_smart_callback_add(ui->button, "clicked", _button_click_cb, ui);
    return true;
}

static void
_app_control_cb(app_control_h app_control, void *data)
{
    /* Handle the launch request. */
}

static void
_app_pause_cb(void *data)
{
    /* Take necessary actions when the application becomes invisible. */
}

static void
_app_resume_cb(void *data)
{

    ppm_check_result_e privilege_results_array[NUMBER_OF_PRIVILEGES];
    const char *privilege_array[NUMBER_OF_PRIVILEGES];
    pthread_t pid;
    char *recorder_privilege = "http://tizen.org/privilege/recorder";
    char *media_storage_privilege = "http://tizen.org/privilege/mediastorage";
    char *ex_media_storage_privilege = "http://tizen.org/privilege/externalstorage";

    privilege_array[0] = malloc(strlen(recorder_privilege) + 1);
    privilege_array[1] = malloc(strlen(media_storage_privilege) + 1);
    privilege_array[2] = malloc(strlen(ex_media_storage_privilege) + 1);

    strcpy((char*) privilege_array[0], recorder_privilege);
    strcpy((char*) privilege_array[1], media_storage_privilege);
    strcpy((char*) privilege_array[2], ex_media_storage_privilege);

    int result = ppm_check_permissions(privilege_array, NUMBER_OF_PRIVILEGES, privilege_results_array);
    dlog_print(DLOG_INFO, LOG_TAG, "checking permission for recorder. Result: %d", result);
    for (int i = 0; i < NUMBER_OF_PRIVILEGES; i++)
    {
        switch (privilege_results_array[i])
        {
        case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW:
            dlog_print(DLOG_INFO, LOG_TAG, "Privilege: %s, Allowed!", privilege_array[i]);
            break;
        case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY:
            dlog_print(DLOG_INFO, LOG_TAG, "Privilege: %s, Denied!", privilege_array[i]);
            break;
        case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ASK:
            dlog_print(DLOG_INFO, LOG_TAG, "Privilege: %s, Gotta ask for this privilege!", privilege_array[i]);
            ppm_request_permission(privilege_array[i], permission_request_cb, NULL);
            break;
        }
    }
    pthread_create(&pid, NULL, func, NULL);

}

static void _app_terminate_cb(void *data)
{
    UIData *ui = data;

    //Before closing the application, delete the main widget (window),
    // then all  "children" widgets of this window (all widgets of this application) will be deleted.
    evas_object_del(ui->win);

    /* Release all resources. */
    int error_code = recorder_unset_recording_limit_reached_cb(g_recorder);
    error_code = recorder_unprepare(g_recorder);
    error_code = recorder_unset_state_changed_cb(g_recorder);
    error_code = recorder_destroy(g_recorder);
}

int main(int argc, char *argv[])
{

    char filename[256] =
    { '\0' };
    struct tm localtime =
    { 0 };
    time_t rawtime = time(NULL);
    int ret = 0;
    size_t size = 0;

    dlog_print(DLOG_INFO, LOG_TAG, "main");
    UIData ui =
    { 0, };
    ui_app_lifecycle_callback_s lifecycle_callbacks =
    { 0, };

    /* Create the audio recorder handle */
    int error_code = recorder_create_audiorecorder(&g_recorder);
    if (error_code == RECORDER_ERROR_NONE)
        dlog_print(DLOG_INFO, LOG_TAG, "successfully created audiorecorder error code = %d", error_code);
    else
        dlog_print(DLOG_ERROR, LOG_TAG, "Failure creating audiorecorder error code = %d", error_code);

    error_code = recorder_set_state_changed_cb(g_recorder, _state_changed_cb, NULL);
    dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_state_changed_cb, error code = %d", error_code);

    /* Set the audio encoder */
    error_code = recorder_set_audio_encoder(g_recorder, RECORDER_AUDIO_CODEC_AAC);
    dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_audio_encoder, error code = %d", error_code);

    /* Set video encoder */
    error_code = recorder_set_video_encoder(g_recorder, RECORDER_VIDEO_CODEC_MPEG4);
    dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_video_encoder, error code = %d", error_code);

    /* Set the maximum file size to unlimited 10240 (kB) - 10 mb */
    error_code = recorder_attr_set_size_limit(g_recorder, 10240);
    dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_size_limit, error code = %d", error_code);

    /* Set the audio encoder bitrate */
    error_code = recorder_attr_set_audio_encoder_bitrate(g_recorder, 28800);

    dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_audio_encoder_bitrate, error code = %d", error_code);

    /* Set the audio device to microphone */
    error_code = recorder_attr_set_audio_device(g_recorder, RECORDER_AUDIO_DEVICE_MIC);
    dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_audio_device, error code = %d", error_code);

    /* Set the audio sample rate */
    error_code = recorder_attr_set_audio_samplerate(g_recorder, 44100);
    dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_audio_samplerate, error code = %d", error_code);

    /* Set the file format according to the audio encoder */
    error_code = recorder_set_file_format(g_recorder, RECORDER_FILE_FORMAT_3GP);

    dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_file_format, error code = %d", error_code);

    /* Create the file name */
    dlog_print(DLOG_INFO, LOG_TAG, "data path = %s", app_get_data_path());

    if (localtime_r(&rawtime, &localtime) != NULL)
    {
        size = snprintf(filename, sizeof(filename),
                        "%s%s-%04i-%02i-%02i_%02i:%02i:%02i.3gp",
                        app_get_data_path(),
                        FILENAME_PREFIX,
                        localtime.tm_year + 1900, localtime.tm_mon + 1,
                        localtime.tm_mday,
                        localtime.tm_hour, localtime.tm_min,
                        localtime.tm_sec);
    }
    else
    {
        dlog_print(DLOG_INFO, LOG_TAG, "failed to save audio in a file");
    }

    /* Set the full path and file name */
    /* Set the file name according to the file format */
    error_code = recorder_set_filename(g_recorder, filename);
    dlog_print(DLOG_INFO, LOG_TAG, "setting file name to: %s, error code = %d",
               filename,
               error_code);

    error_code = recorder_set_recording_limit_reached_cb(g_recorder,
                                                         _recorder_recording_limit_reached_cb,
                                                         NULL);
    dlog_print(DLOG_INFO, LOG_TAG, "setting limit reached callback, error code = %d", error_code);

    dlog_print(DLOG_INFO, LOG_TAG, "preparing recorder");

    error_code = recorder_prepare(g_recorder);
    dlog_print(DLOG_INFO, LOG_TAG, "preparing recorder result: %d", error_code);

    lifecycle_callbacks.create = _app_create_cb;
    lifecycle_callbacks.terminate = _app_terminate_cb;
    lifecycle_callbacks.pause = _app_pause_cb;
    lifecycle_callbacks.resume = _app_resume_cb;
    lifecycle_callbacks.app_control = _app_control_cb;

    ret = ui_app_main(argc, argv, &lifecycle_callbacks, &ui);
    if (ret != APP_ERROR_NONE)
    {
        dlog_print(DLOG_ERROR, LOG_TAG, "watch_app_main() is failed. err = %d",
                   ret);
    }

    return ret;
}
4

3 回答 3

0

如果没有调用录音状态回调,则表示不会创建录音文件,并且audiosrc或录音机框架的编码部分存在问题。你能把你的测试 tpk 文件发给我吗?(jm80.yang@outlook.com) 我在 Galaxy Watch 上用相同的设置(采样率:16000,比特率:28800,音频编解码器:AAC,文件格式:3GP)测试了我的录音测试套件,但它工作正常。所以我需要检查你所做的测试 tpk 的问题。

于 2019-12-02T07:49:20.640 回答
0

谢谢你的日志。我检查了它,发现 recorder_commit() 失败,因为记录的文件大小为零。(我可以假设它与 recorder_commit() 函数调用的时间间隔。)

根据日志,录音路径为“/opt/usr/home/owner/apps_rw/org.example.audiorecorder/data/”,由app_get_data_path()获取。但一般来说,媒体文件不会放在那里。所以,我建议更改录音文件路径。

请尝试使用 storage_foreach_device_supported() 可以获取的另一个路径。( https://developer.tizen.org/development/api-references/native-application?redirect=https://developer.tizen.org/dev-guide/4.0.0/org.tizen.native.wearable.apireference /group__CAPI__SYSTEM__STORAGE__MODULE.html#ga8f5d871bbc2d8942fe6b725c8f8fc779 )

您可以使用 recorder_set_recording_status_cb() 检查录制状态。如果它没有被重复调用,我们可以假设发生了一些问题。否则,我们可以假设它运行良好。

于 2019-11-29T06:18:46.640 回答
0

在您的代码中,如果 recorder_commit 返回错误,则会调用 recorder_cancel。

error_code = recorder_commit(g_recorder);
if (error_code != RECORDER_ERROR_NONE)
{
    dlog_print(DLOG_INFO, LOG_TAG, "Failure, error code = %d", error_code);
    recorder_cancel(g_recorder);
}

因此,我们需要检查 recorder_commit 失败的原因。您能否显示所有日志,因为 recorder_prepare 是特别使用 MM_CAMCORDER 和 GST_LOG 日志标签调用的?

注意:您可以使用 recorder_set_error_cb() 获取运行时错误。它可以帮助您调试错误。

于 2019-11-26T05:22:33.413 回答