File: C:/Users/fred/.codex/.tmp/plugins/plugins/zoom/skills/video-sdk/windows/examples/transcription.md
# Live Transcription
Complete working code for real-time speech-to-text transcription.
**Official Sample**: `VSDK_TranscriptionAndTranslation` in [videosdk-windows-rawdata-sample](https://github.com/zoom/videosdk-windows-rawdata-sample)
---
## Overview
Live transcription provides real-time captions of speech. Features:
- Automatic speech recognition
- Multiple language support
- Speaker identification
- Translation (optional)
```
┌─────────────────────────────────────────────────────────────────┐
│ TRANSCRIPTION FLOW │
├─────────────────────────────────────────────────────────────────┤
│ 1. Enable transcription → startLiveTranscription() │
│ 2. Receive captions → onLiveTranscriptionMsgInfoReceived() │
│ 3. Display/process text │
└─────────────────────────────────────────────────────────────────┘
```
---
## Prerequisites
- Live transcription must be enabled for the session
- Valid Zoom account with transcription feature
---
## Complete Working Code
### TranscriptionManager.h
```cpp
#pragma once
#include <windows.h>
#include <string>
#include <vector>
#include <functional>
#include "zoom_video_sdk_interface.h"
USING_ZOOM_VIDEO_SDK_NAMESPACE
struct TranscriptionMessage {
std::wstring speakerName;
std::wstring text;
bool isFinal;
unsigned long long timestamp;
};
class TranscriptionManager {
public:
TranscriptionManager(IZoomVideoSDK* sdk);
// Control
bool StartTranscription();
bool StopTranscription();
bool IsTranscribing() const { return m_isTranscribing; }
// Language settings
bool SetSpokenLanguage(ILiveTranscriptionLanguage* language);
bool SetTranslationLanguage(ILiveTranscriptionLanguage* language);
std::vector<ILiveTranscriptionLanguage*> GetAvailableLanguages();
// Callbacks from delegate
void OnStatusChanged(ZoomVideoSDKLiveTranscriptionStatus status);
void OnMessageReceived(ILiveTranscriptionMessageInfo* info);
void OnOriginalLanguageReceived(ILiveTranscriptionMessageInfo* info);
void OnError(ILiveTranscriptionLanguage* spoken,
ILiveTranscriptionLanguage* transcript);
// Set message handler
using MessageCallback = std::function<void(const TranscriptionMessage&)>;
void SetMessageHandler(MessageCallback callback) { m_callback = callback; }
private:
IZoomVideoSDK* m_sdk;
IZoomVideoSDKLiveTranscriptionHelper* m_helper;
bool m_isTranscribing;
MessageCallback m_callback;
};
```
### TranscriptionManager.cpp
```cpp
#include "TranscriptionManager.h"
#include <iostream>
TranscriptionManager::TranscriptionManager(IZoomVideoSDK* sdk)
: m_sdk(sdk)
, m_helper(nullptr)
, m_isTranscribing(false) {
}
bool TranscriptionManager::StartTranscription() {
m_helper = m_sdk->getLiveTranscriptionHelper();
if (!m_helper) {
std::cout << "Transcription helper not available" << std::endl;
return false;
}
// Check if transcription is available
if (!m_helper->canStartLiveTranscription()) {
std::cout << "Cannot start transcription" << std::endl;
return false;
}
ZoomVideoSDKErrors err = m_helper->startLiveTranscription();
if (err == ZoomVideoSDKErrors_Success) {
std::cout << "Transcription started" << std::endl;
return true;
}
std::cout << "Start transcription failed: " << err << std::endl;
return false;
}
bool TranscriptionManager::StopTranscription() {
if (!m_helper) return false;
ZoomVideoSDKErrors err = m_helper->stopLiveTranscription();
if (err == ZoomVideoSDKErrors_Success) {
std::cout << "Transcription stopped" << std::endl;
m_isTranscribing = false;
return true;
}
return false;
}
std::vector<ILiveTranscriptionLanguage*> TranscriptionManager::GetAvailableLanguages() {
std::vector<ILiveTranscriptionLanguage*> languages;
if (!m_helper) {
m_helper = m_sdk->getLiveTranscriptionHelper();
}
if (m_helper) {
IVideoSDKVector<ILiveTranscriptionLanguage*>* langList =
m_helper->getAvailableSpokenLanguages();
if (langList) {
for (int i = 0; i < langList->GetCount(); i++) {
languages.push_back(langList->GetItem(i));
}
}
}
return languages;
}
bool TranscriptionManager::SetSpokenLanguage(ILiveTranscriptionLanguage* language) {
if (!m_helper || !language) return false;
ZoomVideoSDKErrors err = m_helper->setSpokenLanguage(language);
if (err == ZoomVideoSDKErrors_Success) {
std::wcout << L"Spoken language set to: "
<< language->getLTTLanguageName() << std::endl;
return true;
}
return false;
}
bool TranscriptionManager::SetTranslationLanguage(ILiveTranscriptionLanguage* language) {
if (!m_helper || !language) return false;
ZoomVideoSDKErrors err = m_helper->setTranslationLanguage(language);
if (err == ZoomVideoSDKErrors_Success) {
std::wcout << L"Translation language set to: "
<< language->getLTTLanguageName() << std::endl;
return true;
}
return false;
}
void TranscriptionManager::OnStatusChanged(ZoomVideoSDKLiveTranscriptionStatus status) {
switch (status) {
case ZoomVideoSDKLiveTranscription_Status_Start:
std::cout << "Transcription started" << std::endl;
m_isTranscribing = true;
break;
case ZoomVideoSDKLiveTranscription_Status_Stop:
std::cout << "Transcription stopped" << std::endl;
m_isTranscribing = false;
break;
case ZoomVideoSDKLiveTranscription_Status_Connecting:
std::cout << "Transcription connecting..." << std::endl;
break;
default:
std::cout << "Transcription status: " << status << std::endl;
}
}
void TranscriptionManager::OnMessageReceived(ILiveTranscriptionMessageInfo* info) {
if (!info) return;
TranscriptionMessage msg;
// Get speaker
IZoomVideoSDKUser* speaker = info->getSpeaker();
if (speaker) {
msg.speakerName = speaker->getUserName();
}
// Get text
const zchar_t* text = info->getMessageContent();
if (text) {
msg.text = text;
}
// Get metadata
msg.isFinal = (info->getMessageType() ==
ZoomVideoSDKLiveTranscriptionOperationType_Complete);
msg.timestamp = info->getTimeStamp();
// Display
std::wcout << L"[" << msg.speakerName << L"] " << msg.text;
if (msg.isFinal) {
std::wcout << L" (final)";
}
std::wcout << std::endl;
// Call user handler
if (m_callback) {
m_callback(msg);
}
}
void TranscriptionManager::OnOriginalLanguageReceived(ILiveTranscriptionMessageInfo* info) {
// Original language message (before translation)
if (!info) return;
const zchar_t* text = info->getMessageContent();
if (text) {
std::wcout << L"[Original] " << text << std::endl;
}
}
void TranscriptionManager::OnError(ILiveTranscriptionLanguage* spoken,
ILiveTranscriptionLanguage* transcript) {
std::cout << "Transcription error" << std::endl;
if (spoken) {
std::wcout << L"Spoken language: " << spoken->getLTTLanguageName() << std::endl;
}
if (transcript) {
std::wcout << L"Transcript language: " << transcript->getLTTLanguageName() << std::endl;
}
}
```
### Using in Delegate
```cpp
class MyDelegate : public IZoomVideoSDKDelegate {
private:
TranscriptionManager* m_transcription;
public:
MyDelegate(IZoomVideoSDK* sdk) {
m_transcription = new TranscriptionManager(sdk);
// Set message handler
m_transcription->SetMessageHandler([](const TranscriptionMessage& msg) {
// Process transcription (e.g., save to file, analyze)
if (msg.isFinal) {
SaveTranscript(msg.speakerName, msg.text);
}
});
}
void onSessionJoin() override {
// Start transcription
m_transcription->StartTranscription();
}
void onLiveTranscriptionStatus(ZoomVideoSDKLiveTranscriptionStatus status) override {
m_transcription->OnStatusChanged(status);
}
void onLiveTranscriptionMsgInfoReceived(ILiveTranscriptionMessageInfo* info) override {
m_transcription->OnMessageReceived(info);
}
void onOriginalLanguageMsgReceived(ILiveTranscriptionMessageInfo* info) override {
m_transcription->OnOriginalLanguageReceived(info);
}
void onLiveTranscriptionMsgError(ILiveTranscriptionLanguage* spoken,
ILiveTranscriptionLanguage* transcript) override {
m_transcription->OnError(spoken, transcript);
}
// ... other callbacks
};
```
---
## Message Types
| Type | Description |
|------|-------------|
| `ZoomVideoSDKLiveTranscriptionOperationType_N` | Interim result (may change) |
| `ZoomVideoSDKLiveTranscriptionOperationType_Complete` | Final result |
| `ZoomVideoSDKLiveTranscriptionOperationType_Update` | Updated previous result |
**Note**: Interim results allow real-time display but may be revised.
---
## ILiveTranscriptionMessageInfo Methods
| Method | Returns | Description |
|--------|---------|-------------|
| `getMessageContent()` | `const zchar_t*` | Transcribed text |
| `getSpeaker()` | `IZoomVideoSDKUser*` | Speaker user object |
| `getTimeStamp()` | `unsigned long long` | Message timestamp |
| `getMessageType()` | `ZoomVideoSDKLiveTranscriptionOperationType` | Interim/final |
| `getMessageID()` | `const zchar_t*` | Unique message ID |
---
## Language Support
### List Available Languages
```cpp
auto languages = transcriptionManager->GetAvailableLanguages();
for (auto lang : languages) {
std::wcout << lang->getLTTLanguageID() << L": "
<< lang->getLTTLanguageName() << std::endl;
}
```
### Common Language Codes
| Code | Language |
|------|----------|
| `en` | English |
| `es` | Spanish |
| `fr` | French |
| `de` | German |
| `zh` | Chinese |
| `ja` | Japanese |
---
## Common Issues
### Transcription Not Available
**Cause**: Feature not enabled for session
**Fix**: Check `canStartLiveTranscription()` first:
```cpp
if (helper->canStartLiveTranscription()) {
helper->startLiveTranscription();
}
```
### No Messages Received
**Cause**: No one is speaking or audio not connected
**Fix**: Ensure audio is connected:
```cpp
void onSessionJoin() override {
sdk->getAudioHelper()->startAudio(); // Connect audio first
transcription->StartTranscription();
}
```
### Wrong Language
**Cause**: Spoken language not set correctly
**Fix**: Set spoken language:
```cpp
auto languages = manager->GetAvailableLanguages();
for (auto lang : languages) {
if (wcscmp(lang->getLTTLanguageID(), L"en") == 0) {
manager->SetSpokenLanguage(lang);
break;
}
}
```
---
## Related Documentation
- [Raw Audio Capture](raw-audio-capture.md) - Alternative audio processing
- [Session Join Pattern](session-join-pattern.md) - Session setup
- [Delegate Methods](../references/delegate-methods.md) - Transcription callbacks
- [API Reference](../references/windows-reference.md) - Method signatures